[{"data":1,"prerenderedAt":82175},["Reactive",2],{"navigation":3,"blog":204,"posts":234},[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":6,"_dir":205,"_draft":206,"_partial":206,"_locale":205,"title":5,"description":207,"navigation":208,"align":209,"links":210,"image":226,"_id":229,"_type":230,"_source":231,"_file":232,"_extension":233},"",false,"Here is a list of articles I wrote to share my learnings about Development, Tooling and DevOps.",{"icon":191},"left",[211,218,221],{"label":212,"color":213,"to":214,"size":215,"target":216,"icon":217},"RSS Feed","primary","/feed.rss","xs","_blank","i-heroicons-rss-20-solid",{"label":219,"color":213,"to":220,"target":216,"icon":217,"size":215},"Atom Feed","/feed.atom",{"label":222,"color":223,"to":224,"size":215,"icon":225},"See all tags","gray","/tags","i-heroicons-hashtag-16-solid",{"src":227,"alt":228},"/images/writing_1.webp","Fountain pen.","content:1.posts.yml","yaml","content","1.posts.yml","yml",[235,1279,6252,9254,10052,10619,16342,19561,23744,31097,31313,31644,31820,32063,32238,32428,32873,37208,37455,38103,39609,40001,40195,41192,42352,42572,43846,46708,47012,47440,47681,47965,50511,50826,51097,52276,53512,53881,55861,57794,58416,59713,62071,62424,62648,63697,64986,65165,65571,65781,66257,66832,67219,67886,68608,72567,73142,76967,77234,79703,81305],{"_path":190,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":189,"description":237,"lead":238,"date":239,"image":240,"badge":242,"tags":244,"ImageAttribution":249,"body":250,"_type":1275,"_id":1276,"_source":231,"_file":1277,"_extension":1278},"posts","I have written several blog posts about HTTP clients in the past. I am a big fan of using HTTP text files versioned in a git repository alongside API code and executed by an IDE tooling. However, there was one use case where a GUI tool like Postman or a swagger page was more convenient: retrieving OAuth 2.0 users' tokens. Thanks to the latest OAuth 2.0 feature in JetBrains' IDE built-in HTTP client, this is no longer an issue.","Automatically acquire OAuth 2.0 tokens","2024-03-11T00:00:00.000Z",{"src":241},"/images/access-code-door.webp",{"label":243},"Tooling",[245,246,247,248],"tooling","HTTP","OAuth2","Azure AD B2C","Picture of \u003Ca href=\"https://unsplash.com/fr/@drice22\">Danielle Rice\u003C/a> on \u003Ca href=\"https://unsplash.com/fr/photos/personne-detenant-une-carte-en-noir-et-blanc-7GfRwb78YWs\">Unsplash\u003C/a>",{"type":251,"children":252,"toc":1265},"root",[253,281,288,310,315,320,326,331,360,365,378,397,402,415,421,426,454,461,475,489,494,499,524,529,565,582,595,601,606,611,1112,1120,1128,1141,1147,1152,1157,1169,1182,1223,1228,1234,1239,1259],{"type":254,"tag":255,"props":256,"children":257},"element","p",{},[258,261,270,272,279],{"type":259,"value":260},"text","I have written several ",{"type":254,"tag":262,"props":263,"children":267},"a",{"href":264,"rel":265},"https://www.techwatching.dev/posts/http-clients",[266],"nofollow",[268],{"type":259,"value":269},"blog posts",{"type":259,"value":271}," about HTTP clients in the past. I am a big fan of using HTTP text files versioned in a git repository alongside API code and executed by an IDE tooling. However, there was one use case where a GUI tool like Postman or a swagger page was more convenient: retrieving OAuth 2.0 users' tokens. Thanks to the latest ",{"type":254,"tag":262,"props":273,"children":276},{"href":274,"rel":275},"https://www.jetbrains.com/help/idea/oauth-2-0-authorization.html",[266],[277],{"type":259,"value":278},"OAuth 2.0 feature",{"type":259,"value":280}," in JetBrains' IDE built-in HTTP client, this is no longer an issue.",{"type":254,"tag":282,"props":283,"children":285},"h2",{"id":284},"context",[286],{"type":259,"value":287},"Context",{"type":254,"tag":255,"props":289,"children":290},{},[291,293,299,301,308],{"type":259,"value":292},"I am developing a web application composed of a Vue.js frontend and an ASP.NET Core backend (just describing my use case, technologies don't matter). The end users of this application are authenticated using ",{"type":254,"tag":262,"props":294,"children":297},{"href":295,"rel":296},"https://learn.microsoft.com/en-us/azure/active-directory-b2c/overview",[266],[298],{"type":259,"value":248},{"type":259,"value":300},", which is a ",{"type":254,"tag":262,"props":302,"children":305},{"href":303,"rel":304},"https://en.wikipedia.org/wiki/Customer_identity_access_management",[266],[306],{"type":259,"value":307},"customer identity access management",{"type":259,"value":309}," solution like Auth0 or other competitors.",{"type":254,"tag":255,"props":311,"children":312},{},[313],{"type":259,"value":314},"I often need to manually call the endpoints of the API to verify the code is working properly and that an endpoint is returning the expected result. HTTP files are a convenient way of writing and executing the HTTP requests. Once committed in the Git repository, they can easily be shared with other developers of the team who may not have worked on some endpoints and want to have proper examples with the query parameters and payloads.",{"type":254,"tag":255,"props":316,"children":317},{},[318],{"type":259,"value":319},"As the API is protected by Azure AD B2C, I need to retrieve a valid access token and pass it to my requests.",{"type":254,"tag":282,"props":321,"children":323},{"id":322},"previous-solutions",[324],{"type":259,"value":325},"Previous solutions",{"type":254,"tag":255,"props":327,"children":328},{},[329],{"type":259,"value":330},"Passing a valid access token to my HTTP requests is something I was previously doing by:",{"type":254,"tag":332,"props":333,"children":334},"ul",{},[335,341,346],{"type":254,"tag":336,"props":337,"children":338},"li",{},[339],{"type":259,"value":340},"signing in my frontend",{"type":254,"tag":336,"props":342,"children":343},{},[344],{"type":259,"value":345},"grabbing the token in the web browser dev tools",{"type":254,"tag":336,"props":347,"children":348},{},[349,351,358],{"type":259,"value":350},"copying the token to my ",{"type":254,"tag":262,"props":352,"children":355},{"href":353,"rel":354},"https://www.jetbrains.com/help/idea/exploring-http-syntax.html#environment-variables",[266],[356],{"type":259,"value":357},"HTTP environment variables",{"type":259,"value":359}," (preferably the private environment file to avoid committing a secret in your repository)",{"type":254,"tag":255,"props":361,"children":362},{},[363],{"type":259,"value":364},"That works but:",{"type":254,"tag":332,"props":366,"children":367},{},[368,373],{"type":254,"tag":336,"props":369,"children":370},{},[371],{"type":259,"value":372},"it's cumbersome",{"type":254,"tag":336,"props":374,"children":375},{},[376],{"type":259,"value":377},"you have to do it each time your access token expires",{"type":254,"tag":255,"props":379,"children":380},{},[381,383,395],{"type":259,"value":382},"Another solution is to use a tool that generates app-specific local JWTs and configure your local dev environment to authenticate with these tokens instead of using the Azure AD B2C configuration. In .NET, you can use the ",{"type":254,"tag":262,"props":384,"children":387},{"href":385,"rel":386},"https://learn.microsoft.com/en-us/aspnet/core/security/authentication/jwt-authn",[266],[388],{"type":254,"tag":389,"props":390,"children":392},"code",{"className":391},[],[393],{"type":259,"value":394},"dotnet user-jwts",{"type":259,"value":396}," to do exactly that. It allows you to generate a JWT token with the scopes, roles, and claims you want. So it's a good solution to debug your API locally without having to bypass the authentication and authorization mechanisms.",{"type":254,"tag":255,"props":398,"children":399},{},[400],{"type":259,"value":401},"However, it has some downsides:",{"type":254,"tag":332,"props":403,"children":404},{},[405,410],{"type":254,"tag":336,"props":406,"children":407},{},[408],{"type":259,"value":409},"the tokens are only valid in your local machine so it only works for your local environment",{"type":254,"tag":336,"props":411,"children":412},{},[413],{"type":259,"value":414},"the Azure AD B2C authentication is replaced by this \"local JWT authentication\" so you are not testing your API in real conditions",{"type":254,"tag":282,"props":416,"children":418},{"id":417},"with-the-new-http-client-oauth-20-feature",[419],{"type":259,"value":420},"With the new HTTP Client OAuth 2.0 feature",{"type":254,"tag":255,"props":422,"children":423},{},[424],{"type":259,"value":425},"Starting version 2024.1, HTTP Client in the JetBrains IDEs (in my case Rider 2024.1) support automatically authenticating HTTP requests, provided that you properly configured it.",{"type":254,"tag":427,"props":428,"children":430},"callout",{"icon":429},"i-heroicons-chat-bubble-left-20-solid",[431],{"type":254,"tag":255,"props":432,"children":433},{},[434,436,443,445,452],{"type":259,"value":435},"Support for OAuth 2.0 started in ",{"type":254,"tag":262,"props":437,"children":440},{"href":438,"rel":439},"https://blog.jetbrains.com/idea/2023/10/intellij-idea-2023-3-eap-3/#oauth-2.0-support",[266],[441],{"type":259,"value":442},"version 2023.3",{"type":259,"value":444},", however, Authorization Code Flow with PKCE (PKCE challenge is required in the ",{"type":254,"tag":262,"props":446,"children":449},{"href":447,"rel":448},"https://oauth.net/2.1/",[266],[450],{"type":259,"value":451},"OAuth 2.1 specification",{"type":259,"value":453}," is only supported since 2024.1.",{"type":254,"tag":455,"props":456,"children":458},"h3",{"id":457},"oauth-20-authorization-code-flow-with-pkce",[459],{"type":259,"value":460},"OAuth 2.0 authorization code flow with PKCE",{"type":254,"tag":255,"props":462,"children":463},{},[464,466,473],{"type":259,"value":465},"The OAuth 2.0 flow involved in retrieving a valid access token to make requests to an Azure AD B2C protected API is the authorization code flow with PKCE. There are 2 steps in the ",{"type":254,"tag":262,"props":467,"children":470},{"href":468,"rel":469},"https://learn.microsoft.com/en-us/azure/active-directory-b2c/authorization-code-flow",[266],[471],{"type":259,"value":472},"OAuth 2.0 authorization code flow",{"type":259,"value":474},":",{"type":254,"tag":476,"props":477,"children":478},"ol",{},[479,484],{"type":254,"tag":336,"props":480,"children":481},{},[482],{"type":259,"value":483},"Get an authorization code",{"type":254,"tag":336,"props":485,"children":486},{},[487],{"type":259,"value":488},"Exchange the authorization code for an access token",{"type":254,"tag":255,"props":490,"children":491},{},[492],{"type":259,"value":493},"Step 1 involves the user entering their credentials in the login form (Azure AD B2C login form in this case). At first sight, it might appear not very suitable for using HTTP files but the JetBrains HTTP Client handled it by opening the login form in the IDE embedded browser.",{"type":254,"tag":255,"props":495,"children":496},{},[497],{"type":259,"value":498},"For Azure AD B2C,",{"type":254,"tag":332,"props":500,"children":501},{},[502,513],{"type":254,"tag":336,"props":503,"children":504},{},[505,507],{"type":259,"value":506},"the authorize endpoint is ",{"type":254,"tag":389,"props":508,"children":510},{"className":509},[],[511],{"type":259,"value":512},"https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/authorize",{"type":254,"tag":336,"props":514,"children":515},{},[516,518],{"type":259,"value":517},"the token endpoint is ",{"type":254,"tag":389,"props":519,"children":521},{"className":520},[],[522],{"type":259,"value":523},"https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/token",{"type":254,"tag":255,"props":525,"children":526},{},[527],{"type":259,"value":528},"where:",{"type":254,"tag":332,"props":530,"children":531},{},[532,543,554],{"type":254,"tag":336,"props":533,"children":534},{},[535,541],{"type":254,"tag":389,"props":536,"children":538},{"className":537},[],[539],{"type":259,"value":540},"tenant",{"type":259,"value":542}," is the name of the Azure AD B2C tenant",{"type":254,"tag":336,"props":544,"children":545},{},[546,552],{"type":254,"tag":389,"props":547,"children":549},{"className":548},[],[550],{"type":259,"value":551},"clientId",{"type":259,"value":553}," is the application ID of the application registered in Azure AD the B2C tenant",{"type":254,"tag":336,"props":555,"children":556},{},[557,563],{"type":254,"tag":389,"props":558,"children":560},{"className":559},[],[561],{"type":259,"value":562},"policy",{"type":259,"value":564}," is the name of the policy created in the Azure AD B2C tenant",{"type":254,"tag":427,"props":566,"children":568},{"icon":567},"i-heroicons-light-bulb",[569],{"type":254,"tag":255,"props":570,"children":571},{},[572,574,580],{"type":259,"value":573},"When using a custom domain in Azure AD B2C, the endpoints are similar but the ",{"type":254,"tag":389,"props":575,"children":577},{"className":576},[],[578],{"type":259,"value":579},"{tenant}.b2clogin.com",{"type":259,"value":581}," part is replaced by the custom domain.",{"type":254,"tag":255,"props":583,"children":584},{},[585,587,593],{"type":259,"value":586},"If you want to better understand how this flow works, there is a nice diagram in ",{"type":254,"tag":262,"props":588,"children":590},{"href":274,"rel":589},[266],[591],{"type":259,"value":592},"Auth0 documentation",{"type":259,"value":594},".",{"type":254,"tag":455,"props":596,"children":598},{"id":597},"configuration-in-the-jetbrains-http-client",[599],{"type":259,"value":600},"Configuration in the JetBrains HTTP Client",{"type":254,"tag":255,"props":602,"children":603},{},[604],{"type":259,"value":605},"To make the authorization code flow work in the HTTP Client, all I have to do is provide the configuration for the Azure AD B2C tenant in the HTTP environment file.",{"type":254,"tag":255,"props":607,"children":608},{},[609],{"type":259,"value":610},"Here is an example of such configuration:",{"type":254,"tag":612,"props":613,"children":617},"pre",{"className":614,"code":615,"language":616,"meta":205,"style":205},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"apiUrl\": \"https://localhost:5001/api\",\n  \"Security\": {\n    \"Auth\": {\n      \"CIAM\": {\n        \"Type\": \"OAuth2\",\n        \"Grant Type\": \"Authorization Code\",\n        \"PKCE\": true,\n        \"Client ID\": \"3a53c90d-20c4-40e9-b440-4825b70374d7\",\n        \"Scope\": \"openid offline_access profile https://mytenant.onmicrosoft.com/security/user.read\",\n        \"Auth URL\": \"https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/authorize\",\n        \"Token URL\": \"https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/token\",\n        \"Redirect URL\": \"https://localhost:8080/oidc-callback\",\n        \"Acquire Automatically\": true\n      }\n    }\n  }\n}\n","json",[618],{"type":254,"tag":389,"props":619,"children":620},{"__ignoreMap":205},[621,633,677,703,730,757,796,834,860,898,936,974,1012,1050,1076,1085,1094,1103],{"type":254,"tag":622,"props":623,"children":626},"span",{"class":624,"line":625},"line",1,[627],{"type":254,"tag":622,"props":628,"children":630},{"style":629},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[631],{"type":259,"value":632},"{\n",{"type":254,"tag":622,"props":634,"children":636},{"class":624,"line":635},2,[637,642,648,653,657,662,668,672],{"type":254,"tag":622,"props":638,"children":639},{"style":629},[640],{"type":259,"value":641},"  \"",{"type":254,"tag":622,"props":643,"children":645},{"style":644},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[646],{"type":259,"value":647},"apiUrl",{"type":254,"tag":622,"props":649,"children":650},{"style":629},[651],{"type":259,"value":652},"\"",{"type":254,"tag":622,"props":654,"children":655},{"style":629},[656],{"type":259,"value":474},{"type":254,"tag":622,"props":658,"children":659},{"style":629},[660],{"type":259,"value":661}," \"",{"type":254,"tag":622,"props":663,"children":665},{"style":664},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[666],{"type":259,"value":667},"https://localhost:5001/api",{"type":254,"tag":622,"props":669,"children":670},{"style":629},[671],{"type":259,"value":652},{"type":254,"tag":622,"props":673,"children":674},{"style":629},[675],{"type":259,"value":676},",\n",{"type":254,"tag":622,"props":678,"children":680},{"class":624,"line":679},3,[681,685,690,694,698],{"type":254,"tag":622,"props":682,"children":683},{"style":629},[684],{"type":259,"value":641},{"type":254,"tag":622,"props":686,"children":687},{"style":644},[688],{"type":259,"value":689},"Security",{"type":254,"tag":622,"props":691,"children":692},{"style":629},[693],{"type":259,"value":652},{"type":254,"tag":622,"props":695,"children":696},{"style":629},[697],{"type":259,"value":474},{"type":254,"tag":622,"props":699,"children":700},{"style":629},[701],{"type":259,"value":702}," {\n",{"type":254,"tag":622,"props":704,"children":706},{"class":624,"line":705},4,[707,712,718,722,726],{"type":254,"tag":622,"props":708,"children":709},{"style":629},[710],{"type":259,"value":711},"    \"",{"type":254,"tag":622,"props":713,"children":715},{"style":714},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[716],{"type":259,"value":717},"Auth",{"type":254,"tag":622,"props":719,"children":720},{"style":629},[721],{"type":259,"value":652},{"type":254,"tag":622,"props":723,"children":724},{"style":629},[725],{"type":259,"value":474},{"type":254,"tag":622,"props":727,"children":728},{"style":629},[729],{"type":259,"value":702},{"type":254,"tag":622,"props":731,"children":733},{"class":624,"line":732},5,[734,739,745,749,753],{"type":254,"tag":622,"props":735,"children":736},{"style":629},[737],{"type":259,"value":738},"      \"",{"type":254,"tag":622,"props":740,"children":742},{"style":741},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[743],{"type":259,"value":744},"CIAM",{"type":254,"tag":622,"props":746,"children":747},{"style":629},[748],{"type":259,"value":652},{"type":254,"tag":622,"props":750,"children":751},{"style":629},[752],{"type":259,"value":474},{"type":254,"tag":622,"props":754,"children":755},{"style":629},[756],{"type":259,"value":702},{"type":254,"tag":622,"props":758,"children":760},{"class":624,"line":759},6,[761,766,772,776,780,784,788,792],{"type":254,"tag":622,"props":762,"children":763},{"style":629},[764],{"type":259,"value":765},"        \"",{"type":254,"tag":622,"props":767,"children":769},{"style":768},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[770],{"type":259,"value":771},"Type",{"type":254,"tag":622,"props":773,"children":774},{"style":629},[775],{"type":259,"value":652},{"type":254,"tag":622,"props":777,"children":778},{"style":629},[779],{"type":259,"value":474},{"type":254,"tag":622,"props":781,"children":782},{"style":629},[783],{"type":259,"value":661},{"type":254,"tag":622,"props":785,"children":786},{"style":664},[787],{"type":259,"value":247},{"type":254,"tag":622,"props":789,"children":790},{"style":629},[791],{"type":259,"value":652},{"type":254,"tag":622,"props":793,"children":794},{"style":629},[795],{"type":259,"value":676},{"type":254,"tag":622,"props":797,"children":799},{"class":624,"line":798},7,[800,804,809,813,817,821,826,830],{"type":254,"tag":622,"props":801,"children":802},{"style":629},[803],{"type":259,"value":765},{"type":254,"tag":622,"props":805,"children":806},{"style":768},[807],{"type":259,"value":808},"Grant Type",{"type":254,"tag":622,"props":810,"children":811},{"style":629},[812],{"type":259,"value":652},{"type":254,"tag":622,"props":814,"children":815},{"style":629},[816],{"type":259,"value":474},{"type":254,"tag":622,"props":818,"children":819},{"style":629},[820],{"type":259,"value":661},{"type":254,"tag":622,"props":822,"children":823},{"style":664},[824],{"type":259,"value":825},"Authorization Code",{"type":254,"tag":622,"props":827,"children":828},{"style":629},[829],{"type":259,"value":652},{"type":254,"tag":622,"props":831,"children":832},{"style":629},[833],{"type":259,"value":676},{"type":254,"tag":622,"props":835,"children":837},{"class":624,"line":836},8,[838,842,847,851,855],{"type":254,"tag":622,"props":839,"children":840},{"style":629},[841],{"type":259,"value":765},{"type":254,"tag":622,"props":843,"children":844},{"style":768},[845],{"type":259,"value":846},"PKCE",{"type":254,"tag":622,"props":848,"children":849},{"style":629},[850],{"type":259,"value":652},{"type":254,"tag":622,"props":852,"children":853},{"style":629},[854],{"type":259,"value":474},{"type":254,"tag":622,"props":856,"children":857},{"style":629},[858],{"type":259,"value":859}," true,\n",{"type":254,"tag":622,"props":861,"children":863},{"class":624,"line":862},9,[864,868,873,877,881,885,890,894],{"type":254,"tag":622,"props":865,"children":866},{"style":629},[867],{"type":259,"value":765},{"type":254,"tag":622,"props":869,"children":870},{"style":768},[871],{"type":259,"value":872},"Client ID",{"type":254,"tag":622,"props":874,"children":875},{"style":629},[876],{"type":259,"value":652},{"type":254,"tag":622,"props":878,"children":879},{"style":629},[880],{"type":259,"value":474},{"type":254,"tag":622,"props":882,"children":883},{"style":629},[884],{"type":259,"value":661},{"type":254,"tag":622,"props":886,"children":887},{"style":664},[888],{"type":259,"value":889},"3a53c90d-20c4-40e9-b440-4825b70374d7",{"type":254,"tag":622,"props":891,"children":892},{"style":629},[893],{"type":259,"value":652},{"type":254,"tag":622,"props":895,"children":896},{"style":629},[897],{"type":259,"value":676},{"type":254,"tag":622,"props":899,"children":901},{"class":624,"line":900},10,[902,906,911,915,919,923,928,932],{"type":254,"tag":622,"props":903,"children":904},{"style":629},[905],{"type":259,"value":765},{"type":254,"tag":622,"props":907,"children":908},{"style":768},[909],{"type":259,"value":910},"Scope",{"type":254,"tag":622,"props":912,"children":913},{"style":629},[914],{"type":259,"value":652},{"type":254,"tag":622,"props":916,"children":917},{"style":629},[918],{"type":259,"value":474},{"type":254,"tag":622,"props":920,"children":921},{"style":629},[922],{"type":259,"value":661},{"type":254,"tag":622,"props":924,"children":925},{"style":664},[926],{"type":259,"value":927},"openid offline_access profile https://mytenant.onmicrosoft.com/security/user.read",{"type":254,"tag":622,"props":929,"children":930},{"style":629},[931],{"type":259,"value":652},{"type":254,"tag":622,"props":933,"children":934},{"style":629},[935],{"type":259,"value":676},{"type":254,"tag":622,"props":937,"children":939},{"class":624,"line":938},11,[940,944,949,953,957,961,966,970],{"type":254,"tag":622,"props":941,"children":942},{"style":629},[943],{"type":259,"value":765},{"type":254,"tag":622,"props":945,"children":946},{"style":768},[947],{"type":259,"value":948},"Auth URL",{"type":254,"tag":622,"props":950,"children":951},{"style":629},[952],{"type":259,"value":652},{"type":254,"tag":622,"props":954,"children":955},{"style":629},[956],{"type":259,"value":474},{"type":254,"tag":622,"props":958,"children":959},{"style":629},[960],{"type":259,"value":661},{"type":254,"tag":622,"props":962,"children":963},{"style":664},[964],{"type":259,"value":965},"https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/authorize",{"type":254,"tag":622,"props":967,"children":968},{"style":629},[969],{"type":259,"value":652},{"type":254,"tag":622,"props":971,"children":972},{"style":629},[973],{"type":259,"value":676},{"type":254,"tag":622,"props":975,"children":977},{"class":624,"line":976},12,[978,982,987,991,995,999,1004,1008],{"type":254,"tag":622,"props":979,"children":980},{"style":629},[981],{"type":259,"value":765},{"type":254,"tag":622,"props":983,"children":984},{"style":768},[985],{"type":259,"value":986},"Token URL",{"type":254,"tag":622,"props":988,"children":989},{"style":629},[990],{"type":259,"value":652},{"type":254,"tag":622,"props":992,"children":993},{"style":629},[994],{"type":259,"value":474},{"type":254,"tag":622,"props":996,"children":997},{"style":629},[998],{"type":259,"value":661},{"type":254,"tag":622,"props":1000,"children":1001},{"style":664},[1002],{"type":259,"value":1003},"https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/token",{"type":254,"tag":622,"props":1005,"children":1006},{"style":629},[1007],{"type":259,"value":652},{"type":254,"tag":622,"props":1009,"children":1010},{"style":629},[1011],{"type":259,"value":676},{"type":254,"tag":622,"props":1013,"children":1015},{"class":624,"line":1014},13,[1016,1020,1025,1029,1033,1037,1042,1046],{"type":254,"tag":622,"props":1017,"children":1018},{"style":629},[1019],{"type":259,"value":765},{"type":254,"tag":622,"props":1021,"children":1022},{"style":768},[1023],{"type":259,"value":1024},"Redirect URL",{"type":254,"tag":622,"props":1026,"children":1027},{"style":629},[1028],{"type":259,"value":652},{"type":254,"tag":622,"props":1030,"children":1031},{"style":629},[1032],{"type":259,"value":474},{"type":254,"tag":622,"props":1034,"children":1035},{"style":629},[1036],{"type":259,"value":661},{"type":254,"tag":622,"props":1038,"children":1039},{"style":664},[1040],{"type":259,"value":1041},"https://localhost:8080/oidc-callback",{"type":254,"tag":622,"props":1043,"children":1044},{"style":629},[1045],{"type":259,"value":652},{"type":254,"tag":622,"props":1047,"children":1048},{"style":629},[1049],{"type":259,"value":676},{"type":254,"tag":622,"props":1051,"children":1053},{"class":624,"line":1052},14,[1054,1058,1063,1067,1071],{"type":254,"tag":622,"props":1055,"children":1056},{"style":629},[1057],{"type":259,"value":765},{"type":254,"tag":622,"props":1059,"children":1060},{"style":768},[1061],{"type":259,"value":1062},"Acquire Automatically",{"type":254,"tag":622,"props":1064,"children":1065},{"style":629},[1066],{"type":259,"value":652},{"type":254,"tag":622,"props":1068,"children":1069},{"style":629},[1070],{"type":259,"value":474},{"type":254,"tag":622,"props":1072,"children":1073},{"style":629},[1074],{"type":259,"value":1075}," true\n",{"type":254,"tag":622,"props":1077,"children":1079},{"class":624,"line":1078},15,[1080],{"type":254,"tag":622,"props":1081,"children":1082},{"style":629},[1083],{"type":259,"value":1084},"      }\n",{"type":254,"tag":622,"props":1086,"children":1088},{"class":624,"line":1087},16,[1089],{"type":254,"tag":622,"props":1090,"children":1091},{"style":629},[1092],{"type":259,"value":1093},"    }\n",{"type":254,"tag":622,"props":1095,"children":1097},{"class":624,"line":1096},17,[1098],{"type":254,"tag":622,"props":1099,"children":1100},{"style":629},[1101],{"type":259,"value":1102},"  }\n",{"type":254,"tag":622,"props":1104,"children":1106},{"class":624,"line":1105},18,[1107],{"type":254,"tag":622,"props":1108,"children":1109},{"style":629},[1110],{"type":259,"value":1111},"}\n",{"type":254,"tag":427,"props":1113,"children":1114},{"icon":567},[1115],{"type":254,"tag":255,"props":1116,"children":1117},{},[1118],{"type":259,"value":1119},"Instead of setting PKCE to true, you can set if to a JSON object with the code challenge method and code verifier to use in it.",{"type":254,"tag":427,"props":1121,"children":1122},{"icon":429},[1123],{"type":254,"tag":255,"props":1124,"children":1125},{},[1126],{"type":259,"value":1127},"In this example, I have set a local Redirect URL as my front was running locally. But I could also have set the Redirect URL to another environment where my web application is running.",{"type":254,"tag":255,"props":1129,"children":1130},{},[1131,1133,1139],{"type":259,"value":1132},"You can check the ",{"type":254,"tag":262,"props":1134,"children":1136},{"href":274,"rel":1135},[266],[1137],{"type":259,"value":1138},"JetBrains documentation",{"type":259,"value":1140}," to have more information about the HTTP Client support for OAuth 2.0 authorization.",{"type":254,"tag":455,"props":1142,"children":1144},{"id":1143},"authenticated-http-requests-in-the-http-file",[1145],{"type":259,"value":1146},"Authenticated HTTP Requests in the HTTP file",{"type":254,"tag":255,"props":1148,"children":1149},{},[1150],{"type":259,"value":1151},"Once the configuration is set, retrieving an access token can be done with a simple click in the configuration file.",{"type":254,"tag":255,"props":1153,"children":1154},{},[1155],{"type":259,"value":1156},"The authentication process is logged so we can check the requests made and identify any mistakes made in the configuration.",{"type":254,"tag":255,"props":1158,"children":1159},{},[1160],{"type":254,"tag":1161,"props":1162,"children":1168},"img",{"alt":1163,"className":1164,"src":1167},"HTTP authentication log",[1165,1166],"rounded-lg","mx-auto","/posts/images/httpclientsoauht2_1.webp",[],{"type":254,"tag":255,"props":1170,"children":1171},{},[1172,1174,1180],{"type":259,"value":1173},"Hopefully, we don't have to manually retrieve an access token each time we need to execute an HTTP request in an HTTP file of our IDE. We can just use the ",{"type":254,"tag":389,"props":1175,"children":1177},{"className":1176},[],[1178],{"type":259,"value":1179},"{{$auth.token()}}",{"type":259,"value":1181}," variable in the Authorization header of our requests, like this:",{"type":254,"tag":612,"props":1183,"children":1187},{"className":1184,"code":1185,"language":1186,"meta":205,"style":205},"language-http shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","GET {{apiUrl}}/products\nAuthorization: Bearer {{$auth.token(\"CIAM\")}}\n","http",[1188],{"type":254,"tag":389,"props":1189,"children":1190},{"__ignoreMap":205},[1191,1206],{"type":254,"tag":622,"props":1192,"children":1193},{"class":624,"line":625},[1194,1200],{"type":254,"tag":622,"props":1195,"children":1197},{"style":1196},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF;--shiki-light-font-style:italic;--shiki-default-font-style:italic;--shiki-dark-font-style:italic",[1198],{"type":259,"value":1199},"GET",{"type":254,"tag":622,"props":1201,"children":1203},{"style":1202},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[1204],{"type":259,"value":1205}," {{apiUrl}}/products\n",{"type":254,"tag":622,"props":1207,"children":1208},{"class":624,"line":635},[1209,1214,1218],{"type":254,"tag":622,"props":1210,"children":1211},{"style":768},[1212],{"type":259,"value":1213},"Authorization",{"type":254,"tag":622,"props":1215,"children":1216},{"style":741},[1217],{"type":259,"value":474},{"type":254,"tag":622,"props":1219,"children":1220},{"style":664},[1221],{"type":259,"value":1222}," Bearer {{$auth.token(\"CIAM\")}}\n",{"type":254,"tag":255,"props":1224,"children":1225},{},[1226],{"type":259,"value":1227},"The IDE will handle the rest for us.",{"type":254,"tag":282,"props":1229,"children":1231},{"id":1230},"wrapping-up",[1232],{"type":259,"value":1233},"Wrapping up",{"type":254,"tag":255,"props":1235,"children":1236},{},[1237],{"type":259,"value":1238},"The HTTP Client OAuth 2.0 feature in JetBrains IDEs has greatly simplified making authenticated HTTP requests to secure APIs. While this article focused on Azure AD B2C, the same principles apply to other Authorization Servers, with only the authorize and token endpoints differing.",{"type":254,"tag":255,"props":1240,"children":1241},{},[1242,1244,1250,1252,1257],{"type":259,"value":1243},"I hope other IDEs will adopt this feature, using the same convention for the ",{"type":254,"tag":389,"props":1245,"children":1247},{"className":1246},[],[1248],{"type":259,"value":1249},"$auth.token()",{"type":259,"value":1251}," variable and its configuration. The only drawback is for developers not using JetBrains IDEs, who will need to adjust requests containing the ",{"type":254,"tag":389,"props":1253,"children":1255},{"className":1254},[],[1256],{"type":259,"value":1249},{"type":259,"value":1258}," variable to run them in their IDEs.",{"type":254,"tag":1260,"props":1261,"children":1262},"style",{},[1263],{"type":259,"value":1264},"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":205,"searchDepth":635,"depth":635,"links":1266},[1267,1268,1269,1274],{"id":284,"depth":635,"text":287},{"id":322,"depth":635,"text":325},{"id":417,"depth":635,"text":420,"children":1270},[1271,1272,1273],{"id":457,"depth":679,"text":460},{"id":597,"depth":679,"text":600},{"id":1143,"depth":679,"text":1146},{"id":1230,"depth":635,"text":1233},"markdown","content:1.posts:61.http-clients-oauth2.md","1.posts/61.http-clients-oauth2.md","md",{"_path":187,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":186,"description":1280,"lead":1281,"date":1282,"image":1283,"badge":1285,"tags":1287,"ImageAttribution":1290,"body":1291,"_type":1275,"_id":6250,"_source":231,"_file":6251,"_extension":1278},"In this post, we will discuss how to write a small .NET program that retrieves events from an IT event calendar and submits them to another one using AngleSharp.","Playing with AngleSharp","2024-03-04T00:00:00.000Z",{"src":1284},"/images/calendar_1.webp",{"label":1286},"Development",[1288,1289],".NET","AngleSharp","Picture of \u003Ca href=\"https://unsplash.com/fr/@towfiqu999999\">Towfiqu barbhuiya on \u003Ca href=\"https://unsplash.com/fr/photos/un-calendrier-avec-des-boutons-poussoirs-rouges-epingles-bwOAixLG0uc\">Unsplash\u003C/a>",{"type":251,"children":1292,"toc":6240},[1293,1297,1303,1326,1340,1345,1351,1383,1397,1403,1416,1421,1555,1590,1771,1777,1782,1791,1796,2022,2030,2049,2638,2643,2799,2805,2810,2823,3287,3303,3309,3322,3636,3651,3657,3670,6179,6184,6190,6195,6203,6208,6226,6231,6236],{"type":254,"tag":255,"props":1294,"children":1295},{},[1296],{"type":259,"value":1280},{"type":254,"tag":282,"props":1298,"children":1300},{"id":1299},"some-context",[1301],{"type":259,"value":1302},"Some context",{"type":254,"tag":255,"props":1304,"children":1305},{},[1306,1308,1315,1317,1324],{"type":259,"value":1307},"There are plenty of websites that list IT events in the world. One that is particularly popular is the ",{"type":254,"tag":262,"props":1309,"children":1312},{"href":1310,"rel":1311},"https://github.com/scraly/developers-conferences-agenda",[266],[1313],{"type":259,"value":1314},"developers conferences agenda",{"type":259,"value":1316}," Github repository that was created by Aurélie Vache, a well-known French DevRel. This repository is an excellent resource where numerous tech conferences and CFPs (Call for Papers) are listed. Adding a new conference/CFP is very easy for any developer because you just have add it in the readme that contains all the conferences and make a PR. Additionally, there is now a ",{"type":254,"tag":262,"props":1318,"children":1321},{"href":1319,"rel":1320},"https://developers.events/",[266],[1322],{"type":259,"value":1323},"website",{"type":259,"value":1325}," available to easily view the list of conferences.",{"type":254,"tag":255,"props":1327,"children":1328},{},[1329,1331,1338],{"type":259,"value":1330},"Another one I like is the ",{"type":254,"tag":262,"props":1332,"children":1335},{"href":1333,"rel":1334},"https://techcommunitycalendar.com/",[266],[1336],{"type":259,"value":1337},"Tech Community Calendar",{"type":259,"value":1339}," created by Lee Englestone, a Microsoft MVP. What I find interesting it that it does not just list conferences and call for papers but also other tech events like hackathon or meetups. Events are displayed on small cards with thumbnails of the events websites, and you can filter them by country or type of event. Yet, it is less popular than the developers conferences agenda I mentioned before, so there are fewer events listed. There is a form to suggest new events, and I have been submitted events from time to time. However, most events I submit are developer conferences and CFPs that people have already added in the developer conferences agenda.",{"type":254,"tag":255,"props":1341,"children":1342},{},[1343],{"type":259,"value":1344},"So I thought, what if I automate the process of retrieving events from the developer conferences agenda and submitting them to the tech community calendar?",{"type":254,"tag":282,"props":1346,"children":1348},{"id":1347},"its-just-a-poc",[1349],{"type":259,"value":1350},"It's just a PoC!",{"type":254,"tag":255,"props":1352,"children":1353},{},[1354,1356,1363,1365,1372,1374,1381],{"type":259,"value":1355},"At first, I spent too much time thinking about how to schedule and host the program I hadn't even started writing 😁. Of course, time-triggered ",{"type":254,"tag":262,"props":1357,"children":1360},{"href":1358,"rel":1359},"https://azure.microsoft.com/fr-fr/products/functions",[266],[1361],{"type":259,"value":1362},"Azure Functions",{"type":259,"value":1364}," came to my mind, I even considered Durable Functions to break down the process into steps (retrieve events, check for existing events, submitting each event...). Then I thought about ",{"type":254,"tag":262,"props":1366,"children":1369},{"href":1367,"rel":1368},"https://learn.microsoft.com/en-us/azure/container-apps/jobs",[266],[1370],{"type":259,"value":1371},"Jobs in Azure Container Apps",{"type":259,"value":1373},", or Dapr with Azure Container Apps and even ",{"type":254,"tag":262,"props":1375,"children":1378},{"href":1376,"rel":1377},"https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/",[266],[1379],{"type":259,"value":1380},"Dapr Workflows",{"type":259,"value":1382},". Eventually, I realized it did not matter much since it was just a proof of concept. I decided to postpone the choice for later (if ever it goes beyond the poc) and just start coding.",{"type":254,"tag":255,"props":1384,"children":1385},{},[1386,1388,1395],{"type":259,"value":1387},"I often like writing .NET tools or small programs using the Worker Service template because it's straightforward and includes useful features like dependency injection and configuration. However, this time I decided to keep things simple: just a .NET console application and all the code in Program.cs file. With ",{"type":254,"tag":262,"props":1389,"children":1392},{"href":1390,"rel":1391},"https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/top-level-statements",[266],[1393],{"type":259,"value":1394},"top level statement",{"type":259,"value":1396},", it feels similar to writing a Bash or PowerShell script, making it quite convenient for experimenting. Of course, this approach isn't what I would use for a real project.",{"type":254,"tag":282,"props":1398,"children":1400},{"id":1399},"retrieve-developer-conferences",[1401],{"type":259,"value":1402},"Retrieve Developer Conferences",{"type":254,"tag":255,"props":1404,"children":1405},{},[1406,1408,1415],{"type":259,"value":1407},"In addition to the readme file, the developers conferences agenda exposes all the data publicly in JSON ",{"type":254,"tag":262,"props":1409,"children":1412},{"href":1410,"rel":1411},"https://developers.events/all-events.json",[266],[1413],{"type":259,"value":1414},"here",{"type":259,"value":594},{"type":254,"tag":255,"props":1417,"children":1418},{},[1419],{"type":259,"value":1420},"Developer conferences can be easily represented with a record (I only kept the properties I needed):",{"type":254,"tag":612,"props":1422,"children":1426},{"className":1423,"code":1424,"language":1425,"meta":205,"style":205},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","public record DeveloperEvent(\n    string Name,\n    long[] Date,\n    string Hyperlink,\n    string Location,\n    string City,\n    string Country\n);\n","csharp",[1427],{"type":254,"tag":389,"props":1428,"children":1429},{"__ignoreMap":205},[1430,1453,1470,1487,1503,1519,1535,1547],{"type":254,"tag":622,"props":1431,"children":1432},{"class":624,"line":625},[1433,1438,1443,1448],{"type":254,"tag":622,"props":1434,"children":1435},{"style":644},[1436],{"type":259,"value":1437},"public",{"type":254,"tag":622,"props":1439,"children":1440},{"style":714},[1441],{"type":259,"value":1442}," record",{"type":254,"tag":622,"props":1444,"children":1445},{"style":714},[1446],{"type":259,"value":1447}," DeveloperEvent",{"type":254,"tag":622,"props":1449,"children":1450},{"style":629},[1451],{"type":259,"value":1452},"(\n",{"type":254,"tag":622,"props":1454,"children":1455},{"class":624,"line":635},[1456,1461,1466],{"type":254,"tag":622,"props":1457,"children":1458},{"style":629},[1459],{"type":259,"value":1460},"    string",{"type":254,"tag":622,"props":1462,"children":1463},{"style":714},[1464],{"type":259,"value":1465}," Name",{"type":254,"tag":622,"props":1467,"children":1468},{"style":629},[1469],{"type":259,"value":676},{"type":254,"tag":622,"props":1471,"children":1472},{"class":624,"line":679},[1473,1478,1483],{"type":254,"tag":622,"props":1474,"children":1475},{"style":629},[1476],{"type":259,"value":1477},"    long[]",{"type":254,"tag":622,"props":1479,"children":1480},{"style":714},[1481],{"type":259,"value":1482}," Date",{"type":254,"tag":622,"props":1484,"children":1485},{"style":629},[1486],{"type":259,"value":676},{"type":254,"tag":622,"props":1488,"children":1489},{"class":624,"line":705},[1490,1494,1499],{"type":254,"tag":622,"props":1491,"children":1492},{"style":629},[1493],{"type":259,"value":1460},{"type":254,"tag":622,"props":1495,"children":1496},{"style":714},[1497],{"type":259,"value":1498}," Hyperlink",{"type":254,"tag":622,"props":1500,"children":1501},{"style":629},[1502],{"type":259,"value":676},{"type":254,"tag":622,"props":1504,"children":1505},{"class":624,"line":732},[1506,1510,1515],{"type":254,"tag":622,"props":1507,"children":1508},{"style":629},[1509],{"type":259,"value":1460},{"type":254,"tag":622,"props":1511,"children":1512},{"style":714},[1513],{"type":259,"value":1514}," Location",{"type":254,"tag":622,"props":1516,"children":1517},{"style":629},[1518],{"type":259,"value":676},{"type":254,"tag":622,"props":1520,"children":1521},{"class":624,"line":759},[1522,1526,1531],{"type":254,"tag":622,"props":1523,"children":1524},{"style":629},[1525],{"type":259,"value":1460},{"type":254,"tag":622,"props":1527,"children":1528},{"style":714},[1529],{"type":259,"value":1530}," City",{"type":254,"tag":622,"props":1532,"children":1533},{"style":629},[1534],{"type":259,"value":676},{"type":254,"tag":622,"props":1536,"children":1537},{"class":624,"line":798},[1538,1542],{"type":254,"tag":622,"props":1539,"children":1540},{"style":629},[1541],{"type":259,"value":1460},{"type":254,"tag":622,"props":1543,"children":1544},{"style":714},[1545],{"type":259,"value":1546}," Country\n",{"type":254,"tag":622,"props":1548,"children":1549},{"class":624,"line":836},[1550],{"type":254,"tag":622,"props":1551,"children":1552},{"style":629},[1553],{"type":259,"value":1554},");\n",{"type":254,"tag":255,"props":1556,"children":1557},{},[1558,1560,1566,1568,1574,1576,1581,1583,1589],{"type":259,"value":1559},"We can use an ",{"type":254,"tag":389,"props":1561,"children":1563},{"className":1562},[],[1564],{"type":259,"value":1565},"HttpClient",{"type":259,"value":1567}," to retrieve the events. The namespace ",{"type":254,"tag":389,"props":1569,"children":1571},{"className":1570},[],[1572],{"type":259,"value":1573},"System.Net.Http.Json",{"type":259,"value":1575}," contains an interesting method to make the ",{"type":254,"tag":389,"props":1577,"children":1579},{"className":1578},[],[1580],{"type":259,"value":1199},{"type":259,"value":1582}," HTTP call and deserialize the data using ",{"type":254,"tag":389,"props":1584,"children":1586},{"className":1585},[],[1587],{"type":259,"value":1588},"System.Text.Json",{"type":259,"value":594},{"type":254,"tag":612,"props":1591,"children":1593},{"className":1423,"code":1592,"language":1425,"meta":205,"style":205},"using var httpClient = new HttpClient()\n{\n    BaseAddress = new Uri(\"https://developers.events/\")\n};\n\nvar events = await httpClient.GetFromJsonAsync\u003CDeveloperEvent[]>(\"all-events.json\");\n",[1594],{"type":254,"tag":389,"props":1595,"children":1596},{"__ignoreMap":205},[1597,1635,1642,1686,1694,1703],{"type":254,"tag":622,"props":1598,"children":1599},{"class":624,"line":625},[1600,1605,1610,1615,1620,1625,1630],{"type":254,"tag":622,"props":1601,"children":1602},{"style":1196},[1603],{"type":259,"value":1604},"using",{"type":254,"tag":622,"props":1606,"children":1607},{"style":714},[1608],{"type":259,"value":1609}," var",{"type":254,"tag":622,"props":1611,"children":1612},{"style":714},[1613],{"type":259,"value":1614}," httpClient",{"type":254,"tag":622,"props":1616,"children":1617},{"style":629},[1618],{"type":259,"value":1619}," =",{"type":254,"tag":622,"props":1621,"children":1622},{"style":629},[1623],{"type":259,"value":1624}," new",{"type":254,"tag":622,"props":1626,"children":1627},{"style":714},[1628],{"type":259,"value":1629}," HttpClient",{"type":254,"tag":622,"props":1631,"children":1632},{"style":629},[1633],{"type":259,"value":1634},"()\n",{"type":254,"tag":622,"props":1636,"children":1637},{"class":624,"line":635},[1638],{"type":254,"tag":622,"props":1639,"children":1640},{"style":629},[1641],{"type":259,"value":632},{"type":254,"tag":622,"props":1643,"children":1644},{"class":624,"line":679},[1645,1650,1655,1659,1664,1669,1673,1677,1681],{"type":254,"tag":622,"props":1646,"children":1647},{"style":1202},[1648],{"type":259,"value":1649},"    BaseAddress ",{"type":254,"tag":622,"props":1651,"children":1652},{"style":629},[1653],{"type":259,"value":1654},"=",{"type":254,"tag":622,"props":1656,"children":1657},{"style":629},[1658],{"type":259,"value":1624},{"type":254,"tag":622,"props":1660,"children":1661},{"style":714},[1662],{"type":259,"value":1663}," Uri",{"type":254,"tag":622,"props":1665,"children":1666},{"style":629},[1667],{"type":259,"value":1668},"(",{"type":254,"tag":622,"props":1670,"children":1671},{"style":629},[1672],{"type":259,"value":652},{"type":254,"tag":622,"props":1674,"children":1675},{"style":664},[1676],{"type":259,"value":1319},{"type":254,"tag":622,"props":1678,"children":1679},{"style":629},[1680],{"type":259,"value":652},{"type":254,"tag":622,"props":1682,"children":1683},{"style":629},[1684],{"type":259,"value":1685},")\n",{"type":254,"tag":622,"props":1687,"children":1688},{"class":624,"line":705},[1689],{"type":254,"tag":622,"props":1690,"children":1691},{"style":629},[1692],{"type":259,"value":1693},"};\n",{"type":254,"tag":622,"props":1695,"children":1696},{"class":624,"line":732},[1697],{"type":254,"tag":622,"props":1698,"children":1700},{"emptyLinePlaceholder":1699},true,[1701],{"type":259,"value":1702},"\n",{"type":254,"tag":622,"props":1704,"children":1705},{"class":624,"line":759},[1706,1711,1716,1720,1725,1729,1733,1739,1744,1749,1754,1758,1763,1767],{"type":254,"tag":622,"props":1707,"children":1708},{"style":714},[1709],{"type":259,"value":1710},"var",{"type":254,"tag":622,"props":1712,"children":1713},{"style":714},[1714],{"type":259,"value":1715}," events",{"type":254,"tag":622,"props":1717,"children":1718},{"style":629},[1719],{"type":259,"value":1619},{"type":254,"tag":622,"props":1721,"children":1722},{"style":629},[1723],{"type":259,"value":1724}," await",{"type":254,"tag":622,"props":1726,"children":1727},{"style":1202},[1728],{"type":259,"value":1614},{"type":254,"tag":622,"props":1730,"children":1731},{"style":629},[1732],{"type":259,"value":594},{"type":254,"tag":622,"props":1734,"children":1736},{"style":1735},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[1737],{"type":259,"value":1738},"GetFromJsonAsync",{"type":254,"tag":622,"props":1740,"children":1741},{"style":629},[1742],{"type":259,"value":1743},"\u003C",{"type":254,"tag":622,"props":1745,"children":1746},{"style":714},[1747],{"type":259,"value":1748},"DeveloperEvent",{"type":254,"tag":622,"props":1750,"children":1751},{"style":629},[1752],{"type":259,"value":1753},"[]>(",{"type":254,"tag":622,"props":1755,"children":1756},{"style":629},[1757],{"type":259,"value":652},{"type":254,"tag":622,"props":1759,"children":1760},{"style":664},[1761],{"type":259,"value":1762},"all-events.json",{"type":254,"tag":622,"props":1764,"children":1765},{"style":629},[1766],{"type":259,"value":652},{"type":254,"tag":622,"props":1768,"children":1769},{"style":629},[1770],{"type":259,"value":1554},{"type":254,"tag":282,"props":1772,"children":1774},{"id":1773},"convert-events-to-the-proper-format",[1775],{"type":259,"value":1776},"Convert Events To The Proper Format",{"type":254,"tag":255,"props":1778,"children":1779},{},[1780],{"type":259,"value":1781},"The form to submit events in the Tech Community Calendar look likes that:",{"type":254,"tag":255,"props":1783,"children":1784},{},[1785],{"type":254,"tag":1161,"props":1786,"children":1790},{"alt":1787,"className":1788,"src":1789},"Form to submit events to tech community calendar",[1165],"/posts/images/iteventcalendar_tcc.webp",[],{"type":254,"tag":255,"props":1792,"children":1793},{},[1794],{"type":259,"value":1795},"The Tech Community Calendar events can be represented with the following record :",{"type":254,"tag":612,"props":1797,"children":1799},{"className":1423,"code":1798,"language":1425,"meta":205,"style":205},"public record TechCommunityCalendarEvent(\n    string Name,\n    string Url,\n    DateTimeOffset StartDate,\n    DateTimeOffset EndDate,\n    EventType EventType,\n    EventFormat EventFormat,\n    string Country,\n    string City\n)\n{\n    public string? TwitterHandle { get; set; }\n}; \n",[1800],{"type":254,"tag":389,"props":1801,"children":1802},{"__ignoreMap":205},[1803,1823,1838,1854,1871,1887,1904,1921,1937,1949,1956,1963,2015],{"type":254,"tag":622,"props":1804,"children":1805},{"class":624,"line":625},[1806,1810,1814,1819],{"type":254,"tag":622,"props":1807,"children":1808},{"style":644},[1809],{"type":259,"value":1437},{"type":254,"tag":622,"props":1811,"children":1812},{"style":714},[1813],{"type":259,"value":1442},{"type":254,"tag":622,"props":1815,"children":1816},{"style":714},[1817],{"type":259,"value":1818}," TechCommunityCalendarEvent",{"type":254,"tag":622,"props":1820,"children":1821},{"style":629},[1822],{"type":259,"value":1452},{"type":254,"tag":622,"props":1824,"children":1825},{"class":624,"line":635},[1826,1830,1834],{"type":254,"tag":622,"props":1827,"children":1828},{"style":629},[1829],{"type":259,"value":1460},{"type":254,"tag":622,"props":1831,"children":1832},{"style":714},[1833],{"type":259,"value":1465},{"type":254,"tag":622,"props":1835,"children":1836},{"style":629},[1837],{"type":259,"value":676},{"type":254,"tag":622,"props":1839,"children":1840},{"class":624,"line":679},[1841,1845,1850],{"type":254,"tag":622,"props":1842,"children":1843},{"style":629},[1844],{"type":259,"value":1460},{"type":254,"tag":622,"props":1846,"children":1847},{"style":714},[1848],{"type":259,"value":1849}," Url",{"type":254,"tag":622,"props":1851,"children":1852},{"style":629},[1853],{"type":259,"value":676},{"type":254,"tag":622,"props":1855,"children":1856},{"class":624,"line":705},[1857,1862,1867],{"type":254,"tag":622,"props":1858,"children":1859},{"style":714},[1860],{"type":259,"value":1861},"    DateTimeOffset",{"type":254,"tag":622,"props":1863,"children":1864},{"style":714},[1865],{"type":259,"value":1866}," StartDate",{"type":254,"tag":622,"props":1868,"children":1869},{"style":629},[1870],{"type":259,"value":676},{"type":254,"tag":622,"props":1872,"children":1873},{"class":624,"line":732},[1874,1878,1883],{"type":254,"tag":622,"props":1875,"children":1876},{"style":714},[1877],{"type":259,"value":1861},{"type":254,"tag":622,"props":1879,"children":1880},{"style":714},[1881],{"type":259,"value":1882}," EndDate",{"type":254,"tag":622,"props":1884,"children":1885},{"style":629},[1886],{"type":259,"value":676},{"type":254,"tag":622,"props":1888,"children":1889},{"class":624,"line":759},[1890,1895,1900],{"type":254,"tag":622,"props":1891,"children":1892},{"style":714},[1893],{"type":259,"value":1894},"    EventType",{"type":254,"tag":622,"props":1896,"children":1897},{"style":714},[1898],{"type":259,"value":1899}," EventType",{"type":254,"tag":622,"props":1901,"children":1902},{"style":629},[1903],{"type":259,"value":676},{"type":254,"tag":622,"props":1905,"children":1906},{"class":624,"line":798},[1907,1912,1917],{"type":254,"tag":622,"props":1908,"children":1909},{"style":714},[1910],{"type":259,"value":1911},"    EventFormat",{"type":254,"tag":622,"props":1913,"children":1914},{"style":714},[1915],{"type":259,"value":1916}," EventFormat",{"type":254,"tag":622,"props":1918,"children":1919},{"style":629},[1920],{"type":259,"value":676},{"type":254,"tag":622,"props":1922,"children":1923},{"class":624,"line":836},[1924,1928,1933],{"type":254,"tag":622,"props":1925,"children":1926},{"style":629},[1927],{"type":259,"value":1460},{"type":254,"tag":622,"props":1929,"children":1930},{"style":714},[1931],{"type":259,"value":1932}," Country",{"type":254,"tag":622,"props":1934,"children":1935},{"style":629},[1936],{"type":259,"value":676},{"type":254,"tag":622,"props":1938,"children":1939},{"class":624,"line":862},[1940,1944],{"type":254,"tag":622,"props":1941,"children":1942},{"style":629},[1943],{"type":259,"value":1460},{"type":254,"tag":622,"props":1945,"children":1946},{"style":714},[1947],{"type":259,"value":1948}," City\n",{"type":254,"tag":622,"props":1950,"children":1951},{"class":624,"line":900},[1952],{"type":254,"tag":622,"props":1953,"children":1954},{"style":629},[1955],{"type":259,"value":1685},{"type":254,"tag":622,"props":1957,"children":1958},{"class":624,"line":938},[1959],{"type":254,"tag":622,"props":1960,"children":1961},{"style":629},[1962],{"type":259,"value":632},{"type":254,"tag":622,"props":1964,"children":1965},{"class":624,"line":976},[1966,1971,1976,1981,1986,1991,1996,2001,2006,2010],{"type":254,"tag":622,"props":1967,"children":1968},{"style":644},[1969],{"type":259,"value":1970},"    public",{"type":254,"tag":622,"props":1972,"children":1973},{"style":629},[1974],{"type":259,"value":1975}," string",{"type":254,"tag":622,"props":1977,"children":1978},{"style":629},[1979],{"type":259,"value":1980},"?",{"type":254,"tag":622,"props":1982,"children":1983},{"style":714},[1984],{"type":259,"value":1985}," TwitterHandle",{"type":254,"tag":622,"props":1987,"children":1988},{"style":629},[1989],{"type":259,"value":1990}," {",{"type":254,"tag":622,"props":1992,"children":1993},{"style":714},[1994],{"type":259,"value":1995}," get",{"type":254,"tag":622,"props":1997,"children":1998},{"style":629},[1999],{"type":259,"value":2000},";",{"type":254,"tag":622,"props":2002,"children":2003},{"style":714},[2004],{"type":259,"value":2005}," set",{"type":254,"tag":622,"props":2007,"children":2008},{"style":629},[2009],{"type":259,"value":2000},{"type":254,"tag":622,"props":2011,"children":2012},{"style":629},[2013],{"type":259,"value":2014}," }\n",{"type":254,"tag":622,"props":2016,"children":2017},{"class":624,"line":1014},[2018],{"type":254,"tag":622,"props":2019,"children":2020},{"style":629},[2021],{"type":259,"value":1693},{"type":254,"tag":427,"props":2023,"children":2024},{"icon":429},[2025],{"type":254,"tag":255,"props":2026,"children":2027},{},[2028],{"type":259,"value":2029},"Positional parameters in a record are init-only. As I want to set the Twitter URL after the event has been created, I use a read-write property for it.",{"type":254,"tag":255,"props":2031,"children":2032},{},[2033,2035,2040,2042,2048],{"type":259,"value":2034},"We can write a method to convert a ",{"type":254,"tag":389,"props":2036,"children":2038},{"className":2037},[],[2039],{"type":259,"value":1748},{"type":259,"value":2041}," to a ",{"type":254,"tag":389,"props":2043,"children":2045},{"className":2044},[],[2046],{"type":259,"value":2047},"TechCommunityCalendarEvent",{"type":259,"value":474},{"type":254,"tag":612,"props":2050,"children":2052},{"className":1423,"code":2051,"language":1425,"meta":205,"style":205},"TechCommunityCalendarEvent ConvertToTechEvent(DeveloperEvent developerEvent)\n{\n    var startingDate = DateTimeOffset.FromUnixTimeMilliseconds(developerEvent.Date.First());\n    var endingDate = DateTimeOffset.FromUnixTimeMilliseconds(developerEvent.Date.Last());\n    var eventNameContainsYear = int.TryParse(developerEvent.Name.Split(\" \").LastOrDefault(), out var year) \n                                && year == startingDate.Year;\n    return new TechCommunityCalendarEvent(\n        eventNameContainsYear ? developerEvent.Name : $\"{developerEvent.Name} {startingDate.Year}\",\n        developerEvent.Hyperlink,\n        startingDate,\n        endingDate,\n        EventType.Conference,\n        developerEvent.Country is \"Online\" ? EventFormat.Virtual : EventFormat.In_Person,\n        developerEvent.Country,\n        developerEvent.City\n    );\n}\n",[2053],{"type":254,"tag":389,"props":2054,"children":2055},{"__ignoreMap":205},[2056,2085,2092,2155,2212,2315,2351,2371,2448,2469,2481,2493,2514,2587,2607,2623,2631],{"type":254,"tag":622,"props":2057,"children":2058},{"class":624,"line":625},[2059,2063,2068,2072,2076,2081],{"type":254,"tag":622,"props":2060,"children":2061},{"style":714},[2062],{"type":259,"value":2047},{"type":254,"tag":622,"props":2064,"children":2065},{"style":1735},[2066],{"type":259,"value":2067}," ConvertToTechEvent",{"type":254,"tag":622,"props":2069,"children":2070},{"style":629},[2071],{"type":259,"value":1668},{"type":254,"tag":622,"props":2073,"children":2074},{"style":714},[2075],{"type":259,"value":1748},{"type":254,"tag":622,"props":2077,"children":2078},{"style":714},[2079],{"type":259,"value":2080}," developerEvent",{"type":254,"tag":622,"props":2082,"children":2083},{"style":629},[2084],{"type":259,"value":1685},{"type":254,"tag":622,"props":2086,"children":2087},{"class":624,"line":635},[2088],{"type":254,"tag":622,"props":2089,"children":2090},{"style":629},[2091],{"type":259,"value":632},{"type":254,"tag":622,"props":2093,"children":2094},{"class":624,"line":679},[2095,2100,2105,2109,2114,2118,2123,2127,2132,2136,2141,2145,2150],{"type":254,"tag":622,"props":2096,"children":2097},{"style":714},[2098],{"type":259,"value":2099},"    var",{"type":254,"tag":622,"props":2101,"children":2102},{"style":714},[2103],{"type":259,"value":2104}," startingDate",{"type":254,"tag":622,"props":2106,"children":2107},{"style":629},[2108],{"type":259,"value":1619},{"type":254,"tag":622,"props":2110,"children":2111},{"style":1202},[2112],{"type":259,"value":2113}," DateTimeOffset",{"type":254,"tag":622,"props":2115,"children":2116},{"style":629},[2117],{"type":259,"value":594},{"type":254,"tag":622,"props":2119,"children":2120},{"style":1735},[2121],{"type":259,"value":2122},"FromUnixTimeMilliseconds",{"type":254,"tag":622,"props":2124,"children":2125},{"style":629},[2126],{"type":259,"value":1668},{"type":254,"tag":622,"props":2128,"children":2129},{"style":1202},[2130],{"type":259,"value":2131},"developerEvent",{"type":254,"tag":622,"props":2133,"children":2134},{"style":629},[2135],{"type":259,"value":594},{"type":254,"tag":622,"props":2137,"children":2138},{"style":1202},[2139],{"type":259,"value":2140},"Date",{"type":254,"tag":622,"props":2142,"children":2143},{"style":629},[2144],{"type":259,"value":594},{"type":254,"tag":622,"props":2146,"children":2147},{"style":1735},[2148],{"type":259,"value":2149},"First",{"type":254,"tag":622,"props":2151,"children":2152},{"style":629},[2153],{"type":259,"value":2154},"());\n",{"type":254,"tag":622,"props":2156,"children":2157},{"class":624,"line":705},[2158,2162,2167,2171,2175,2179,2183,2187,2191,2195,2199,2203,2208],{"type":254,"tag":622,"props":2159,"children":2160},{"style":714},[2161],{"type":259,"value":2099},{"type":254,"tag":622,"props":2163,"children":2164},{"style":714},[2165],{"type":259,"value":2166}," endingDate",{"type":254,"tag":622,"props":2168,"children":2169},{"style":629},[2170],{"type":259,"value":1619},{"type":254,"tag":622,"props":2172,"children":2173},{"style":1202},[2174],{"type":259,"value":2113},{"type":254,"tag":622,"props":2176,"children":2177},{"style":629},[2178],{"type":259,"value":594},{"type":254,"tag":622,"props":2180,"children":2181},{"style":1735},[2182],{"type":259,"value":2122},{"type":254,"tag":622,"props":2184,"children":2185},{"style":629},[2186],{"type":259,"value":1668},{"type":254,"tag":622,"props":2188,"children":2189},{"style":1202},[2190],{"type":259,"value":2131},{"type":254,"tag":622,"props":2192,"children":2193},{"style":629},[2194],{"type":259,"value":594},{"type":254,"tag":622,"props":2196,"children":2197},{"style":1202},[2198],{"type":259,"value":2140},{"type":254,"tag":622,"props":2200,"children":2201},{"style":629},[2202],{"type":259,"value":594},{"type":254,"tag":622,"props":2204,"children":2205},{"style":1735},[2206],{"type":259,"value":2207},"Last",{"type":254,"tag":622,"props":2209,"children":2210},{"style":629},[2211],{"type":259,"value":2154},{"type":254,"tag":622,"props":2213,"children":2214},{"class":624,"line":732},[2215,2219,2224,2228,2233,2238,2242,2246,2250,2255,2259,2264,2268,2272,2276,2281,2286,2291,2296,2300,2305,2310],{"type":254,"tag":622,"props":2216,"children":2217},{"style":714},[2218],{"type":259,"value":2099},{"type":254,"tag":622,"props":2220,"children":2221},{"style":714},[2222],{"type":259,"value":2223}," eventNameContainsYear",{"type":254,"tag":622,"props":2225,"children":2226},{"style":629},[2227],{"type":259,"value":1619},{"type":254,"tag":622,"props":2229,"children":2230},{"style":629},[2231],{"type":259,"value":2232}," int.",{"type":254,"tag":622,"props":2234,"children":2235},{"style":1735},[2236],{"type":259,"value":2237},"TryParse",{"type":254,"tag":622,"props":2239,"children":2240},{"style":629},[2241],{"type":259,"value":1668},{"type":254,"tag":622,"props":2243,"children":2244},{"style":1202},[2245],{"type":259,"value":2131},{"type":254,"tag":622,"props":2247,"children":2248},{"style":629},[2249],{"type":259,"value":594},{"type":254,"tag":622,"props":2251,"children":2252},{"style":1202},[2253],{"type":259,"value":2254},"Name",{"type":254,"tag":622,"props":2256,"children":2257},{"style":629},[2258],{"type":259,"value":594},{"type":254,"tag":622,"props":2260,"children":2261},{"style":1735},[2262],{"type":259,"value":2263},"Split",{"type":254,"tag":622,"props":2265,"children":2266},{"style":629},[2267],{"type":259,"value":1668},{"type":254,"tag":622,"props":2269,"children":2270},{"style":629},[2271],{"type":259,"value":652},{"type":254,"tag":622,"props":2273,"children":2274},{"style":629},[2275],{"type":259,"value":661},{"type":254,"tag":622,"props":2277,"children":2278},{"style":629},[2279],{"type":259,"value":2280},").",{"type":254,"tag":622,"props":2282,"children":2283},{"style":1735},[2284],{"type":259,"value":2285},"LastOrDefault",{"type":254,"tag":622,"props":2287,"children":2288},{"style":629},[2289],{"type":259,"value":2290},"(),",{"type":254,"tag":622,"props":2292,"children":2293},{"style":644},[2294],{"type":259,"value":2295}," out",{"type":254,"tag":622,"props":2297,"children":2298},{"style":714},[2299],{"type":259,"value":1609},{"type":254,"tag":622,"props":2301,"children":2302},{"style":714},[2303],{"type":259,"value":2304}," year",{"type":254,"tag":622,"props":2306,"children":2307},{"style":629},[2308],{"type":259,"value":2309},")",{"type":254,"tag":622,"props":2311,"children":2312},{"style":1202},[2313],{"type":259,"value":2314}," \n",{"type":254,"tag":622,"props":2316,"children":2317},{"class":624,"line":759},[2318,2323,2328,2333,2337,2341,2346],{"type":254,"tag":622,"props":2319,"children":2320},{"style":629},[2321],{"type":259,"value":2322},"                                &&",{"type":254,"tag":622,"props":2324,"children":2325},{"style":1202},[2326],{"type":259,"value":2327}," year ",{"type":254,"tag":622,"props":2329,"children":2330},{"style":629},[2331],{"type":259,"value":2332},"==",{"type":254,"tag":622,"props":2334,"children":2335},{"style":1202},[2336],{"type":259,"value":2104},{"type":254,"tag":622,"props":2338,"children":2339},{"style":629},[2340],{"type":259,"value":594},{"type":254,"tag":622,"props":2342,"children":2343},{"style":1202},[2344],{"type":259,"value":2345},"Year",{"type":254,"tag":622,"props":2347,"children":2348},{"style":629},[2349],{"type":259,"value":2350},";\n",{"type":254,"tag":622,"props":2352,"children":2353},{"class":624,"line":798},[2354,2359,2363,2367],{"type":254,"tag":622,"props":2355,"children":2356},{"style":1196},[2357],{"type":259,"value":2358},"    return",{"type":254,"tag":622,"props":2360,"children":2361},{"style":629},[2362],{"type":259,"value":1624},{"type":254,"tag":622,"props":2364,"children":2365},{"style":714},[2366],{"type":259,"value":1818},{"type":254,"tag":622,"props":2368,"children":2369},{"style":629},[2370],{"type":259,"value":1452},{"type":254,"tag":622,"props":2372,"children":2373},{"class":624,"line":836},[2374,2379,2383,2387,2391,2396,2400,2405,2409,2413,2417,2422,2426,2431,2435,2439,2444],{"type":254,"tag":622,"props":2375,"children":2376},{"style":1202},[2377],{"type":259,"value":2378},"        eventNameContainsYear ",{"type":254,"tag":622,"props":2380,"children":2381},{"style":629},[2382],{"type":259,"value":1980},{"type":254,"tag":622,"props":2384,"children":2385},{"style":1202},[2386],{"type":259,"value":2080},{"type":254,"tag":622,"props":2388,"children":2389},{"style":629},[2390],{"type":259,"value":594},{"type":254,"tag":622,"props":2392,"children":2393},{"style":1202},[2394],{"type":259,"value":2395},"Name ",{"type":254,"tag":622,"props":2397,"children":2398},{"style":629},[2399],{"type":259,"value":474},{"type":254,"tag":622,"props":2401,"children":2402},{"style":629},[2403],{"type":259,"value":2404}," $\"{",{"type":254,"tag":622,"props":2406,"children":2407},{"style":1202},[2408],{"type":259,"value":2131},{"type":254,"tag":622,"props":2410,"children":2411},{"style":629},[2412],{"type":259,"value":594},{"type":254,"tag":622,"props":2414,"children":2415},{"style":1202},[2416],{"type":259,"value":2254},{"type":254,"tag":622,"props":2418,"children":2419},{"style":629},[2420],{"type":259,"value":2421},"}",{"type":254,"tag":622,"props":2423,"children":2424},{"style":629},[2425],{"type":259,"value":1990},{"type":254,"tag":622,"props":2427,"children":2428},{"style":1202},[2429],{"type":259,"value":2430},"startingDate",{"type":254,"tag":622,"props":2432,"children":2433},{"style":629},[2434],{"type":259,"value":594},{"type":254,"tag":622,"props":2436,"children":2437},{"style":1202},[2438],{"type":259,"value":2345},{"type":254,"tag":622,"props":2440,"children":2441},{"style":629},[2442],{"type":259,"value":2443},"}\"",{"type":254,"tag":622,"props":2445,"children":2446},{"style":629},[2447],{"type":259,"value":676},{"type":254,"tag":622,"props":2449,"children":2450},{"class":624,"line":862},[2451,2456,2460,2465],{"type":254,"tag":622,"props":2452,"children":2453},{"style":1202},[2454],{"type":259,"value":2455},"        developerEvent",{"type":254,"tag":622,"props":2457,"children":2458},{"style":629},[2459],{"type":259,"value":594},{"type":254,"tag":622,"props":2461,"children":2462},{"style":1202},[2463],{"type":259,"value":2464},"Hyperlink",{"type":254,"tag":622,"props":2466,"children":2467},{"style":629},[2468],{"type":259,"value":676},{"type":254,"tag":622,"props":2470,"children":2471},{"class":624,"line":900},[2472,2477],{"type":254,"tag":622,"props":2473,"children":2474},{"style":1202},[2475],{"type":259,"value":2476},"        startingDate",{"type":254,"tag":622,"props":2478,"children":2479},{"style":629},[2480],{"type":259,"value":676},{"type":254,"tag":622,"props":2482,"children":2483},{"class":624,"line":938},[2484,2489],{"type":254,"tag":622,"props":2485,"children":2486},{"style":1202},[2487],{"type":259,"value":2488},"        endingDate",{"type":254,"tag":622,"props":2490,"children":2491},{"style":629},[2492],{"type":259,"value":676},{"type":254,"tag":622,"props":2494,"children":2495},{"class":624,"line":976},[2496,2501,2505,2510],{"type":254,"tag":622,"props":2497,"children":2498},{"style":1202},[2499],{"type":259,"value":2500},"        EventType",{"type":254,"tag":622,"props":2502,"children":2503},{"style":629},[2504],{"type":259,"value":594},{"type":254,"tag":622,"props":2506,"children":2507},{"style":1202},[2508],{"type":259,"value":2509},"Conference",{"type":254,"tag":622,"props":2511,"children":2512},{"style":629},[2513],{"type":259,"value":676},{"type":254,"tag":622,"props":2515,"children":2516},{"class":624,"line":1014},[2517,2521,2525,2530,2535,2539,2544,2548,2553,2557,2561,2566,2570,2574,2578,2583],{"type":254,"tag":622,"props":2518,"children":2519},{"style":1202},[2520],{"type":259,"value":2455},{"type":254,"tag":622,"props":2522,"children":2523},{"style":629},[2524],{"type":259,"value":594},{"type":254,"tag":622,"props":2526,"children":2527},{"style":1202},[2528],{"type":259,"value":2529},"Country ",{"type":254,"tag":622,"props":2531,"children":2532},{"style":629},[2533],{"type":259,"value":2534},"is",{"type":254,"tag":622,"props":2536,"children":2537},{"style":629},[2538],{"type":259,"value":661},{"type":254,"tag":622,"props":2540,"children":2541},{"style":664},[2542],{"type":259,"value":2543},"Online",{"type":254,"tag":622,"props":2545,"children":2546},{"style":629},[2547],{"type":259,"value":652},{"type":254,"tag":622,"props":2549,"children":2550},{"style":629},[2551],{"type":259,"value":2552}," ?",{"type":254,"tag":622,"props":2554,"children":2555},{"style":1202},[2556],{"type":259,"value":1916},{"type":254,"tag":622,"props":2558,"children":2559},{"style":629},[2560],{"type":259,"value":594},{"type":254,"tag":622,"props":2562,"children":2563},{"style":1202},[2564],{"type":259,"value":2565},"Virtual ",{"type":254,"tag":622,"props":2567,"children":2568},{"style":629},[2569],{"type":259,"value":474},{"type":254,"tag":622,"props":2571,"children":2572},{"style":1202},[2573],{"type":259,"value":1916},{"type":254,"tag":622,"props":2575,"children":2576},{"style":629},[2577],{"type":259,"value":594},{"type":254,"tag":622,"props":2579,"children":2580},{"style":1202},[2581],{"type":259,"value":2582},"In_Person",{"type":254,"tag":622,"props":2584,"children":2585},{"style":629},[2586],{"type":259,"value":676},{"type":254,"tag":622,"props":2588,"children":2589},{"class":624,"line":1052},[2590,2594,2598,2603],{"type":254,"tag":622,"props":2591,"children":2592},{"style":1202},[2593],{"type":259,"value":2455},{"type":254,"tag":622,"props":2595,"children":2596},{"style":629},[2597],{"type":259,"value":594},{"type":254,"tag":622,"props":2599,"children":2600},{"style":1202},[2601],{"type":259,"value":2602},"Country",{"type":254,"tag":622,"props":2604,"children":2605},{"style":629},[2606],{"type":259,"value":676},{"type":254,"tag":622,"props":2608,"children":2609},{"class":624,"line":1078},[2610,2614,2618],{"type":254,"tag":622,"props":2611,"children":2612},{"style":1202},[2613],{"type":259,"value":2455},{"type":254,"tag":622,"props":2615,"children":2616},{"style":629},[2617],{"type":259,"value":594},{"type":254,"tag":622,"props":2619,"children":2620},{"style":1202},[2621],{"type":259,"value":2622},"City\n",{"type":254,"tag":622,"props":2624,"children":2625},{"class":624,"line":1087},[2626],{"type":254,"tag":622,"props":2627,"children":2628},{"style":629},[2629],{"type":259,"value":2630},"    );\n",{"type":254,"tag":622,"props":2632,"children":2633},{"class":624,"line":1096},[2634],{"type":254,"tag":622,"props":2635,"children":2636},{"style":629},[2637],{"type":259,"value":1111},{"type":254,"tag":255,"props":2639,"children":2640},{},[2641],{"type":259,"value":2642},"It allows us to convert all retrieved events after filtering on their date to only keep upcoming events.",{"type":254,"tag":612,"props":2644,"children":2646},{"className":1423,"code":2645,"language":1425,"meta":205,"style":205},"var upcomingEvents =  events\n    .Where(e => e.Date.FirstOrDefault() > DateTimeOffset.UtcNow.ToUnixTimeMilliseconds())\n    .Select(ConvertToTechEvent)\n    .ToList();\n",[2647],{"type":254,"tag":389,"props":2648,"children":2649},{"__ignoreMap":205},[2650,2671,2757,2782],{"type":254,"tag":622,"props":2651,"children":2652},{"class":624,"line":625},[2653,2657,2662,2666],{"type":254,"tag":622,"props":2654,"children":2655},{"style":714},[2656],{"type":259,"value":1710},{"type":254,"tag":622,"props":2658,"children":2659},{"style":714},[2660],{"type":259,"value":2661}," upcomingEvents",{"type":254,"tag":622,"props":2663,"children":2664},{"style":629},[2665],{"type":259,"value":1619},{"type":254,"tag":622,"props":2667,"children":2668},{"style":1202},[2669],{"type":259,"value":2670},"  events\n",{"type":254,"tag":622,"props":2672,"children":2673},{"class":624,"line":635},[2674,2679,2684,2688,2693,2698,2703,2707,2711,2715,2720,2725,2730,2734,2738,2743,2747,2752],{"type":254,"tag":622,"props":2675,"children":2676},{"style":629},[2677],{"type":259,"value":2678},"    .",{"type":254,"tag":622,"props":2680,"children":2681},{"style":1735},[2682],{"type":259,"value":2683},"Where",{"type":254,"tag":622,"props":2685,"children":2686},{"style":629},[2687],{"type":259,"value":1668},{"type":254,"tag":622,"props":2689,"children":2690},{"style":714},[2691],{"type":259,"value":2692},"e",{"type":254,"tag":622,"props":2694,"children":2695},{"style":629},[2696],{"type":259,"value":2697}," =>",{"type":254,"tag":622,"props":2699,"children":2700},{"style":1202},[2701],{"type":259,"value":2702}," e",{"type":254,"tag":622,"props":2704,"children":2705},{"style":629},[2706],{"type":259,"value":594},{"type":254,"tag":622,"props":2708,"children":2709},{"style":1202},[2710],{"type":259,"value":2140},{"type":254,"tag":622,"props":2712,"children":2713},{"style":629},[2714],{"type":259,"value":594},{"type":254,"tag":622,"props":2716,"children":2717},{"style":1735},[2718],{"type":259,"value":2719},"FirstOrDefault",{"type":254,"tag":622,"props":2721,"children":2722},{"style":629},[2723],{"type":259,"value":2724},"()",{"type":254,"tag":622,"props":2726,"children":2727},{"style":629},[2728],{"type":259,"value":2729}," >",{"type":254,"tag":622,"props":2731,"children":2732},{"style":1202},[2733],{"type":259,"value":2113},{"type":254,"tag":622,"props":2735,"children":2736},{"style":629},[2737],{"type":259,"value":594},{"type":254,"tag":622,"props":2739,"children":2740},{"style":1202},[2741],{"type":259,"value":2742},"UtcNow",{"type":254,"tag":622,"props":2744,"children":2745},{"style":629},[2746],{"type":259,"value":594},{"type":254,"tag":622,"props":2748,"children":2749},{"style":1735},[2750],{"type":259,"value":2751},"ToUnixTimeMilliseconds",{"type":254,"tag":622,"props":2753,"children":2754},{"style":629},[2755],{"type":259,"value":2756},"())\n",{"type":254,"tag":622,"props":2758,"children":2759},{"class":624,"line":679},[2760,2764,2769,2773,2778],{"type":254,"tag":622,"props":2761,"children":2762},{"style":629},[2763],{"type":259,"value":2678},{"type":254,"tag":622,"props":2765,"children":2766},{"style":1735},[2767],{"type":259,"value":2768},"Select",{"type":254,"tag":622,"props":2770,"children":2771},{"style":629},[2772],{"type":259,"value":1668},{"type":254,"tag":622,"props":2774,"children":2775},{"style":1202},[2776],{"type":259,"value":2777},"ConvertToTechEvent",{"type":254,"tag":622,"props":2779,"children":2780},{"style":629},[2781],{"type":259,"value":1685},{"type":254,"tag":622,"props":2783,"children":2784},{"class":624,"line":705},[2785,2789,2794],{"type":254,"tag":622,"props":2786,"children":2787},{"style":629},[2788],{"type":259,"value":2678},{"type":254,"tag":622,"props":2790,"children":2791},{"style":1735},[2792],{"type":259,"value":2793},"ToList",{"type":254,"tag":622,"props":2795,"children":2796},{"style":629},[2797],{"type":259,"value":2798},"();\n",{"type":254,"tag":282,"props":2800,"children":2802},{"id":2801},"retrieve-an-event-twitter-profile-link",[2803],{"type":259,"value":2804},"Retrieve An Event Twitter Profile Link",{"type":254,"tag":255,"props":2806,"children":2807},{},[2808],{"type":259,"value":2809},"In the submission form, there's an optional field for entering the Twitter Profile Link of an event. That's not something the events from the developers conferences agenda have but that's interesting data that could be useful to supply. All events have an associated website and most of them contain a link to their Twitter Profile on it.",{"type":254,"tag":255,"props":2811,"children":2812},{},[2813,2815,2821],{"type":259,"value":2814},"This is where a library like ",{"type":254,"tag":262,"props":2816,"children":2819},{"href":2817,"rel":2818},"https://github.com/AngleSharp/AngleSharp",[266],[2820],{"type":259,"value":1289},{"type":259,"value":2822},", which can parse HTML according to W3C specifications, becomes useful. Although I have not used this library before, creating a method to find the Twitter URL on an event's webpage is straightforward.",{"type":254,"tag":612,"props":2824,"children":2826},{"className":1423,"code":2825,"language":1425,"meta":205,"style":205},"async Task\u003Cstring?> RetrieveEventTwitterProfileLink(string eventUrl)\n{\n    var context = BrowsingContext.New(Configuration.Default.WithDefaultLoader());\n    var queryDocument = await context.OpenAsync(eventUrl);\n\n    var twitterSelector = \"a[href*='twitter.com'], a[href*='https://x.com']\";\n    var twitterSocialLink = queryDocument.QuerySelector(twitterSelector)\n        ?.GetAttribute(\"href\");\n\n    return Uri.TryCreate(twitterSocialLink, UriKind.Absolute, out var twitterProfileUri) ?\n        // Normalize X/Twitter profile URL by removing query parameters and fragments\n        $\"{twitterProfileUri.Scheme}://{twitterProfileUri.Host}{twitterProfileUri.AbsolutePath}\" : null;\n}\n",[2827],{"type":254,"tag":389,"props":2828,"children":2829},{"__ignoreMap":205},[2830,2879,2886,2947,2993,3000,3033,3075,3109,3116,3190,3199,3280],{"type":254,"tag":622,"props":2831,"children":2832},{"class":624,"line":625},[2833,2838,2843,2847,2852,2857,2862,2866,2870,2875],{"type":254,"tag":622,"props":2834,"children":2835},{"style":644},[2836],{"type":259,"value":2837},"async",{"type":254,"tag":622,"props":2839,"children":2840},{"style":714},[2841],{"type":259,"value":2842}," Task",{"type":254,"tag":622,"props":2844,"children":2845},{"style":629},[2846],{"type":259,"value":1743},{"type":254,"tag":622,"props":2848,"children":2849},{"style":629},[2850],{"type":259,"value":2851},"string",{"type":254,"tag":622,"props":2853,"children":2854},{"style":629},[2855],{"type":259,"value":2856},"?>",{"type":254,"tag":622,"props":2858,"children":2859},{"style":1735},[2860],{"type":259,"value":2861}," RetrieveEventTwitterProfileLink",{"type":254,"tag":622,"props":2863,"children":2864},{"style":629},[2865],{"type":259,"value":1668},{"type":254,"tag":622,"props":2867,"children":2868},{"style":629},[2869],{"type":259,"value":2851},{"type":254,"tag":622,"props":2871,"children":2872},{"style":714},[2873],{"type":259,"value":2874}," eventUrl",{"type":254,"tag":622,"props":2876,"children":2877},{"style":629},[2878],{"type":259,"value":1685},{"type":254,"tag":622,"props":2880,"children":2881},{"class":624,"line":635},[2882],{"type":254,"tag":622,"props":2883,"children":2884},{"style":629},[2885],{"type":259,"value":632},{"type":254,"tag":622,"props":2887,"children":2888},{"class":624,"line":679},[2889,2893,2898,2902,2907,2911,2916,2920,2925,2929,2934,2938,2943],{"type":254,"tag":622,"props":2890,"children":2891},{"style":714},[2892],{"type":259,"value":2099},{"type":254,"tag":622,"props":2894,"children":2895},{"style":714},[2896],{"type":259,"value":2897}," context",{"type":254,"tag":622,"props":2899,"children":2900},{"style":629},[2901],{"type":259,"value":1619},{"type":254,"tag":622,"props":2903,"children":2904},{"style":1202},[2905],{"type":259,"value":2906}," BrowsingContext",{"type":254,"tag":622,"props":2908,"children":2909},{"style":629},[2910],{"type":259,"value":594},{"type":254,"tag":622,"props":2912,"children":2913},{"style":1735},[2914],{"type":259,"value":2915},"New",{"type":254,"tag":622,"props":2917,"children":2918},{"style":629},[2919],{"type":259,"value":1668},{"type":254,"tag":622,"props":2921,"children":2922},{"style":1202},[2923],{"type":259,"value":2924},"Configuration",{"type":254,"tag":622,"props":2926,"children":2927},{"style":629},[2928],{"type":259,"value":594},{"type":254,"tag":622,"props":2930,"children":2931},{"style":1202},[2932],{"type":259,"value":2933},"Default",{"type":254,"tag":622,"props":2935,"children":2936},{"style":629},[2937],{"type":259,"value":594},{"type":254,"tag":622,"props":2939,"children":2940},{"style":1735},[2941],{"type":259,"value":2942},"WithDefaultLoader",{"type":254,"tag":622,"props":2944,"children":2945},{"style":629},[2946],{"type":259,"value":2154},{"type":254,"tag":622,"props":2948,"children":2949},{"class":624,"line":705},[2950,2954,2959,2963,2967,2971,2975,2980,2984,2989],{"type":254,"tag":622,"props":2951,"children":2952},{"style":714},[2953],{"type":259,"value":2099},{"type":254,"tag":622,"props":2955,"children":2956},{"style":714},[2957],{"type":259,"value":2958}," queryDocument",{"type":254,"tag":622,"props":2960,"children":2961},{"style":629},[2962],{"type":259,"value":1619},{"type":254,"tag":622,"props":2964,"children":2965},{"style":629},[2966],{"type":259,"value":1724},{"type":254,"tag":622,"props":2968,"children":2969},{"style":1202},[2970],{"type":259,"value":2897},{"type":254,"tag":622,"props":2972,"children":2973},{"style":629},[2974],{"type":259,"value":594},{"type":254,"tag":622,"props":2976,"children":2977},{"style":1735},[2978],{"type":259,"value":2979},"OpenAsync",{"type":254,"tag":622,"props":2981,"children":2982},{"style":629},[2983],{"type":259,"value":1668},{"type":254,"tag":622,"props":2985,"children":2986},{"style":1202},[2987],{"type":259,"value":2988},"eventUrl",{"type":254,"tag":622,"props":2990,"children":2991},{"style":629},[2992],{"type":259,"value":1554},{"type":254,"tag":622,"props":2994,"children":2995},{"class":624,"line":732},[2996],{"type":254,"tag":622,"props":2997,"children":2998},{"emptyLinePlaceholder":1699},[2999],{"type":259,"value":1702},{"type":254,"tag":622,"props":3001,"children":3002},{"class":624,"line":759},[3003,3007,3012,3016,3020,3025,3029],{"type":254,"tag":622,"props":3004,"children":3005},{"style":714},[3006],{"type":259,"value":2099},{"type":254,"tag":622,"props":3008,"children":3009},{"style":714},[3010],{"type":259,"value":3011}," twitterSelector",{"type":254,"tag":622,"props":3013,"children":3014},{"style":629},[3015],{"type":259,"value":1619},{"type":254,"tag":622,"props":3017,"children":3018},{"style":629},[3019],{"type":259,"value":661},{"type":254,"tag":622,"props":3021,"children":3022},{"style":664},[3023],{"type":259,"value":3024},"a[href*='twitter.com'], a[href*='https://x.com']",{"type":254,"tag":622,"props":3026,"children":3027},{"style":629},[3028],{"type":259,"value":652},{"type":254,"tag":622,"props":3030,"children":3031},{"style":629},[3032],{"type":259,"value":2350},{"type":254,"tag":622,"props":3034,"children":3035},{"class":624,"line":798},[3036,3040,3045,3049,3053,3057,3062,3066,3071],{"type":254,"tag":622,"props":3037,"children":3038},{"style":714},[3039],{"type":259,"value":2099},{"type":254,"tag":622,"props":3041,"children":3042},{"style":714},[3043],{"type":259,"value":3044}," twitterSocialLink",{"type":254,"tag":622,"props":3046,"children":3047},{"style":629},[3048],{"type":259,"value":1619},{"type":254,"tag":622,"props":3050,"children":3051},{"style":1202},[3052],{"type":259,"value":2958},{"type":254,"tag":622,"props":3054,"children":3055},{"style":629},[3056],{"type":259,"value":594},{"type":254,"tag":622,"props":3058,"children":3059},{"style":1735},[3060],{"type":259,"value":3061},"QuerySelector",{"type":254,"tag":622,"props":3063,"children":3064},{"style":629},[3065],{"type":259,"value":1668},{"type":254,"tag":622,"props":3067,"children":3068},{"style":1202},[3069],{"type":259,"value":3070},"twitterSelector",{"type":254,"tag":622,"props":3072,"children":3073},{"style":629},[3074],{"type":259,"value":1685},{"type":254,"tag":622,"props":3076,"children":3077},{"class":624,"line":836},[3078,3083,3088,3092,3096,3101,3105],{"type":254,"tag":622,"props":3079,"children":3080},{"style":629},[3081],{"type":259,"value":3082},"        ?.",{"type":254,"tag":622,"props":3084,"children":3085},{"style":1735},[3086],{"type":259,"value":3087},"GetAttribute",{"type":254,"tag":622,"props":3089,"children":3090},{"style":629},[3091],{"type":259,"value":1668},{"type":254,"tag":622,"props":3093,"children":3094},{"style":629},[3095],{"type":259,"value":652},{"type":254,"tag":622,"props":3097,"children":3098},{"style":664},[3099],{"type":259,"value":3100},"href",{"type":254,"tag":622,"props":3102,"children":3103},{"style":629},[3104],{"type":259,"value":652},{"type":254,"tag":622,"props":3106,"children":3107},{"style":629},[3108],{"type":259,"value":1554},{"type":254,"tag":622,"props":3110,"children":3111},{"class":624,"line":862},[3112],{"type":254,"tag":622,"props":3113,"children":3114},{"emptyLinePlaceholder":1699},[3115],{"type":259,"value":1702},{"type":254,"tag":622,"props":3117,"children":3118},{"class":624,"line":900},[3119,3123,3127,3131,3136,3140,3145,3150,3155,3159,3164,3168,3172,3176,3181,3185],{"type":254,"tag":622,"props":3120,"children":3121},{"style":1196},[3122],{"type":259,"value":2358},{"type":254,"tag":622,"props":3124,"children":3125},{"style":1202},[3126],{"type":259,"value":1663},{"type":254,"tag":622,"props":3128,"children":3129},{"style":629},[3130],{"type":259,"value":594},{"type":254,"tag":622,"props":3132,"children":3133},{"style":1735},[3134],{"type":259,"value":3135},"TryCreate",{"type":254,"tag":622,"props":3137,"children":3138},{"style":629},[3139],{"type":259,"value":1668},{"type":254,"tag":622,"props":3141,"children":3142},{"style":1202},[3143],{"type":259,"value":3144},"twitterSocialLink",{"type":254,"tag":622,"props":3146,"children":3147},{"style":629},[3148],{"type":259,"value":3149},",",{"type":254,"tag":622,"props":3151,"children":3152},{"style":1202},[3153],{"type":259,"value":3154}," UriKind",{"type":254,"tag":622,"props":3156,"children":3157},{"style":629},[3158],{"type":259,"value":594},{"type":254,"tag":622,"props":3160,"children":3161},{"style":1202},[3162],{"type":259,"value":3163},"Absolute",{"type":254,"tag":622,"props":3165,"children":3166},{"style":629},[3167],{"type":259,"value":3149},{"type":254,"tag":622,"props":3169,"children":3170},{"style":644},[3171],{"type":259,"value":2295},{"type":254,"tag":622,"props":3173,"children":3174},{"style":714},[3175],{"type":259,"value":1609},{"type":254,"tag":622,"props":3177,"children":3178},{"style":714},[3179],{"type":259,"value":3180}," twitterProfileUri",{"type":254,"tag":622,"props":3182,"children":3183},{"style":629},[3184],{"type":259,"value":2309},{"type":254,"tag":622,"props":3186,"children":3187},{"style":629},[3188],{"type":259,"value":3189}," ?\n",{"type":254,"tag":622,"props":3191,"children":3192},{"class":624,"line":938},[3193],{"type":254,"tag":622,"props":3194,"children":3196},{"style":3195},"--shiki-light:#90A4AE;--shiki-default:#546E7A;--shiki-dark:#676E95;--shiki-light-font-style:italic;--shiki-default-font-style:italic;--shiki-dark-font-style:italic",[3197],{"type":259,"value":3198},"        // Normalize X/Twitter profile URL by removing query parameters and fragments\n",{"type":254,"tag":622,"props":3200,"children":3201},{"class":624,"line":976},[3202,3207,3212,3216,3221,3225,3230,3235,3239,3243,3248,3253,3257,3261,3266,3270,3275],{"type":254,"tag":622,"props":3203,"children":3204},{"style":629},[3205],{"type":259,"value":3206},"        $\"{",{"type":254,"tag":622,"props":3208,"children":3209},{"style":1202},[3210],{"type":259,"value":3211},"twitterProfileUri",{"type":254,"tag":622,"props":3213,"children":3214},{"style":629},[3215],{"type":259,"value":594},{"type":254,"tag":622,"props":3217,"children":3218},{"style":1202},[3219],{"type":259,"value":3220},"Scheme",{"type":254,"tag":622,"props":3222,"children":3223},{"style":629},[3224],{"type":259,"value":2421},{"type":254,"tag":622,"props":3226,"children":3227},{"style":664},[3228],{"type":259,"value":3229},"://",{"type":254,"tag":622,"props":3231,"children":3232},{"style":629},[3233],{"type":259,"value":3234},"{",{"type":254,"tag":622,"props":3236,"children":3237},{"style":1202},[3238],{"type":259,"value":3211},{"type":254,"tag":622,"props":3240,"children":3241},{"style":629},[3242],{"type":259,"value":594},{"type":254,"tag":622,"props":3244,"children":3245},{"style":1202},[3246],{"type":259,"value":3247},"Host",{"type":254,"tag":622,"props":3249,"children":3250},{"style":629},[3251],{"type":259,"value":3252},"}{",{"type":254,"tag":622,"props":3254,"children":3255},{"style":1202},[3256],{"type":259,"value":3211},{"type":254,"tag":622,"props":3258,"children":3259},{"style":629},[3260],{"type":259,"value":594},{"type":254,"tag":622,"props":3262,"children":3263},{"style":1202},[3264],{"type":259,"value":3265},"AbsolutePath",{"type":254,"tag":622,"props":3267,"children":3268},{"style":629},[3269],{"type":259,"value":2443},{"type":254,"tag":622,"props":3271,"children":3272},{"style":629},[3273],{"type":259,"value":3274}," :",{"type":254,"tag":622,"props":3276,"children":3277},{"style":629},[3278],{"type":259,"value":3279}," null;\n",{"type":254,"tag":622,"props":3281,"children":3282},{"class":624,"line":1014},[3283],{"type":254,"tag":622,"props":3284,"children":3285},{"style":629},[3286],{"type":259,"value":1111},{"type":254,"tag":427,"props":3288,"children":3289},{"icon":429},[3290],{"type":254,"tag":255,"props":3291,"children":3292},{},[3293,3295,3301],{"type":259,"value":3294},"As the DOM API exposed follows the W3C specifications, it is very convenient. If you can retrieve something with ",{"type":254,"tag":389,"props":3296,"children":3298},{"className":3297},[],[3299],{"type":259,"value":3300},"document.querySelector",{"type":259,"value":3302}," in your browser console, you will be able to retrieve it using the same selector in your AngleSharp code.",{"type":254,"tag":282,"props":3304,"children":3306},{"id":3305},"submit-an-event",[3307],{"type":259,"value":3308},"Submit An Event",{"type":254,"tag":255,"props":3310,"children":3311},{},[3312,3314,3320],{"type":259,"value":3313},"Submitting forms is also possible using AngleSharp. We first have to retrieve the form element in the HTML document using the query sector ",{"type":254,"tag":389,"props":3315,"children":3317},{"className":3316},[],[3318],{"type":259,"value":3319},"form[action=\"/addevent/\"]",{"type":259,"value":3321},". Then we can directly submit the event.",{"type":254,"tag":612,"props":3323,"children":3325},{"className":1423,"code":3324,"language":1425,"meta":205,"style":205},"async Task SubmitEventToTechCommunityCalendar(TechCommunityCalendarEvent techCommunityCalendarEvent)\n{\n    var context = BrowsingContext.New(Configuration.Default.WithDefaultLoader());\n    var queryDocument = await context.OpenAsync(\"https://techcommunitycalendar.com/addevent/\");\n    var form = queryDocument.QuerySelector\u003CIHtmlFormElement>(\"\"\"form[action=\"/addevent/\"]\"\"\");\n    if (form is not null)\n    {\n        var response = await form.SubmitAsync(techCommunityCalendarEvent);\n    }\n}\n",[3326],{"type":254,"tag":389,"props":3327,"children":3328},{"__ignoreMap":205},[3329,3362,3369,3424,3476,3535,3567,3575,3622,3629],{"type":254,"tag":622,"props":3330,"children":3331},{"class":624,"line":625},[3332,3336,3340,3345,3349,3353,3358],{"type":254,"tag":622,"props":3333,"children":3334},{"style":644},[3335],{"type":259,"value":2837},{"type":254,"tag":622,"props":3337,"children":3338},{"style":714},[3339],{"type":259,"value":2842},{"type":254,"tag":622,"props":3341,"children":3342},{"style":1735},[3343],{"type":259,"value":3344}," SubmitEventToTechCommunityCalendar",{"type":254,"tag":622,"props":3346,"children":3347},{"style":629},[3348],{"type":259,"value":1668},{"type":254,"tag":622,"props":3350,"children":3351},{"style":714},[3352],{"type":259,"value":2047},{"type":254,"tag":622,"props":3354,"children":3355},{"style":714},[3356],{"type":259,"value":3357}," techCommunityCalendarEvent",{"type":254,"tag":622,"props":3359,"children":3360},{"style":629},[3361],{"type":259,"value":1685},{"type":254,"tag":622,"props":3363,"children":3364},{"class":624,"line":635},[3365],{"type":254,"tag":622,"props":3366,"children":3367},{"style":629},[3368],{"type":259,"value":632},{"type":254,"tag":622,"props":3370,"children":3371},{"class":624,"line":679},[3372,3376,3380,3384,3388,3392,3396,3400,3404,3408,3412,3416,3420],{"type":254,"tag":622,"props":3373,"children":3374},{"style":714},[3375],{"type":259,"value":2099},{"type":254,"tag":622,"props":3377,"children":3378},{"style":714},[3379],{"type":259,"value":2897},{"type":254,"tag":622,"props":3381,"children":3382},{"style":629},[3383],{"type":259,"value":1619},{"type":254,"tag":622,"props":3385,"children":3386},{"style":1202},[3387],{"type":259,"value":2906},{"type":254,"tag":622,"props":3389,"children":3390},{"style":629},[3391],{"type":259,"value":594},{"type":254,"tag":622,"props":3393,"children":3394},{"style":1735},[3395],{"type":259,"value":2915},{"type":254,"tag":622,"props":3397,"children":3398},{"style":629},[3399],{"type":259,"value":1668},{"type":254,"tag":622,"props":3401,"children":3402},{"style":1202},[3403],{"type":259,"value":2924},{"type":254,"tag":622,"props":3405,"children":3406},{"style":629},[3407],{"type":259,"value":594},{"type":254,"tag":622,"props":3409,"children":3410},{"style":1202},[3411],{"type":259,"value":2933},{"type":254,"tag":622,"props":3413,"children":3414},{"style":629},[3415],{"type":259,"value":594},{"type":254,"tag":622,"props":3417,"children":3418},{"style":1735},[3419],{"type":259,"value":2942},{"type":254,"tag":622,"props":3421,"children":3422},{"style":629},[3423],{"type":259,"value":2154},{"type":254,"tag":622,"props":3425,"children":3426},{"class":624,"line":705},[3427,3431,3435,3439,3443,3447,3451,3455,3459,3463,3468,3472],{"type":254,"tag":622,"props":3428,"children":3429},{"style":714},[3430],{"type":259,"value":2099},{"type":254,"tag":622,"props":3432,"children":3433},{"style":714},[3434],{"type":259,"value":2958},{"type":254,"tag":622,"props":3436,"children":3437},{"style":629},[3438],{"type":259,"value":1619},{"type":254,"tag":622,"props":3440,"children":3441},{"style":629},[3442],{"type":259,"value":1724},{"type":254,"tag":622,"props":3444,"children":3445},{"style":1202},[3446],{"type":259,"value":2897},{"type":254,"tag":622,"props":3448,"children":3449},{"style":629},[3450],{"type":259,"value":594},{"type":254,"tag":622,"props":3452,"children":3453},{"style":1735},[3454],{"type":259,"value":2979},{"type":254,"tag":622,"props":3456,"children":3457},{"style":629},[3458],{"type":259,"value":1668},{"type":254,"tag":622,"props":3460,"children":3461},{"style":629},[3462],{"type":259,"value":652},{"type":254,"tag":622,"props":3464,"children":3465},{"style":664},[3466],{"type":259,"value":3467},"https://techcommunitycalendar.com/addevent/",{"type":254,"tag":622,"props":3469,"children":3470},{"style":629},[3471],{"type":259,"value":652},{"type":254,"tag":622,"props":3473,"children":3474},{"style":629},[3475],{"type":259,"value":1554},{"type":254,"tag":622,"props":3477,"children":3478},{"class":624,"line":732},[3479,3483,3488,3492,3496,3500,3504,3508,3513,3518,3523,3527,3531],{"type":254,"tag":622,"props":3480,"children":3481},{"style":714},[3482],{"type":259,"value":2099},{"type":254,"tag":622,"props":3484,"children":3485},{"style":714},[3486],{"type":259,"value":3487}," form",{"type":254,"tag":622,"props":3489,"children":3490},{"style":629},[3491],{"type":259,"value":1619},{"type":254,"tag":622,"props":3493,"children":3494},{"style":1202},[3495],{"type":259,"value":2958},{"type":254,"tag":622,"props":3497,"children":3498},{"style":629},[3499],{"type":259,"value":594},{"type":254,"tag":622,"props":3501,"children":3502},{"style":1735},[3503],{"type":259,"value":3061},{"type":254,"tag":622,"props":3505,"children":3506},{"style":629},[3507],{"type":259,"value":1743},{"type":254,"tag":622,"props":3509,"children":3510},{"style":714},[3511],{"type":259,"value":3512},"IHtmlFormElement",{"type":254,"tag":622,"props":3514,"children":3515},{"style":629},[3516],{"type":259,"value":3517},">(",{"type":254,"tag":622,"props":3519,"children":3520},{"style":629},[3521],{"type":259,"value":3522},"\"\"\"",{"type":254,"tag":622,"props":3524,"children":3525},{"style":664},[3526],{"type":259,"value":3319},{"type":254,"tag":622,"props":3528,"children":3529},{"style":629},[3530],{"type":259,"value":3522},{"type":254,"tag":622,"props":3532,"children":3533},{"style":629},[3534],{"type":259,"value":1554},{"type":254,"tag":622,"props":3536,"children":3537},{"class":624,"line":759},[3538,3543,3548,3553,3557,3562],{"type":254,"tag":622,"props":3539,"children":3540},{"style":1196},[3541],{"type":259,"value":3542},"    if",{"type":254,"tag":622,"props":3544,"children":3545},{"style":629},[3546],{"type":259,"value":3547}," (",{"type":254,"tag":622,"props":3549,"children":3550},{"style":1202},[3551],{"type":259,"value":3552},"form ",{"type":254,"tag":622,"props":3554,"children":3555},{"style":629},[3556],{"type":259,"value":2534},{"type":254,"tag":622,"props":3558,"children":3559},{"style":629},[3560],{"type":259,"value":3561}," not",{"type":254,"tag":622,"props":3563,"children":3564},{"style":629},[3565],{"type":259,"value":3566}," null)\n",{"type":254,"tag":622,"props":3568,"children":3569},{"class":624,"line":798},[3570],{"type":254,"tag":622,"props":3571,"children":3572},{"style":629},[3573],{"type":259,"value":3574},"    {\n",{"type":254,"tag":622,"props":3576,"children":3577},{"class":624,"line":836},[3578,3583,3588,3592,3596,3600,3604,3609,3613,3618],{"type":254,"tag":622,"props":3579,"children":3580},{"style":714},[3581],{"type":259,"value":3582},"        var",{"type":254,"tag":622,"props":3584,"children":3585},{"style":714},[3586],{"type":259,"value":3587}," response",{"type":254,"tag":622,"props":3589,"children":3590},{"style":629},[3591],{"type":259,"value":1619},{"type":254,"tag":622,"props":3593,"children":3594},{"style":629},[3595],{"type":259,"value":1724},{"type":254,"tag":622,"props":3597,"children":3598},{"style":1202},[3599],{"type":259,"value":3487},{"type":254,"tag":622,"props":3601,"children":3602},{"style":629},[3603],{"type":259,"value":594},{"type":254,"tag":622,"props":3605,"children":3606},{"style":1735},[3607],{"type":259,"value":3608},"SubmitAsync",{"type":254,"tag":622,"props":3610,"children":3611},{"style":629},[3612],{"type":259,"value":1668},{"type":254,"tag":622,"props":3614,"children":3615},{"style":1202},[3616],{"type":259,"value":3617},"techCommunityCalendarEvent",{"type":254,"tag":622,"props":3619,"children":3620},{"style":629},[3621],{"type":259,"value":1554},{"type":254,"tag":622,"props":3623,"children":3624},{"class":624,"line":862},[3625],{"type":254,"tag":622,"props":3626,"children":3627},{"style":629},[3628],{"type":259,"value":1093},{"type":254,"tag":622,"props":3630,"children":3631},{"class":624,"line":900},[3632],{"type":254,"tag":622,"props":3633,"children":3634},{"style":629},[3635],{"type":259,"value":1111},{"type":254,"tag":427,"props":3637,"children":3638},{"icon":429},[3639],{"type":254,"tag":255,"props":3640,"children":3641},{},[3642,3644,3649],{"type":259,"value":3643},"I intentionally named the properties in the ",{"type":254,"tag":389,"props":3645,"children":3647},{"className":3646},[],[3648],{"type":259,"value":2047},{"type":259,"value":3650}," record with the same names as the fields in the form. This way, I can directly submit the event without any transformation. Otherwise, I would have to convert the event to an anonymous object with the correct names.",{"type":254,"tag":282,"props":3652,"children":3654},{"id":3653},"the-full-program",[3655],{"type":259,"value":3656},"The Full Program",{"type":254,"tag":255,"props":3658,"children":3659},{},[3660,3662,3668],{"type":259,"value":3661},"Here is the content of the complete ",{"type":254,"tag":389,"props":3663,"children":3665},{"className":3664},[],[3666],{"type":259,"value":3667},"Program.cs",{"type":259,"value":3669}," file.",{"type":254,"tag":612,"props":3671,"children":3673},{"className":1423,"code":3672,"filename":3667,"language":1425,"meta":205,"style":205},"using System.Net.Http.Json;\nusing AngleSharp;\nusing AngleSharp.Dom;\nusing AngleSharp.Html.Dom;\n\nusing var httpClient = new HttpClient()\n{\n    BaseAddress = new Uri(\"https://developers.events/\")\n};\n\nvar events = await httpClient.GetFromJsonAsync\u003CDeveloperEvent[]>(\"all-events.json\");\nvar upcomingEvents =  events\n    .Where(e => e.Date.FirstOrDefault() > DateTimeOffset.UtcNow.ToUnixTimeMilliseconds())\n    .Select(ConvertToTechEvent)\n    .ToList();\n\nforeach (var upcomingEvent in upcomingEvents)\n{\n    upcomingEvent.TwitterHandle = await RetrieveEventTwitterProfileLink(upcomingEvent.Url);\n    await SubmitEventToTechCommunityCalendar(upcomingEvent);\n}\n\nasync Task\u003Cstring?> RetrieveEventTwitterProfileLink(string eventUrl)\n{\n    var context = BrowsingContext.New(Configuration.Default.WithDefaultLoader());\n    var queryDocument = await context.OpenAsync(eventUrl);\n\n    var twitterSelector = \"a[href*='twitter.com'], a[href*='https://x.com']\";\n    var twitterSocialLink = queryDocument.QuerySelector(twitterSelector)\n        ?.GetAttribute(\"href\");\n\n    return Uri.TryCreate(twitterSocialLink, UriKind.Absolute, out var twitterProfileUri) ?\n        // Normalize X/Twitter profile URL by removing query parameters and fragments\n        $\"{twitterProfileUri.Scheme}://{twitterProfileUri.Host}{twitterProfileUri.AbsolutePath}\" : null;\n}\n\nasync Task SubmitEventToTechCommunityCalendar(TechCommunityCalendarEvent techCommunityCalendarEvent)\n{\n    var context = BrowsingContext.New(Configuration.Default.WithDefaultLoader());\n    var queryDocument = await context.OpenAsync(\"https://techcommunitycalendar.com/addevent/\");\n    var form = queryDocument.QuerySelector\u003CIHtmlFormElement>(\"\"\"form[action=\"/addevent/\"]\"\"\");\n    if (form is not null)\n    {\n        var response = await form.SubmitAsync(techCommunityCalendarEvent);\n    }\n}\n\nTechCommunityCalendarEvent ConvertToTechEvent(DeveloperEvent developerEvent)\n{\n    var startingDate = DateTimeOffset.FromUnixTimeMilliseconds(developerEvent.Date.First());\n    var endingDate = DateTimeOffset.FromUnixTimeMilliseconds(developerEvent.Date.Last());\n    var eventNameContainsYear = int.TryParse(developerEvent.Name.Split(\" \").LastOrDefault(), out var year) \n                                && year == startingDate.Year;\n    return new TechCommunityCalendarEvent(\n        eventNameContainsYear ? developerEvent.Name : $\"{developerEvent.Name} {startingDate.Year}\",\n        developerEvent.Hyperlink,\n        startingDate,\n        endingDate,\n        EventType.Conference,\n        developerEvent.Country is \"Online\" ? EventFormat.Virtual : EventFormat.In_Person,\n        developerEvent.Country,\n        developerEvent.City\n    );\n}\n\npublic record DeveloperEvent(\n    string Name,\n    long[] Date,\n    string Hyperlink,\n    string Location,\n    string City,\n    string Country\n);\n\npublic record TechCommunityCalendarEvent(\n    string Name,\n    string Url,\n    DateTimeOffset StartDate,\n    DateTimeOffset EndDate,\n    EventType EventType,\n    EventFormat EventFormat,\n    string Country,\n    string City\n)\n{\n    public string? TwitterHandle { get; set; }\n};\n\npublic enum EventFormat\n{\n    Unknown = 1,\n    Virtual = 2,\n    In_Person = 3,\n    Hybrid = 4\n}\n\npublic enum EventType\n{\n    Any = 0,\n    Unknown = 1,\n    Conference = 2,\n    Meetup = 3,\n    Hackathon = 4,\n    Call_For_Papers = 5,\n    Website = 6,\n}\n",[3674],{"type":254,"tag":389,"props":3675,"children":3676},{"__ignoreMap":205},[3677,3720,3736,3760,3792,3799,3830,3837,3876,3883,3890,3949,3968,4043,4066,4081,4088,4122,4129,4181,4206,4214,4222,4266,4274,4330,4374,4382,4414,4454,4486,4494,4562,4570,4642,4650,4658,4690,4698,4754,4806,4862,4890,4898,4942,4950,4958,4966,4994,5002,5058,5114,5206,5238,5258,5330,5350,5362,5374,5394,5462,5482,5498,5506,5514,5522,5542,5558,5574,5590,5606,5622,5634,5642,5650,5670,5686,5702,5718,5734,5750,5766,5782,5794,5802,5810,5854,5862,5870,5888,5896,5918,5940,5962,5980,5988,5996,6013,6021,6043,6063,6084,6105,6127,6149,6171],{"type":254,"tag":622,"props":3678,"children":3679},{"class":624,"line":625},[3680,3684,3689,3693,3698,3702,3707,3711,3716],{"type":254,"tag":622,"props":3681,"children":3682},{"style":741},[3683],{"type":259,"value":1604},{"type":254,"tag":622,"props":3685,"children":3686},{"style":1202},[3687],{"type":259,"value":3688}," System",{"type":254,"tag":622,"props":3690,"children":3691},{"style":629},[3692],{"type":259,"value":594},{"type":254,"tag":622,"props":3694,"children":3695},{"style":1202},[3696],{"type":259,"value":3697},"Net",{"type":254,"tag":622,"props":3699,"children":3700},{"style":629},[3701],{"type":259,"value":594},{"type":254,"tag":622,"props":3703,"children":3704},{"style":1202},[3705],{"type":259,"value":3706},"Http",{"type":254,"tag":622,"props":3708,"children":3709},{"style":629},[3710],{"type":259,"value":594},{"type":254,"tag":622,"props":3712,"children":3713},{"style":1202},[3714],{"type":259,"value":3715},"Json",{"type":254,"tag":622,"props":3717,"children":3718},{"style":629},[3719],{"type":259,"value":2350},{"type":254,"tag":622,"props":3721,"children":3722},{"class":624,"line":635},[3723,3727,3732],{"type":254,"tag":622,"props":3724,"children":3725},{"style":741},[3726],{"type":259,"value":1604},{"type":254,"tag":622,"props":3728,"children":3729},{"style":1202},[3730],{"type":259,"value":3731}," AngleSharp",{"type":254,"tag":622,"props":3733,"children":3734},{"style":629},[3735],{"type":259,"value":2350},{"type":254,"tag":622,"props":3737,"children":3738},{"class":624,"line":679},[3739,3743,3747,3751,3756],{"type":254,"tag":622,"props":3740,"children":3741},{"style":741},[3742],{"type":259,"value":1604},{"type":254,"tag":622,"props":3744,"children":3745},{"style":1202},[3746],{"type":259,"value":3731},{"type":254,"tag":622,"props":3748,"children":3749},{"style":629},[3750],{"type":259,"value":594},{"type":254,"tag":622,"props":3752,"children":3753},{"style":1202},[3754],{"type":259,"value":3755},"Dom",{"type":254,"tag":622,"props":3757,"children":3758},{"style":629},[3759],{"type":259,"value":2350},{"type":254,"tag":622,"props":3761,"children":3762},{"class":624,"line":705},[3763,3767,3771,3775,3780,3784,3788],{"type":254,"tag":622,"props":3764,"children":3765},{"style":741},[3766],{"type":259,"value":1604},{"type":254,"tag":622,"props":3768,"children":3769},{"style":1202},[3770],{"type":259,"value":3731},{"type":254,"tag":622,"props":3772,"children":3773},{"style":629},[3774],{"type":259,"value":594},{"type":254,"tag":622,"props":3776,"children":3777},{"style":1202},[3778],{"type":259,"value":3779},"Html",{"type":254,"tag":622,"props":3781,"children":3782},{"style":629},[3783],{"type":259,"value":594},{"type":254,"tag":622,"props":3785,"children":3786},{"style":1202},[3787],{"type":259,"value":3755},{"type":254,"tag":622,"props":3789,"children":3790},{"style":629},[3791],{"type":259,"value":2350},{"type":254,"tag":622,"props":3793,"children":3794},{"class":624,"line":732},[3795],{"type":254,"tag":622,"props":3796,"children":3797},{"emptyLinePlaceholder":1699},[3798],{"type":259,"value":1702},{"type":254,"tag":622,"props":3800,"children":3801},{"class":624,"line":759},[3802,3806,3810,3814,3818,3822,3826],{"type":254,"tag":622,"props":3803,"children":3804},{"style":1196},[3805],{"type":259,"value":1604},{"type":254,"tag":622,"props":3807,"children":3808},{"style":714},[3809],{"type":259,"value":1609},{"type":254,"tag":622,"props":3811,"children":3812},{"style":714},[3813],{"type":259,"value":1614},{"type":254,"tag":622,"props":3815,"children":3816},{"style":629},[3817],{"type":259,"value":1619},{"type":254,"tag":622,"props":3819,"children":3820},{"style":629},[3821],{"type":259,"value":1624},{"type":254,"tag":622,"props":3823,"children":3824},{"style":714},[3825],{"type":259,"value":1629},{"type":254,"tag":622,"props":3827,"children":3828},{"style":629},[3829],{"type":259,"value":1634},{"type":254,"tag":622,"props":3831,"children":3832},{"class":624,"line":798},[3833],{"type":254,"tag":622,"props":3834,"children":3835},{"style":629},[3836],{"type":259,"value":632},{"type":254,"tag":622,"props":3838,"children":3839},{"class":624,"line":836},[3840,3844,3848,3852,3856,3860,3864,3868,3872],{"type":254,"tag":622,"props":3841,"children":3842},{"style":1202},[3843],{"type":259,"value":1649},{"type":254,"tag":622,"props":3845,"children":3846},{"style":629},[3847],{"type":259,"value":1654},{"type":254,"tag":622,"props":3849,"children":3850},{"style":629},[3851],{"type":259,"value":1624},{"type":254,"tag":622,"props":3853,"children":3854},{"style":714},[3855],{"type":259,"value":1663},{"type":254,"tag":622,"props":3857,"children":3858},{"style":629},[3859],{"type":259,"value":1668},{"type":254,"tag":622,"props":3861,"children":3862},{"style":629},[3863],{"type":259,"value":652},{"type":254,"tag":622,"props":3865,"children":3866},{"style":664},[3867],{"type":259,"value":1319},{"type":254,"tag":622,"props":3869,"children":3870},{"style":629},[3871],{"type":259,"value":652},{"type":254,"tag":622,"props":3873,"children":3874},{"style":629},[3875],{"type":259,"value":1685},{"type":254,"tag":622,"props":3877,"children":3878},{"class":624,"line":862},[3879],{"type":254,"tag":622,"props":3880,"children":3881},{"style":629},[3882],{"type":259,"value":1693},{"type":254,"tag":622,"props":3884,"children":3885},{"class":624,"line":900},[3886],{"type":254,"tag":622,"props":3887,"children":3888},{"emptyLinePlaceholder":1699},[3889],{"type":259,"value":1702},{"type":254,"tag":622,"props":3891,"children":3892},{"class":624,"line":938},[3893,3897,3901,3905,3909,3913,3917,3921,3925,3929,3933,3937,3941,3945],{"type":254,"tag":622,"props":3894,"children":3895},{"style":714},[3896],{"type":259,"value":1710},{"type":254,"tag":622,"props":3898,"children":3899},{"style":714},[3900],{"type":259,"value":1715},{"type":254,"tag":622,"props":3902,"children":3903},{"style":629},[3904],{"type":259,"value":1619},{"type":254,"tag":622,"props":3906,"children":3907},{"style":629},[3908],{"type":259,"value":1724},{"type":254,"tag":622,"props":3910,"children":3911},{"style":1202},[3912],{"type":259,"value":1614},{"type":254,"tag":622,"props":3914,"children":3915},{"style":629},[3916],{"type":259,"value":594},{"type":254,"tag":622,"props":3918,"children":3919},{"style":1735},[3920],{"type":259,"value":1738},{"type":254,"tag":622,"props":3922,"children":3923},{"style":629},[3924],{"type":259,"value":1743},{"type":254,"tag":622,"props":3926,"children":3927},{"style":714},[3928],{"type":259,"value":1748},{"type":254,"tag":622,"props":3930,"children":3931},{"style":629},[3932],{"type":259,"value":1753},{"type":254,"tag":622,"props":3934,"children":3935},{"style":629},[3936],{"type":259,"value":652},{"type":254,"tag":622,"props":3938,"children":3939},{"style":664},[3940],{"type":259,"value":1762},{"type":254,"tag":622,"props":3942,"children":3943},{"style":629},[3944],{"type":259,"value":652},{"type":254,"tag":622,"props":3946,"children":3947},{"style":629},[3948],{"type":259,"value":1554},{"type":254,"tag":622,"props":3950,"children":3951},{"class":624,"line":976},[3952,3956,3960,3964],{"type":254,"tag":622,"props":3953,"children":3954},{"style":714},[3955],{"type":259,"value":1710},{"type":254,"tag":622,"props":3957,"children":3958},{"style":714},[3959],{"type":259,"value":2661},{"type":254,"tag":622,"props":3961,"children":3962},{"style":629},[3963],{"type":259,"value":1619},{"type":254,"tag":622,"props":3965,"children":3966},{"style":1202},[3967],{"type":259,"value":2670},{"type":254,"tag":622,"props":3969,"children":3970},{"class":624,"line":1014},[3971,3975,3979,3983,3987,3991,3995,3999,4003,4007,4011,4015,4019,4023,4027,4031,4035,4039],{"type":254,"tag":622,"props":3972,"children":3973},{"style":629},[3974],{"type":259,"value":2678},{"type":254,"tag":622,"props":3976,"children":3977},{"style":1735},[3978],{"type":259,"value":2683},{"type":254,"tag":622,"props":3980,"children":3981},{"style":629},[3982],{"type":259,"value":1668},{"type":254,"tag":622,"props":3984,"children":3985},{"style":714},[3986],{"type":259,"value":2692},{"type":254,"tag":622,"props":3988,"children":3989},{"style":629},[3990],{"type":259,"value":2697},{"type":254,"tag":622,"props":3992,"children":3993},{"style":1202},[3994],{"type":259,"value":2702},{"type":254,"tag":622,"props":3996,"children":3997},{"style":629},[3998],{"type":259,"value":594},{"type":254,"tag":622,"props":4000,"children":4001},{"style":1202},[4002],{"type":259,"value":2140},{"type":254,"tag":622,"props":4004,"children":4005},{"style":629},[4006],{"type":259,"value":594},{"type":254,"tag":622,"props":4008,"children":4009},{"style":1735},[4010],{"type":259,"value":2719},{"type":254,"tag":622,"props":4012,"children":4013},{"style":629},[4014],{"type":259,"value":2724},{"type":254,"tag":622,"props":4016,"children":4017},{"style":629},[4018],{"type":259,"value":2729},{"type":254,"tag":622,"props":4020,"children":4021},{"style":1202},[4022],{"type":259,"value":2113},{"type":254,"tag":622,"props":4024,"children":4025},{"style":629},[4026],{"type":259,"value":594},{"type":254,"tag":622,"props":4028,"children":4029},{"style":1202},[4030],{"type":259,"value":2742},{"type":254,"tag":622,"props":4032,"children":4033},{"style":629},[4034],{"type":259,"value":594},{"type":254,"tag":622,"props":4036,"children":4037},{"style":1735},[4038],{"type":259,"value":2751},{"type":254,"tag":622,"props":4040,"children":4041},{"style":629},[4042],{"type":259,"value":2756},{"type":254,"tag":622,"props":4044,"children":4045},{"class":624,"line":1052},[4046,4050,4054,4058,4062],{"type":254,"tag":622,"props":4047,"children":4048},{"style":629},[4049],{"type":259,"value":2678},{"type":254,"tag":622,"props":4051,"children":4052},{"style":1735},[4053],{"type":259,"value":2768},{"type":254,"tag":622,"props":4055,"children":4056},{"style":629},[4057],{"type":259,"value":1668},{"type":254,"tag":622,"props":4059,"children":4060},{"style":1202},[4061],{"type":259,"value":2777},{"type":254,"tag":622,"props":4063,"children":4064},{"style":629},[4065],{"type":259,"value":1685},{"type":254,"tag":622,"props":4067,"children":4068},{"class":624,"line":1078},[4069,4073,4077],{"type":254,"tag":622,"props":4070,"children":4071},{"style":629},[4072],{"type":259,"value":2678},{"type":254,"tag":622,"props":4074,"children":4075},{"style":1735},[4076],{"type":259,"value":2793},{"type":254,"tag":622,"props":4078,"children":4079},{"style":629},[4080],{"type":259,"value":2798},{"type":254,"tag":622,"props":4082,"children":4083},{"class":624,"line":1087},[4084],{"type":254,"tag":622,"props":4085,"children":4086},{"emptyLinePlaceholder":1699},[4087],{"type":259,"value":1702},{"type":254,"tag":622,"props":4089,"children":4090},{"class":624,"line":1096},[4091,4096,4100,4104,4109,4114,4118],{"type":254,"tag":622,"props":4092,"children":4093},{"style":1196},[4094],{"type":259,"value":4095},"foreach",{"type":254,"tag":622,"props":4097,"children":4098},{"style":629},[4099],{"type":259,"value":3547},{"type":254,"tag":622,"props":4101,"children":4102},{"style":714},[4103],{"type":259,"value":1710},{"type":254,"tag":622,"props":4105,"children":4106},{"style":714},[4107],{"type":259,"value":4108}," upcomingEvent",{"type":254,"tag":622,"props":4110,"children":4111},{"style":1196},[4112],{"type":259,"value":4113}," in",{"type":254,"tag":622,"props":4115,"children":4116},{"style":1202},[4117],{"type":259,"value":2661},{"type":254,"tag":622,"props":4119,"children":4120},{"style":629},[4121],{"type":259,"value":1685},{"type":254,"tag":622,"props":4123,"children":4124},{"class":624,"line":1105},[4125],{"type":254,"tag":622,"props":4126,"children":4127},{"style":629},[4128],{"type":259,"value":632},{"type":254,"tag":622,"props":4130,"children":4132},{"class":624,"line":4131},19,[4133,4138,4142,4147,4151,4155,4159,4163,4168,4172,4177],{"type":254,"tag":622,"props":4134,"children":4135},{"style":1202},[4136],{"type":259,"value":4137},"    upcomingEvent",{"type":254,"tag":622,"props":4139,"children":4140},{"style":629},[4141],{"type":259,"value":594},{"type":254,"tag":622,"props":4143,"children":4144},{"style":1202},[4145],{"type":259,"value":4146},"TwitterHandle ",{"type":254,"tag":622,"props":4148,"children":4149},{"style":629},[4150],{"type":259,"value":1654},{"type":254,"tag":622,"props":4152,"children":4153},{"style":629},[4154],{"type":259,"value":1724},{"type":254,"tag":622,"props":4156,"children":4157},{"style":1735},[4158],{"type":259,"value":2861},{"type":254,"tag":622,"props":4160,"children":4161},{"style":629},[4162],{"type":259,"value":1668},{"type":254,"tag":622,"props":4164,"children":4165},{"style":1202},[4166],{"type":259,"value":4167},"upcomingEvent",{"type":254,"tag":622,"props":4169,"children":4170},{"style":629},[4171],{"type":259,"value":594},{"type":254,"tag":622,"props":4173,"children":4174},{"style":1202},[4175],{"type":259,"value":4176},"Url",{"type":254,"tag":622,"props":4178,"children":4179},{"style":629},[4180],{"type":259,"value":1554},{"type":254,"tag":622,"props":4182,"children":4184},{"class":624,"line":4183},20,[4185,4190,4194,4198,4202],{"type":254,"tag":622,"props":4186,"children":4187},{"style":629},[4188],{"type":259,"value":4189},"    await",{"type":254,"tag":622,"props":4191,"children":4192},{"style":1735},[4193],{"type":259,"value":3344},{"type":254,"tag":622,"props":4195,"children":4196},{"style":629},[4197],{"type":259,"value":1668},{"type":254,"tag":622,"props":4199,"children":4200},{"style":1202},[4201],{"type":259,"value":4167},{"type":254,"tag":622,"props":4203,"children":4204},{"style":629},[4205],{"type":259,"value":1554},{"type":254,"tag":622,"props":4207,"children":4209},{"class":624,"line":4208},21,[4210],{"type":254,"tag":622,"props":4211,"children":4212},{"style":629},[4213],{"type":259,"value":1111},{"type":254,"tag":622,"props":4215,"children":4217},{"class":624,"line":4216},22,[4218],{"type":254,"tag":622,"props":4219,"children":4220},{"emptyLinePlaceholder":1699},[4221],{"type":259,"value":1702},{"type":254,"tag":622,"props":4223,"children":4225},{"class":624,"line":4224},23,[4226,4230,4234,4238,4242,4246,4250,4254,4258,4262],{"type":254,"tag":622,"props":4227,"children":4228},{"style":644},[4229],{"type":259,"value":2837},{"type":254,"tag":622,"props":4231,"children":4232},{"style":714},[4233],{"type":259,"value":2842},{"type":254,"tag":622,"props":4235,"children":4236},{"style":629},[4237],{"type":259,"value":1743},{"type":254,"tag":622,"props":4239,"children":4240},{"style":629},[4241],{"type":259,"value":2851},{"type":254,"tag":622,"props":4243,"children":4244},{"style":629},[4245],{"type":259,"value":2856},{"type":254,"tag":622,"props":4247,"children":4248},{"style":1735},[4249],{"type":259,"value":2861},{"type":254,"tag":622,"props":4251,"children":4252},{"style":629},[4253],{"type":259,"value":1668},{"type":254,"tag":622,"props":4255,"children":4256},{"style":629},[4257],{"type":259,"value":2851},{"type":254,"tag":622,"props":4259,"children":4260},{"style":714},[4261],{"type":259,"value":2874},{"type":254,"tag":622,"props":4263,"children":4264},{"style":629},[4265],{"type":259,"value":1685},{"type":254,"tag":622,"props":4267,"children":4269},{"class":624,"line":4268},24,[4270],{"type":254,"tag":622,"props":4271,"children":4272},{"style":629},[4273],{"type":259,"value":632},{"type":254,"tag":622,"props":4275,"children":4277},{"class":624,"line":4276},25,[4278,4282,4286,4290,4294,4298,4302,4306,4310,4314,4318,4322,4326],{"type":254,"tag":622,"props":4279,"children":4280},{"style":714},[4281],{"type":259,"value":2099},{"type":254,"tag":622,"props":4283,"children":4284},{"style":714},[4285],{"type":259,"value":2897},{"type":254,"tag":622,"props":4287,"children":4288},{"style":629},[4289],{"type":259,"value":1619},{"type":254,"tag":622,"props":4291,"children":4292},{"style":1202},[4293],{"type":259,"value":2906},{"type":254,"tag":622,"props":4295,"children":4296},{"style":629},[4297],{"type":259,"value":594},{"type":254,"tag":622,"props":4299,"children":4300},{"style":1735},[4301],{"type":259,"value":2915},{"type":254,"tag":622,"props":4303,"children":4304},{"style":629},[4305],{"type":259,"value":1668},{"type":254,"tag":622,"props":4307,"children":4308},{"style":1202},[4309],{"type":259,"value":2924},{"type":254,"tag":622,"props":4311,"children":4312},{"style":629},[4313],{"type":259,"value":594},{"type":254,"tag":622,"props":4315,"children":4316},{"style":1202},[4317],{"type":259,"value":2933},{"type":254,"tag":622,"props":4319,"children":4320},{"style":629},[4321],{"type":259,"value":594},{"type":254,"tag":622,"props":4323,"children":4324},{"style":1735},[4325],{"type":259,"value":2942},{"type":254,"tag":622,"props":4327,"children":4328},{"style":629},[4329],{"type":259,"value":2154},{"type":254,"tag":622,"props":4331,"children":4333},{"class":624,"line":4332},26,[4334,4338,4342,4346,4350,4354,4358,4362,4366,4370],{"type":254,"tag":622,"props":4335,"children":4336},{"style":714},[4337],{"type":259,"value":2099},{"type":254,"tag":622,"props":4339,"children":4340},{"style":714},[4341],{"type":259,"value":2958},{"type":254,"tag":622,"props":4343,"children":4344},{"style":629},[4345],{"type":259,"value":1619},{"type":254,"tag":622,"props":4347,"children":4348},{"style":629},[4349],{"type":259,"value":1724},{"type":254,"tag":622,"props":4351,"children":4352},{"style":1202},[4353],{"type":259,"value":2897},{"type":254,"tag":622,"props":4355,"children":4356},{"style":629},[4357],{"type":259,"value":594},{"type":254,"tag":622,"props":4359,"children":4360},{"style":1735},[4361],{"type":259,"value":2979},{"type":254,"tag":622,"props":4363,"children":4364},{"style":629},[4365],{"type":259,"value":1668},{"type":254,"tag":622,"props":4367,"children":4368},{"style":1202},[4369],{"type":259,"value":2988},{"type":254,"tag":622,"props":4371,"children":4372},{"style":629},[4373],{"type":259,"value":1554},{"type":254,"tag":622,"props":4375,"children":4377},{"class":624,"line":4376},27,[4378],{"type":254,"tag":622,"props":4379,"children":4380},{"emptyLinePlaceholder":1699},[4381],{"type":259,"value":1702},{"type":254,"tag":622,"props":4383,"children":4385},{"class":624,"line":4384},28,[4386,4390,4394,4398,4402,4406,4410],{"type":254,"tag":622,"props":4387,"children":4388},{"style":714},[4389],{"type":259,"value":2099},{"type":254,"tag":622,"props":4391,"children":4392},{"style":714},[4393],{"type":259,"value":3011},{"type":254,"tag":622,"props":4395,"children":4396},{"style":629},[4397],{"type":259,"value":1619},{"type":254,"tag":622,"props":4399,"children":4400},{"style":629},[4401],{"type":259,"value":661},{"type":254,"tag":622,"props":4403,"children":4404},{"style":664},[4405],{"type":259,"value":3024},{"type":254,"tag":622,"props":4407,"children":4408},{"style":629},[4409],{"type":259,"value":652},{"type":254,"tag":622,"props":4411,"children":4412},{"style":629},[4413],{"type":259,"value":2350},{"type":254,"tag":622,"props":4415,"children":4417},{"class":624,"line":4416},29,[4418,4422,4426,4430,4434,4438,4442,4446,4450],{"type":254,"tag":622,"props":4419,"children":4420},{"style":714},[4421],{"type":259,"value":2099},{"type":254,"tag":622,"props":4423,"children":4424},{"style":714},[4425],{"type":259,"value":3044},{"type":254,"tag":622,"props":4427,"children":4428},{"style":629},[4429],{"type":259,"value":1619},{"type":254,"tag":622,"props":4431,"children":4432},{"style":1202},[4433],{"type":259,"value":2958},{"type":254,"tag":622,"props":4435,"children":4436},{"style":629},[4437],{"type":259,"value":594},{"type":254,"tag":622,"props":4439,"children":4440},{"style":1735},[4441],{"type":259,"value":3061},{"type":254,"tag":622,"props":4443,"children":4444},{"style":629},[4445],{"type":259,"value":1668},{"type":254,"tag":622,"props":4447,"children":4448},{"style":1202},[4449],{"type":259,"value":3070},{"type":254,"tag":622,"props":4451,"children":4452},{"style":629},[4453],{"type":259,"value":1685},{"type":254,"tag":622,"props":4455,"children":4457},{"class":624,"line":4456},30,[4458,4462,4466,4470,4474,4478,4482],{"type":254,"tag":622,"props":4459,"children":4460},{"style":629},[4461],{"type":259,"value":3082},{"type":254,"tag":622,"props":4463,"children":4464},{"style":1735},[4465],{"type":259,"value":3087},{"type":254,"tag":622,"props":4467,"children":4468},{"style":629},[4469],{"type":259,"value":1668},{"type":254,"tag":622,"props":4471,"children":4472},{"style":629},[4473],{"type":259,"value":652},{"type":254,"tag":622,"props":4475,"children":4476},{"style":664},[4477],{"type":259,"value":3100},{"type":254,"tag":622,"props":4479,"children":4480},{"style":629},[4481],{"type":259,"value":652},{"type":254,"tag":622,"props":4483,"children":4484},{"style":629},[4485],{"type":259,"value":1554},{"type":254,"tag":622,"props":4487,"children":4489},{"class":624,"line":4488},31,[4490],{"type":254,"tag":622,"props":4491,"children":4492},{"emptyLinePlaceholder":1699},[4493],{"type":259,"value":1702},{"type":254,"tag":622,"props":4495,"children":4497},{"class":624,"line":4496},32,[4498,4502,4506,4510,4514,4518,4522,4526,4530,4534,4538,4542,4546,4550,4554,4558],{"type":254,"tag":622,"props":4499,"children":4500},{"style":1196},[4501],{"type":259,"value":2358},{"type":254,"tag":622,"props":4503,"children":4504},{"style":1202},[4505],{"type":259,"value":1663},{"type":254,"tag":622,"props":4507,"children":4508},{"style":629},[4509],{"type":259,"value":594},{"type":254,"tag":622,"props":4511,"children":4512},{"style":1735},[4513],{"type":259,"value":3135},{"type":254,"tag":622,"props":4515,"children":4516},{"style":629},[4517],{"type":259,"value":1668},{"type":254,"tag":622,"props":4519,"children":4520},{"style":1202},[4521],{"type":259,"value":3144},{"type":254,"tag":622,"props":4523,"children":4524},{"style":629},[4525],{"type":259,"value":3149},{"type":254,"tag":622,"props":4527,"children":4528},{"style":1202},[4529],{"type":259,"value":3154},{"type":254,"tag":622,"props":4531,"children":4532},{"style":629},[4533],{"type":259,"value":594},{"type":254,"tag":622,"props":4535,"children":4536},{"style":1202},[4537],{"type":259,"value":3163},{"type":254,"tag":622,"props":4539,"children":4540},{"style":629},[4541],{"type":259,"value":3149},{"type":254,"tag":622,"props":4543,"children":4544},{"style":644},[4545],{"type":259,"value":2295},{"type":254,"tag":622,"props":4547,"children":4548},{"style":714},[4549],{"type":259,"value":1609},{"type":254,"tag":622,"props":4551,"children":4552},{"style":714},[4553],{"type":259,"value":3180},{"type":254,"tag":622,"props":4555,"children":4556},{"style":629},[4557],{"type":259,"value":2309},{"type":254,"tag":622,"props":4559,"children":4560},{"style":629},[4561],{"type":259,"value":3189},{"type":254,"tag":622,"props":4563,"children":4565},{"class":624,"line":4564},33,[4566],{"type":254,"tag":622,"props":4567,"children":4568},{"style":3195},[4569],{"type":259,"value":3198},{"type":254,"tag":622,"props":4571,"children":4573},{"class":624,"line":4572},34,[4574,4578,4582,4586,4590,4594,4598,4602,4606,4610,4614,4618,4622,4626,4630,4634,4638],{"type":254,"tag":622,"props":4575,"children":4576},{"style":629},[4577],{"type":259,"value":3206},{"type":254,"tag":622,"props":4579,"children":4580},{"style":1202},[4581],{"type":259,"value":3211},{"type":254,"tag":622,"props":4583,"children":4584},{"style":629},[4585],{"type":259,"value":594},{"type":254,"tag":622,"props":4587,"children":4588},{"style":1202},[4589],{"type":259,"value":3220},{"type":254,"tag":622,"props":4591,"children":4592},{"style":629},[4593],{"type":259,"value":2421},{"type":254,"tag":622,"props":4595,"children":4596},{"style":664},[4597],{"type":259,"value":3229},{"type":254,"tag":622,"props":4599,"children":4600},{"style":629},[4601],{"type":259,"value":3234},{"type":254,"tag":622,"props":4603,"children":4604},{"style":1202},[4605],{"type":259,"value":3211},{"type":254,"tag":622,"props":4607,"children":4608},{"style":629},[4609],{"type":259,"value":594},{"type":254,"tag":622,"props":4611,"children":4612},{"style":1202},[4613],{"type":259,"value":3247},{"type":254,"tag":622,"props":4615,"children":4616},{"style":629},[4617],{"type":259,"value":3252},{"type":254,"tag":622,"props":4619,"children":4620},{"style":1202},[4621],{"type":259,"value":3211},{"type":254,"tag":622,"props":4623,"children":4624},{"style":629},[4625],{"type":259,"value":594},{"type":254,"tag":622,"props":4627,"children":4628},{"style":1202},[4629],{"type":259,"value":3265},{"type":254,"tag":622,"props":4631,"children":4632},{"style":629},[4633],{"type":259,"value":2443},{"type":254,"tag":622,"props":4635,"children":4636},{"style":629},[4637],{"type":259,"value":3274},{"type":254,"tag":622,"props":4639,"children":4640},{"style":629},[4641],{"type":259,"value":3279},{"type":254,"tag":622,"props":4643,"children":4645},{"class":624,"line":4644},35,[4646],{"type":254,"tag":622,"props":4647,"children":4648},{"style":629},[4649],{"type":259,"value":1111},{"type":254,"tag":622,"props":4651,"children":4653},{"class":624,"line":4652},36,[4654],{"type":254,"tag":622,"props":4655,"children":4656},{"emptyLinePlaceholder":1699},[4657],{"type":259,"value":1702},{"type":254,"tag":622,"props":4659,"children":4661},{"class":624,"line":4660},37,[4662,4666,4670,4674,4678,4682,4686],{"type":254,"tag":622,"props":4663,"children":4664},{"style":644},[4665],{"type":259,"value":2837},{"type":254,"tag":622,"props":4667,"children":4668},{"style":714},[4669],{"type":259,"value":2842},{"type":254,"tag":622,"props":4671,"children":4672},{"style":1735},[4673],{"type":259,"value":3344},{"type":254,"tag":622,"props":4675,"children":4676},{"style":629},[4677],{"type":259,"value":1668},{"type":254,"tag":622,"props":4679,"children":4680},{"style":714},[4681],{"type":259,"value":2047},{"type":254,"tag":622,"props":4683,"children":4684},{"style":714},[4685],{"type":259,"value":3357},{"type":254,"tag":622,"props":4687,"children":4688},{"style":629},[4689],{"type":259,"value":1685},{"type":254,"tag":622,"props":4691,"children":4693},{"class":624,"line":4692},38,[4694],{"type":254,"tag":622,"props":4695,"children":4696},{"style":629},[4697],{"type":259,"value":632},{"type":254,"tag":622,"props":4699,"children":4701},{"class":624,"line":4700},39,[4702,4706,4710,4714,4718,4722,4726,4730,4734,4738,4742,4746,4750],{"type":254,"tag":622,"props":4703,"children":4704},{"style":714},[4705],{"type":259,"value":2099},{"type":254,"tag":622,"props":4707,"children":4708},{"style":714},[4709],{"type":259,"value":2897},{"type":254,"tag":622,"props":4711,"children":4712},{"style":629},[4713],{"type":259,"value":1619},{"type":254,"tag":622,"props":4715,"children":4716},{"style":1202},[4717],{"type":259,"value":2906},{"type":254,"tag":622,"props":4719,"children":4720},{"style":629},[4721],{"type":259,"value":594},{"type":254,"tag":622,"props":4723,"children":4724},{"style":1735},[4725],{"type":259,"value":2915},{"type":254,"tag":622,"props":4727,"children":4728},{"style":629},[4729],{"type":259,"value":1668},{"type":254,"tag":622,"props":4731,"children":4732},{"style":1202},[4733],{"type":259,"value":2924},{"type":254,"tag":622,"props":4735,"children":4736},{"style":629},[4737],{"type":259,"value":594},{"type":254,"tag":622,"props":4739,"children":4740},{"style":1202},[4741],{"type":259,"value":2933},{"type":254,"tag":622,"props":4743,"children":4744},{"style":629},[4745],{"type":259,"value":594},{"type":254,"tag":622,"props":4747,"children":4748},{"style":1735},[4749],{"type":259,"value":2942},{"type":254,"tag":622,"props":4751,"children":4752},{"style":629},[4753],{"type":259,"value":2154},{"type":254,"tag":622,"props":4755,"children":4757},{"class":624,"line":4756},40,[4758,4762,4766,4770,4774,4778,4782,4786,4790,4794,4798,4802],{"type":254,"tag":622,"props":4759,"children":4760},{"style":714},[4761],{"type":259,"value":2099},{"type":254,"tag":622,"props":4763,"children":4764},{"style":714},[4765],{"type":259,"value":2958},{"type":254,"tag":622,"props":4767,"children":4768},{"style":629},[4769],{"type":259,"value":1619},{"type":254,"tag":622,"props":4771,"children":4772},{"style":629},[4773],{"type":259,"value":1724},{"type":254,"tag":622,"props":4775,"children":4776},{"style":1202},[4777],{"type":259,"value":2897},{"type":254,"tag":622,"props":4779,"children":4780},{"style":629},[4781],{"type":259,"value":594},{"type":254,"tag":622,"props":4783,"children":4784},{"style":1735},[4785],{"type":259,"value":2979},{"type":254,"tag":622,"props":4787,"children":4788},{"style":629},[4789],{"type":259,"value":1668},{"type":254,"tag":622,"props":4791,"children":4792},{"style":629},[4793],{"type":259,"value":652},{"type":254,"tag":622,"props":4795,"children":4796},{"style":664},[4797],{"type":259,"value":3467},{"type":254,"tag":622,"props":4799,"children":4800},{"style":629},[4801],{"type":259,"value":652},{"type":254,"tag":622,"props":4803,"children":4804},{"style":629},[4805],{"type":259,"value":1554},{"type":254,"tag":622,"props":4807,"children":4809},{"class":624,"line":4808},41,[4810,4814,4818,4822,4826,4830,4834,4838,4842,4846,4850,4854,4858],{"type":254,"tag":622,"props":4811,"children":4812},{"style":714},[4813],{"type":259,"value":2099},{"type":254,"tag":622,"props":4815,"children":4816},{"style":714},[4817],{"type":259,"value":3487},{"type":254,"tag":622,"props":4819,"children":4820},{"style":629},[4821],{"type":259,"value":1619},{"type":254,"tag":622,"props":4823,"children":4824},{"style":1202},[4825],{"type":259,"value":2958},{"type":254,"tag":622,"props":4827,"children":4828},{"style":629},[4829],{"type":259,"value":594},{"type":254,"tag":622,"props":4831,"children":4832},{"style":1735},[4833],{"type":259,"value":3061},{"type":254,"tag":622,"props":4835,"children":4836},{"style":629},[4837],{"type":259,"value":1743},{"type":254,"tag":622,"props":4839,"children":4840},{"style":714},[4841],{"type":259,"value":3512},{"type":254,"tag":622,"props":4843,"children":4844},{"style":629},[4845],{"type":259,"value":3517},{"type":254,"tag":622,"props":4847,"children":4848},{"style":629},[4849],{"type":259,"value":3522},{"type":254,"tag":622,"props":4851,"children":4852},{"style":664},[4853],{"type":259,"value":3319},{"type":254,"tag":622,"props":4855,"children":4856},{"style":629},[4857],{"type":259,"value":3522},{"type":254,"tag":622,"props":4859,"children":4860},{"style":629},[4861],{"type":259,"value":1554},{"type":254,"tag":622,"props":4863,"children":4865},{"class":624,"line":4864},42,[4866,4870,4874,4878,4882,4886],{"type":254,"tag":622,"props":4867,"children":4868},{"style":1196},[4869],{"type":259,"value":3542},{"type":254,"tag":622,"props":4871,"children":4872},{"style":629},[4873],{"type":259,"value":3547},{"type":254,"tag":622,"props":4875,"children":4876},{"style":1202},[4877],{"type":259,"value":3552},{"type":254,"tag":622,"props":4879,"children":4880},{"style":629},[4881],{"type":259,"value":2534},{"type":254,"tag":622,"props":4883,"children":4884},{"style":629},[4885],{"type":259,"value":3561},{"type":254,"tag":622,"props":4887,"children":4888},{"style":629},[4889],{"type":259,"value":3566},{"type":254,"tag":622,"props":4891,"children":4893},{"class":624,"line":4892},43,[4894],{"type":254,"tag":622,"props":4895,"children":4896},{"style":629},[4897],{"type":259,"value":3574},{"type":254,"tag":622,"props":4899,"children":4901},{"class":624,"line":4900},44,[4902,4906,4910,4914,4918,4922,4926,4930,4934,4938],{"type":254,"tag":622,"props":4903,"children":4904},{"style":714},[4905],{"type":259,"value":3582},{"type":254,"tag":622,"props":4907,"children":4908},{"style":714},[4909],{"type":259,"value":3587},{"type":254,"tag":622,"props":4911,"children":4912},{"style":629},[4913],{"type":259,"value":1619},{"type":254,"tag":622,"props":4915,"children":4916},{"style":629},[4917],{"type":259,"value":1724},{"type":254,"tag":622,"props":4919,"children":4920},{"style":1202},[4921],{"type":259,"value":3487},{"type":254,"tag":622,"props":4923,"children":4924},{"style":629},[4925],{"type":259,"value":594},{"type":254,"tag":622,"props":4927,"children":4928},{"style":1735},[4929],{"type":259,"value":3608},{"type":254,"tag":622,"props":4931,"children":4932},{"style":629},[4933],{"type":259,"value":1668},{"type":254,"tag":622,"props":4935,"children":4936},{"style":1202},[4937],{"type":259,"value":3617},{"type":254,"tag":622,"props":4939,"children":4940},{"style":629},[4941],{"type":259,"value":1554},{"type":254,"tag":622,"props":4943,"children":4945},{"class":624,"line":4944},45,[4946],{"type":254,"tag":622,"props":4947,"children":4948},{"style":629},[4949],{"type":259,"value":1093},{"type":254,"tag":622,"props":4951,"children":4953},{"class":624,"line":4952},46,[4954],{"type":254,"tag":622,"props":4955,"children":4956},{"style":629},[4957],{"type":259,"value":1111},{"type":254,"tag":622,"props":4959,"children":4961},{"class":624,"line":4960},47,[4962],{"type":254,"tag":622,"props":4963,"children":4964},{"emptyLinePlaceholder":1699},[4965],{"type":259,"value":1702},{"type":254,"tag":622,"props":4967,"children":4969},{"class":624,"line":4968},48,[4970,4974,4978,4982,4986,4990],{"type":254,"tag":622,"props":4971,"children":4972},{"style":714},[4973],{"type":259,"value":2047},{"type":254,"tag":622,"props":4975,"children":4976},{"style":1735},[4977],{"type":259,"value":2067},{"type":254,"tag":622,"props":4979,"children":4980},{"style":629},[4981],{"type":259,"value":1668},{"type":254,"tag":622,"props":4983,"children":4984},{"style":714},[4985],{"type":259,"value":1748},{"type":254,"tag":622,"props":4987,"children":4988},{"style":714},[4989],{"type":259,"value":2080},{"type":254,"tag":622,"props":4991,"children":4992},{"style":629},[4993],{"type":259,"value":1685},{"type":254,"tag":622,"props":4995,"children":4997},{"class":624,"line":4996},49,[4998],{"type":254,"tag":622,"props":4999,"children":5000},{"style":629},[5001],{"type":259,"value":632},{"type":254,"tag":622,"props":5003,"children":5005},{"class":624,"line":5004},50,[5006,5010,5014,5018,5022,5026,5030,5034,5038,5042,5046,5050,5054],{"type":254,"tag":622,"props":5007,"children":5008},{"style":714},[5009],{"type":259,"value":2099},{"type":254,"tag":622,"props":5011,"children":5012},{"style":714},[5013],{"type":259,"value":2104},{"type":254,"tag":622,"props":5015,"children":5016},{"style":629},[5017],{"type":259,"value":1619},{"type":254,"tag":622,"props":5019,"children":5020},{"style":1202},[5021],{"type":259,"value":2113},{"type":254,"tag":622,"props":5023,"children":5024},{"style":629},[5025],{"type":259,"value":594},{"type":254,"tag":622,"props":5027,"children":5028},{"style":1735},[5029],{"type":259,"value":2122},{"type":254,"tag":622,"props":5031,"children":5032},{"style":629},[5033],{"type":259,"value":1668},{"type":254,"tag":622,"props":5035,"children":5036},{"style":1202},[5037],{"type":259,"value":2131},{"type":254,"tag":622,"props":5039,"children":5040},{"style":629},[5041],{"type":259,"value":594},{"type":254,"tag":622,"props":5043,"children":5044},{"style":1202},[5045],{"type":259,"value":2140},{"type":254,"tag":622,"props":5047,"children":5048},{"style":629},[5049],{"type":259,"value":594},{"type":254,"tag":622,"props":5051,"children":5052},{"style":1735},[5053],{"type":259,"value":2149},{"type":254,"tag":622,"props":5055,"children":5056},{"style":629},[5057],{"type":259,"value":2154},{"type":254,"tag":622,"props":5059,"children":5061},{"class":624,"line":5060},51,[5062,5066,5070,5074,5078,5082,5086,5090,5094,5098,5102,5106,5110],{"type":254,"tag":622,"props":5063,"children":5064},{"style":714},[5065],{"type":259,"value":2099},{"type":254,"tag":622,"props":5067,"children":5068},{"style":714},[5069],{"type":259,"value":2166},{"type":254,"tag":622,"props":5071,"children":5072},{"style":629},[5073],{"type":259,"value":1619},{"type":254,"tag":622,"props":5075,"children":5076},{"style":1202},[5077],{"type":259,"value":2113},{"type":254,"tag":622,"props":5079,"children":5080},{"style":629},[5081],{"type":259,"value":594},{"type":254,"tag":622,"props":5083,"children":5084},{"style":1735},[5085],{"type":259,"value":2122},{"type":254,"tag":622,"props":5087,"children":5088},{"style":629},[5089],{"type":259,"value":1668},{"type":254,"tag":622,"props":5091,"children":5092},{"style":1202},[5093],{"type":259,"value":2131},{"type":254,"tag":622,"props":5095,"children":5096},{"style":629},[5097],{"type":259,"value":594},{"type":254,"tag":622,"props":5099,"children":5100},{"style":1202},[5101],{"type":259,"value":2140},{"type":254,"tag":622,"props":5103,"children":5104},{"style":629},[5105],{"type":259,"value":594},{"type":254,"tag":622,"props":5107,"children":5108},{"style":1735},[5109],{"type":259,"value":2207},{"type":254,"tag":622,"props":5111,"children":5112},{"style":629},[5113],{"type":259,"value":2154},{"type":254,"tag":622,"props":5115,"children":5117},{"class":624,"line":5116},52,[5118,5122,5126,5130,5134,5138,5142,5146,5150,5154,5158,5162,5166,5170,5174,5178,5182,5186,5190,5194,5198,5202],{"type":254,"tag":622,"props":5119,"children":5120},{"style":714},[5121],{"type":259,"value":2099},{"type":254,"tag":622,"props":5123,"children":5124},{"style":714},[5125],{"type":259,"value":2223},{"type":254,"tag":622,"props":5127,"children":5128},{"style":629},[5129],{"type":259,"value":1619},{"type":254,"tag":622,"props":5131,"children":5132},{"style":629},[5133],{"type":259,"value":2232},{"type":254,"tag":622,"props":5135,"children":5136},{"style":1735},[5137],{"type":259,"value":2237},{"type":254,"tag":622,"props":5139,"children":5140},{"style":629},[5141],{"type":259,"value":1668},{"type":254,"tag":622,"props":5143,"children":5144},{"style":1202},[5145],{"type":259,"value":2131},{"type":254,"tag":622,"props":5147,"children":5148},{"style":629},[5149],{"type":259,"value":594},{"type":254,"tag":622,"props":5151,"children":5152},{"style":1202},[5153],{"type":259,"value":2254},{"type":254,"tag":622,"props":5155,"children":5156},{"style":629},[5157],{"type":259,"value":594},{"type":254,"tag":622,"props":5159,"children":5160},{"style":1735},[5161],{"type":259,"value":2263},{"type":254,"tag":622,"props":5163,"children":5164},{"style":629},[5165],{"type":259,"value":1668},{"type":254,"tag":622,"props":5167,"children":5168},{"style":629},[5169],{"type":259,"value":652},{"type":254,"tag":622,"props":5171,"children":5172},{"style":629},[5173],{"type":259,"value":661},{"type":254,"tag":622,"props":5175,"children":5176},{"style":629},[5177],{"type":259,"value":2280},{"type":254,"tag":622,"props":5179,"children":5180},{"style":1735},[5181],{"type":259,"value":2285},{"type":254,"tag":622,"props":5183,"children":5184},{"style":629},[5185],{"type":259,"value":2290},{"type":254,"tag":622,"props":5187,"children":5188},{"style":644},[5189],{"type":259,"value":2295},{"type":254,"tag":622,"props":5191,"children":5192},{"style":714},[5193],{"type":259,"value":1609},{"type":254,"tag":622,"props":5195,"children":5196},{"style":714},[5197],{"type":259,"value":2304},{"type":254,"tag":622,"props":5199,"children":5200},{"style":629},[5201],{"type":259,"value":2309},{"type":254,"tag":622,"props":5203,"children":5204},{"style":1202},[5205],{"type":259,"value":2314},{"type":254,"tag":622,"props":5207,"children":5209},{"class":624,"line":5208},53,[5210,5214,5218,5222,5226,5230,5234],{"type":254,"tag":622,"props":5211,"children":5212},{"style":629},[5213],{"type":259,"value":2322},{"type":254,"tag":622,"props":5215,"children":5216},{"style":1202},[5217],{"type":259,"value":2327},{"type":254,"tag":622,"props":5219,"children":5220},{"style":629},[5221],{"type":259,"value":2332},{"type":254,"tag":622,"props":5223,"children":5224},{"style":1202},[5225],{"type":259,"value":2104},{"type":254,"tag":622,"props":5227,"children":5228},{"style":629},[5229],{"type":259,"value":594},{"type":254,"tag":622,"props":5231,"children":5232},{"style":1202},[5233],{"type":259,"value":2345},{"type":254,"tag":622,"props":5235,"children":5236},{"style":629},[5237],{"type":259,"value":2350},{"type":254,"tag":622,"props":5239,"children":5241},{"class":624,"line":5240},54,[5242,5246,5250,5254],{"type":254,"tag":622,"props":5243,"children":5244},{"style":1196},[5245],{"type":259,"value":2358},{"type":254,"tag":622,"props":5247,"children":5248},{"style":629},[5249],{"type":259,"value":1624},{"type":254,"tag":622,"props":5251,"children":5252},{"style":714},[5253],{"type":259,"value":1818},{"type":254,"tag":622,"props":5255,"children":5256},{"style":629},[5257],{"type":259,"value":1452},{"type":254,"tag":622,"props":5259,"children":5261},{"class":624,"line":5260},55,[5262,5266,5270,5274,5278,5282,5286,5290,5294,5298,5302,5306,5310,5314,5318,5322,5326],{"type":254,"tag":622,"props":5263,"children":5264},{"style":1202},[5265],{"type":259,"value":2378},{"type":254,"tag":622,"props":5267,"children":5268},{"style":629},[5269],{"type":259,"value":1980},{"type":254,"tag":622,"props":5271,"children":5272},{"style":1202},[5273],{"type":259,"value":2080},{"type":254,"tag":622,"props":5275,"children":5276},{"style":629},[5277],{"type":259,"value":594},{"type":254,"tag":622,"props":5279,"children":5280},{"style":1202},[5281],{"type":259,"value":2395},{"type":254,"tag":622,"props":5283,"children":5284},{"style":629},[5285],{"type":259,"value":474},{"type":254,"tag":622,"props":5287,"children":5288},{"style":629},[5289],{"type":259,"value":2404},{"type":254,"tag":622,"props":5291,"children":5292},{"style":1202},[5293],{"type":259,"value":2131},{"type":254,"tag":622,"props":5295,"children":5296},{"style":629},[5297],{"type":259,"value":594},{"type":254,"tag":622,"props":5299,"children":5300},{"style":1202},[5301],{"type":259,"value":2254},{"type":254,"tag":622,"props":5303,"children":5304},{"style":629},[5305],{"type":259,"value":2421},{"type":254,"tag":622,"props":5307,"children":5308},{"style":629},[5309],{"type":259,"value":1990},{"type":254,"tag":622,"props":5311,"children":5312},{"style":1202},[5313],{"type":259,"value":2430},{"type":254,"tag":622,"props":5315,"children":5316},{"style":629},[5317],{"type":259,"value":594},{"type":254,"tag":622,"props":5319,"children":5320},{"style":1202},[5321],{"type":259,"value":2345},{"type":254,"tag":622,"props":5323,"children":5324},{"style":629},[5325],{"type":259,"value":2443},{"type":254,"tag":622,"props":5327,"children":5328},{"style":629},[5329],{"type":259,"value":676},{"type":254,"tag":622,"props":5331,"children":5333},{"class":624,"line":5332},56,[5334,5338,5342,5346],{"type":254,"tag":622,"props":5335,"children":5336},{"style":1202},[5337],{"type":259,"value":2455},{"type":254,"tag":622,"props":5339,"children":5340},{"style":629},[5341],{"type":259,"value":594},{"type":254,"tag":622,"props":5343,"children":5344},{"style":1202},[5345],{"type":259,"value":2464},{"type":254,"tag":622,"props":5347,"children":5348},{"style":629},[5349],{"type":259,"value":676},{"type":254,"tag":622,"props":5351,"children":5353},{"class":624,"line":5352},57,[5354,5358],{"type":254,"tag":622,"props":5355,"children":5356},{"style":1202},[5357],{"type":259,"value":2476},{"type":254,"tag":622,"props":5359,"children":5360},{"style":629},[5361],{"type":259,"value":676},{"type":254,"tag":622,"props":5363,"children":5365},{"class":624,"line":5364},58,[5366,5370],{"type":254,"tag":622,"props":5367,"children":5368},{"style":1202},[5369],{"type":259,"value":2488},{"type":254,"tag":622,"props":5371,"children":5372},{"style":629},[5373],{"type":259,"value":676},{"type":254,"tag":622,"props":5375,"children":5377},{"class":624,"line":5376},59,[5378,5382,5386,5390],{"type":254,"tag":622,"props":5379,"children":5380},{"style":1202},[5381],{"type":259,"value":2500},{"type":254,"tag":622,"props":5383,"children":5384},{"style":629},[5385],{"type":259,"value":594},{"type":254,"tag":622,"props":5387,"children":5388},{"style":1202},[5389],{"type":259,"value":2509},{"type":254,"tag":622,"props":5391,"children":5392},{"style":629},[5393],{"type":259,"value":676},{"type":254,"tag":622,"props":5395,"children":5397},{"class":624,"line":5396},60,[5398,5402,5406,5410,5414,5418,5422,5426,5430,5434,5438,5442,5446,5450,5454,5458],{"type":254,"tag":622,"props":5399,"children":5400},{"style":1202},[5401],{"type":259,"value":2455},{"type":254,"tag":622,"props":5403,"children":5404},{"style":629},[5405],{"type":259,"value":594},{"type":254,"tag":622,"props":5407,"children":5408},{"style":1202},[5409],{"type":259,"value":2529},{"type":254,"tag":622,"props":5411,"children":5412},{"style":629},[5413],{"type":259,"value":2534},{"type":254,"tag":622,"props":5415,"children":5416},{"style":629},[5417],{"type":259,"value":661},{"type":254,"tag":622,"props":5419,"children":5420},{"style":664},[5421],{"type":259,"value":2543},{"type":254,"tag":622,"props":5423,"children":5424},{"style":629},[5425],{"type":259,"value":652},{"type":254,"tag":622,"props":5427,"children":5428},{"style":629},[5429],{"type":259,"value":2552},{"type":254,"tag":622,"props":5431,"children":5432},{"style":1202},[5433],{"type":259,"value":1916},{"type":254,"tag":622,"props":5435,"children":5436},{"style":629},[5437],{"type":259,"value":594},{"type":254,"tag":622,"props":5439,"children":5440},{"style":1202},[5441],{"type":259,"value":2565},{"type":254,"tag":622,"props":5443,"children":5444},{"style":629},[5445],{"type":259,"value":474},{"type":254,"tag":622,"props":5447,"children":5448},{"style":1202},[5449],{"type":259,"value":1916},{"type":254,"tag":622,"props":5451,"children":5452},{"style":629},[5453],{"type":259,"value":594},{"type":254,"tag":622,"props":5455,"children":5456},{"style":1202},[5457],{"type":259,"value":2582},{"type":254,"tag":622,"props":5459,"children":5460},{"style":629},[5461],{"type":259,"value":676},{"type":254,"tag":622,"props":5463,"children":5465},{"class":624,"line":5464},61,[5466,5470,5474,5478],{"type":254,"tag":622,"props":5467,"children":5468},{"style":1202},[5469],{"type":259,"value":2455},{"type":254,"tag":622,"props":5471,"children":5472},{"style":629},[5473],{"type":259,"value":594},{"type":254,"tag":622,"props":5475,"children":5476},{"style":1202},[5477],{"type":259,"value":2602},{"type":254,"tag":622,"props":5479,"children":5480},{"style":629},[5481],{"type":259,"value":676},{"type":254,"tag":622,"props":5483,"children":5485},{"class":624,"line":5484},62,[5486,5490,5494],{"type":254,"tag":622,"props":5487,"children":5488},{"style":1202},[5489],{"type":259,"value":2455},{"type":254,"tag":622,"props":5491,"children":5492},{"style":629},[5493],{"type":259,"value":594},{"type":254,"tag":622,"props":5495,"children":5496},{"style":1202},[5497],{"type":259,"value":2622},{"type":254,"tag":622,"props":5499,"children":5501},{"class":624,"line":5500},63,[5502],{"type":254,"tag":622,"props":5503,"children":5504},{"style":629},[5505],{"type":259,"value":2630},{"type":254,"tag":622,"props":5507,"children":5509},{"class":624,"line":5508},64,[5510],{"type":254,"tag":622,"props":5511,"children":5512},{"style":629},[5513],{"type":259,"value":1111},{"type":254,"tag":622,"props":5515,"children":5517},{"class":624,"line":5516},65,[5518],{"type":254,"tag":622,"props":5519,"children":5520},{"emptyLinePlaceholder":1699},[5521],{"type":259,"value":1702},{"type":254,"tag":622,"props":5523,"children":5525},{"class":624,"line":5524},66,[5526,5530,5534,5538],{"type":254,"tag":622,"props":5527,"children":5528},{"style":644},[5529],{"type":259,"value":1437},{"type":254,"tag":622,"props":5531,"children":5532},{"style":714},[5533],{"type":259,"value":1442},{"type":254,"tag":622,"props":5535,"children":5536},{"style":714},[5537],{"type":259,"value":1447},{"type":254,"tag":622,"props":5539,"children":5540},{"style":629},[5541],{"type":259,"value":1452},{"type":254,"tag":622,"props":5543,"children":5545},{"class":624,"line":5544},67,[5546,5550,5554],{"type":254,"tag":622,"props":5547,"children":5548},{"style":629},[5549],{"type":259,"value":1460},{"type":254,"tag":622,"props":5551,"children":5552},{"style":714},[5553],{"type":259,"value":1465},{"type":254,"tag":622,"props":5555,"children":5556},{"style":629},[5557],{"type":259,"value":676},{"type":254,"tag":622,"props":5559,"children":5561},{"class":624,"line":5560},68,[5562,5566,5570],{"type":254,"tag":622,"props":5563,"children":5564},{"style":629},[5565],{"type":259,"value":1477},{"type":254,"tag":622,"props":5567,"children":5568},{"style":714},[5569],{"type":259,"value":1482},{"type":254,"tag":622,"props":5571,"children":5572},{"style":629},[5573],{"type":259,"value":676},{"type":254,"tag":622,"props":5575,"children":5577},{"class":624,"line":5576},69,[5578,5582,5586],{"type":254,"tag":622,"props":5579,"children":5580},{"style":629},[5581],{"type":259,"value":1460},{"type":254,"tag":622,"props":5583,"children":5584},{"style":714},[5585],{"type":259,"value":1498},{"type":254,"tag":622,"props":5587,"children":5588},{"style":629},[5589],{"type":259,"value":676},{"type":254,"tag":622,"props":5591,"children":5593},{"class":624,"line":5592},70,[5594,5598,5602],{"type":254,"tag":622,"props":5595,"children":5596},{"style":629},[5597],{"type":259,"value":1460},{"type":254,"tag":622,"props":5599,"children":5600},{"style":714},[5601],{"type":259,"value":1514},{"type":254,"tag":622,"props":5603,"children":5604},{"style":629},[5605],{"type":259,"value":676},{"type":254,"tag":622,"props":5607,"children":5609},{"class":624,"line":5608},71,[5610,5614,5618],{"type":254,"tag":622,"props":5611,"children":5612},{"style":629},[5613],{"type":259,"value":1460},{"type":254,"tag":622,"props":5615,"children":5616},{"style":714},[5617],{"type":259,"value":1530},{"type":254,"tag":622,"props":5619,"children":5620},{"style":629},[5621],{"type":259,"value":676},{"type":254,"tag":622,"props":5623,"children":5625},{"class":624,"line":5624},72,[5626,5630],{"type":254,"tag":622,"props":5627,"children":5628},{"style":629},[5629],{"type":259,"value":1460},{"type":254,"tag":622,"props":5631,"children":5632},{"style":714},[5633],{"type":259,"value":1546},{"type":254,"tag":622,"props":5635,"children":5637},{"class":624,"line":5636},73,[5638],{"type":254,"tag":622,"props":5639,"children":5640},{"style":629},[5641],{"type":259,"value":1554},{"type":254,"tag":622,"props":5643,"children":5645},{"class":624,"line":5644},74,[5646],{"type":254,"tag":622,"props":5647,"children":5648},{"emptyLinePlaceholder":1699},[5649],{"type":259,"value":1702},{"type":254,"tag":622,"props":5651,"children":5653},{"class":624,"line":5652},75,[5654,5658,5662,5666],{"type":254,"tag":622,"props":5655,"children":5656},{"style":644},[5657],{"type":259,"value":1437},{"type":254,"tag":622,"props":5659,"children":5660},{"style":714},[5661],{"type":259,"value":1442},{"type":254,"tag":622,"props":5663,"children":5664},{"style":714},[5665],{"type":259,"value":1818},{"type":254,"tag":622,"props":5667,"children":5668},{"style":629},[5669],{"type":259,"value":1452},{"type":254,"tag":622,"props":5671,"children":5673},{"class":624,"line":5672},76,[5674,5678,5682],{"type":254,"tag":622,"props":5675,"children":5676},{"style":629},[5677],{"type":259,"value":1460},{"type":254,"tag":622,"props":5679,"children":5680},{"style":714},[5681],{"type":259,"value":1465},{"type":254,"tag":622,"props":5683,"children":5684},{"style":629},[5685],{"type":259,"value":676},{"type":254,"tag":622,"props":5687,"children":5689},{"class":624,"line":5688},77,[5690,5694,5698],{"type":254,"tag":622,"props":5691,"children":5692},{"style":629},[5693],{"type":259,"value":1460},{"type":254,"tag":622,"props":5695,"children":5696},{"style":714},[5697],{"type":259,"value":1849},{"type":254,"tag":622,"props":5699,"children":5700},{"style":629},[5701],{"type":259,"value":676},{"type":254,"tag":622,"props":5703,"children":5705},{"class":624,"line":5704},78,[5706,5710,5714],{"type":254,"tag":622,"props":5707,"children":5708},{"style":714},[5709],{"type":259,"value":1861},{"type":254,"tag":622,"props":5711,"children":5712},{"style":714},[5713],{"type":259,"value":1866},{"type":254,"tag":622,"props":5715,"children":5716},{"style":629},[5717],{"type":259,"value":676},{"type":254,"tag":622,"props":5719,"children":5721},{"class":624,"line":5720},79,[5722,5726,5730],{"type":254,"tag":622,"props":5723,"children":5724},{"style":714},[5725],{"type":259,"value":1861},{"type":254,"tag":622,"props":5727,"children":5728},{"style":714},[5729],{"type":259,"value":1882},{"type":254,"tag":622,"props":5731,"children":5732},{"style":629},[5733],{"type":259,"value":676},{"type":254,"tag":622,"props":5735,"children":5737},{"class":624,"line":5736},80,[5738,5742,5746],{"type":254,"tag":622,"props":5739,"children":5740},{"style":714},[5741],{"type":259,"value":1894},{"type":254,"tag":622,"props":5743,"children":5744},{"style":714},[5745],{"type":259,"value":1899},{"type":254,"tag":622,"props":5747,"children":5748},{"style":629},[5749],{"type":259,"value":676},{"type":254,"tag":622,"props":5751,"children":5753},{"class":624,"line":5752},81,[5754,5758,5762],{"type":254,"tag":622,"props":5755,"children":5756},{"style":714},[5757],{"type":259,"value":1911},{"type":254,"tag":622,"props":5759,"children":5760},{"style":714},[5761],{"type":259,"value":1916},{"type":254,"tag":622,"props":5763,"children":5764},{"style":629},[5765],{"type":259,"value":676},{"type":254,"tag":622,"props":5767,"children":5769},{"class":624,"line":5768},82,[5770,5774,5778],{"type":254,"tag":622,"props":5771,"children":5772},{"style":629},[5773],{"type":259,"value":1460},{"type":254,"tag":622,"props":5775,"children":5776},{"style":714},[5777],{"type":259,"value":1932},{"type":254,"tag":622,"props":5779,"children":5780},{"style":629},[5781],{"type":259,"value":676},{"type":254,"tag":622,"props":5783,"children":5785},{"class":624,"line":5784},83,[5786,5790],{"type":254,"tag":622,"props":5787,"children":5788},{"style":629},[5789],{"type":259,"value":1460},{"type":254,"tag":622,"props":5791,"children":5792},{"style":714},[5793],{"type":259,"value":1948},{"type":254,"tag":622,"props":5795,"children":5797},{"class":624,"line":5796},84,[5798],{"type":254,"tag":622,"props":5799,"children":5800},{"style":629},[5801],{"type":259,"value":1685},{"type":254,"tag":622,"props":5803,"children":5805},{"class":624,"line":5804},85,[5806],{"type":254,"tag":622,"props":5807,"children":5808},{"style":629},[5809],{"type":259,"value":632},{"type":254,"tag":622,"props":5811,"children":5813},{"class":624,"line":5812},86,[5814,5818,5822,5826,5830,5834,5838,5842,5846,5850],{"type":254,"tag":622,"props":5815,"children":5816},{"style":644},[5817],{"type":259,"value":1970},{"type":254,"tag":622,"props":5819,"children":5820},{"style":629},[5821],{"type":259,"value":1975},{"type":254,"tag":622,"props":5823,"children":5824},{"style":629},[5825],{"type":259,"value":1980},{"type":254,"tag":622,"props":5827,"children":5828},{"style":714},[5829],{"type":259,"value":1985},{"type":254,"tag":622,"props":5831,"children":5832},{"style":629},[5833],{"type":259,"value":1990},{"type":254,"tag":622,"props":5835,"children":5836},{"style":714},[5837],{"type":259,"value":1995},{"type":254,"tag":622,"props":5839,"children":5840},{"style":629},[5841],{"type":259,"value":2000},{"type":254,"tag":622,"props":5843,"children":5844},{"style":714},[5845],{"type":259,"value":2005},{"type":254,"tag":622,"props":5847,"children":5848},{"style":629},[5849],{"type":259,"value":2000},{"type":254,"tag":622,"props":5851,"children":5852},{"style":629},[5853],{"type":259,"value":2014},{"type":254,"tag":622,"props":5855,"children":5857},{"class":624,"line":5856},87,[5858],{"type":254,"tag":622,"props":5859,"children":5860},{"style":629},[5861],{"type":259,"value":1693},{"type":254,"tag":622,"props":5863,"children":5865},{"class":624,"line":5864},88,[5866],{"type":254,"tag":622,"props":5867,"children":5868},{"emptyLinePlaceholder":1699},[5869],{"type":259,"value":1702},{"type":254,"tag":622,"props":5871,"children":5873},{"class":624,"line":5872},89,[5874,5878,5883],{"type":254,"tag":622,"props":5875,"children":5876},{"style":644},[5877],{"type":259,"value":1437},{"type":254,"tag":622,"props":5879,"children":5880},{"style":714},[5881],{"type":259,"value":5882}," enum",{"type":254,"tag":622,"props":5884,"children":5885},{"style":714},[5886],{"type":259,"value":5887}," EventFormat\n",{"type":254,"tag":622,"props":5889,"children":5891},{"class":624,"line":5890},90,[5892],{"type":254,"tag":622,"props":5893,"children":5894},{"style":629},[5895],{"type":259,"value":632},{"type":254,"tag":622,"props":5897,"children":5899},{"class":624,"line":5898},91,[5900,5905,5909,5914],{"type":254,"tag":622,"props":5901,"children":5902},{"style":714},[5903],{"type":259,"value":5904},"    Unknown",{"type":254,"tag":622,"props":5906,"children":5907},{"style":629},[5908],{"type":259,"value":1619},{"type":254,"tag":622,"props":5910,"children":5911},{"style":741},[5912],{"type":259,"value":5913}," 1",{"type":254,"tag":622,"props":5915,"children":5916},{"style":629},[5917],{"type":259,"value":676},{"type":254,"tag":622,"props":5919,"children":5921},{"class":624,"line":5920},92,[5922,5927,5931,5936],{"type":254,"tag":622,"props":5923,"children":5924},{"style":714},[5925],{"type":259,"value":5926},"    Virtual",{"type":254,"tag":622,"props":5928,"children":5929},{"style":629},[5930],{"type":259,"value":1619},{"type":254,"tag":622,"props":5932,"children":5933},{"style":741},[5934],{"type":259,"value":5935}," 2",{"type":254,"tag":622,"props":5937,"children":5938},{"style":629},[5939],{"type":259,"value":676},{"type":254,"tag":622,"props":5941,"children":5943},{"class":624,"line":5942},93,[5944,5949,5953,5958],{"type":254,"tag":622,"props":5945,"children":5946},{"style":714},[5947],{"type":259,"value":5948},"    In_Person",{"type":254,"tag":622,"props":5950,"children":5951},{"style":629},[5952],{"type":259,"value":1619},{"type":254,"tag":622,"props":5954,"children":5955},{"style":741},[5956],{"type":259,"value":5957}," 3",{"type":254,"tag":622,"props":5959,"children":5960},{"style":629},[5961],{"type":259,"value":676},{"type":254,"tag":622,"props":5963,"children":5965},{"class":624,"line":5964},94,[5966,5971,5975],{"type":254,"tag":622,"props":5967,"children":5968},{"style":714},[5969],{"type":259,"value":5970},"    Hybrid",{"type":254,"tag":622,"props":5972,"children":5973},{"style":629},[5974],{"type":259,"value":1619},{"type":254,"tag":622,"props":5976,"children":5977},{"style":741},[5978],{"type":259,"value":5979}," 4\n",{"type":254,"tag":622,"props":5981,"children":5983},{"class":624,"line":5982},95,[5984],{"type":254,"tag":622,"props":5985,"children":5986},{"style":629},[5987],{"type":259,"value":1111},{"type":254,"tag":622,"props":5989,"children":5991},{"class":624,"line":5990},96,[5992],{"type":254,"tag":622,"props":5993,"children":5994},{"emptyLinePlaceholder":1699},[5995],{"type":259,"value":1702},{"type":254,"tag":622,"props":5997,"children":5999},{"class":624,"line":5998},97,[6000,6004,6008],{"type":254,"tag":622,"props":6001,"children":6002},{"style":644},[6003],{"type":259,"value":1437},{"type":254,"tag":622,"props":6005,"children":6006},{"style":714},[6007],{"type":259,"value":5882},{"type":254,"tag":622,"props":6009,"children":6010},{"style":714},[6011],{"type":259,"value":6012}," EventType\n",{"type":254,"tag":622,"props":6014,"children":6016},{"class":624,"line":6015},98,[6017],{"type":254,"tag":622,"props":6018,"children":6019},{"style":629},[6020],{"type":259,"value":632},{"type":254,"tag":622,"props":6022,"children":6024},{"class":624,"line":6023},99,[6025,6030,6034,6039],{"type":254,"tag":622,"props":6026,"children":6027},{"style":714},[6028],{"type":259,"value":6029},"    Any",{"type":254,"tag":622,"props":6031,"children":6032},{"style":629},[6033],{"type":259,"value":1619},{"type":254,"tag":622,"props":6035,"children":6036},{"style":741},[6037],{"type":259,"value":6038}," 0",{"type":254,"tag":622,"props":6040,"children":6041},{"style":629},[6042],{"type":259,"value":676},{"type":254,"tag":622,"props":6044,"children":6046},{"class":624,"line":6045},100,[6047,6051,6055,6059],{"type":254,"tag":622,"props":6048,"children":6049},{"style":714},[6050],{"type":259,"value":5904},{"type":254,"tag":622,"props":6052,"children":6053},{"style":629},[6054],{"type":259,"value":1619},{"type":254,"tag":622,"props":6056,"children":6057},{"style":741},[6058],{"type":259,"value":5913},{"type":254,"tag":622,"props":6060,"children":6061},{"style":629},[6062],{"type":259,"value":676},{"type":254,"tag":622,"props":6064,"children":6066},{"class":624,"line":6065},101,[6067,6072,6076,6080],{"type":254,"tag":622,"props":6068,"children":6069},{"style":714},[6070],{"type":259,"value":6071},"    Conference",{"type":254,"tag":622,"props":6073,"children":6074},{"style":629},[6075],{"type":259,"value":1619},{"type":254,"tag":622,"props":6077,"children":6078},{"style":741},[6079],{"type":259,"value":5935},{"type":254,"tag":622,"props":6081,"children":6082},{"style":629},[6083],{"type":259,"value":676},{"type":254,"tag":622,"props":6085,"children":6087},{"class":624,"line":6086},102,[6088,6093,6097,6101],{"type":254,"tag":622,"props":6089,"children":6090},{"style":714},[6091],{"type":259,"value":6092},"    Meetup",{"type":254,"tag":622,"props":6094,"children":6095},{"style":629},[6096],{"type":259,"value":1619},{"type":254,"tag":622,"props":6098,"children":6099},{"style":741},[6100],{"type":259,"value":5957},{"type":254,"tag":622,"props":6102,"children":6103},{"style":629},[6104],{"type":259,"value":676},{"type":254,"tag":622,"props":6106,"children":6108},{"class":624,"line":6107},103,[6109,6114,6118,6123],{"type":254,"tag":622,"props":6110,"children":6111},{"style":714},[6112],{"type":259,"value":6113},"    Hackathon",{"type":254,"tag":622,"props":6115,"children":6116},{"style":629},[6117],{"type":259,"value":1619},{"type":254,"tag":622,"props":6119,"children":6120},{"style":741},[6121],{"type":259,"value":6122}," 4",{"type":254,"tag":622,"props":6124,"children":6125},{"style":629},[6126],{"type":259,"value":676},{"type":254,"tag":622,"props":6128,"children":6130},{"class":624,"line":6129},104,[6131,6136,6140,6145],{"type":254,"tag":622,"props":6132,"children":6133},{"style":714},[6134],{"type":259,"value":6135},"    Call_For_Papers",{"type":254,"tag":622,"props":6137,"children":6138},{"style":629},[6139],{"type":259,"value":1619},{"type":254,"tag":622,"props":6141,"children":6142},{"style":741},[6143],{"type":259,"value":6144}," 5",{"type":254,"tag":622,"props":6146,"children":6147},{"style":629},[6148],{"type":259,"value":676},{"type":254,"tag":622,"props":6150,"children":6152},{"class":624,"line":6151},105,[6153,6158,6162,6167],{"type":254,"tag":622,"props":6154,"children":6155},{"style":714},[6156],{"type":259,"value":6157},"    Website",{"type":254,"tag":622,"props":6159,"children":6160},{"style":629},[6161],{"type":259,"value":1619},{"type":254,"tag":622,"props":6163,"children":6164},{"style":741},[6165],{"type":259,"value":6166}," 6",{"type":254,"tag":622,"props":6168,"children":6169},{"style":629},[6170],{"type":259,"value":676},{"type":254,"tag":622,"props":6172,"children":6174},{"class":624,"line":6173},106,[6175],{"type":254,"tag":622,"props":6176,"children":6177},{"style":629},[6178],{"type":259,"value":1111},{"type":254,"tag":255,"props":6180,"children":6181},{},[6182],{"type":259,"value":6183},"Keep it mind that it's a quick experiment to automate the submission of developer conferences to the Tech Community Calendar, not production-ready code.",{"type":254,"tag":282,"props":6185,"children":6187},{"id":6186},"final-thoughts",[6188],{"type":259,"value":6189},"Final Thoughts",{"type":254,"tag":255,"props":6191,"children":6192},{},[6193],{"type":259,"value":6194},"I think this PoC is a good starting point to create a scheduled process that automatically submit the events from the developers conferences agenda to the tech community calendar.",{"type":254,"tag":427,"props":6196,"children":6197},{"icon":567},[6198],{"type":254,"tag":255,"props":6199,"children":6200},{},[6201],{"type":259,"value":6202},"Of course it would be great to do the opposite as well (automatically import events from the tech community calendar to the developers conferences agenda) but it seems complicated as events in the tech community calendar are stored in a database I don't have access to and it would involved parsing and writing in the README file of the developers conferences agenda repository.",{"type":254,"tag":255,"props":6204,"children":6205},{},[6206],{"type":259,"value":6207},"Some ideas for improvement:",{"type":254,"tag":332,"props":6209,"children":6210},{},[6211,6216,6221],{"type":254,"tag":336,"props":6212,"children":6213},{},[6214],{"type":259,"value":6215},"store somewhere the events already submitted to only process new events on each run",{"type":254,"tag":336,"props":6217,"children":6218},{},[6219],{"type":259,"value":6220},"parallelize the processing of events as retrieving the twitter URL of submitting an event can take some time",{"type":254,"tag":336,"props":6222,"children":6223},{},[6224],{"type":259,"value":6225},"reorganize the code",{"type":254,"tag":255,"props":6227,"children":6228},{},[6229],{"type":259,"value":6230},"It was the first time I used AngleSharp, and I was happy with the result. It's a nice library that I would use again for similar needs.",{"type":254,"tag":255,"props":6232,"children":6233},{},[6234],{"type":259,"value":6235},"A big thank you to the contributors of these IT event calendars. As someone who tries to attend tech events and speak at developer conferences, I find them incredibly useful. A special shoutout to Aurélie Vache and her developer conferences agenda for making this data openly available (JSON files with CFPs and conferences publicly accessible).",{"type":254,"tag":1260,"props":6237,"children":6238},{},[6239],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":6241},[6242,6243,6244,6245,6246,6247,6248,6249],{"id":1299,"depth":635,"text":1302},{"id":1347,"depth":635,"text":1350},{"id":1399,"depth":635,"text":1402},{"id":1773,"depth":635,"text":1776},{"id":2801,"depth":635,"text":2804},{"id":3305,"depth":635,"text":3308},{"id":3653,"depth":635,"text":3656},{"id":6186,"depth":635,"text":6189},"content:1.posts:60.it-event-calendars.md","1.posts/60.it-event-calendars.md",{"_path":184,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":183,"description":6253,"lead":6254,"date":6255,"image":6256,"badge":6258,"tags":6259,"ImageAttribution":6262,"body":6263,"_type":1275,"_id":9252,"_source":231,"_file":9253,"_extension":1278},"I love how the Azure SDKs have evolved over the years. In the past, there was no consistency between the various Azure SDKs. However, that's not longer the case (at least for most Azure libraries), as they now adhere to the same principles and follow a set of well-defined guidelines.","Register your Azure clients the proper way","2024-02-19T00:00:00.000Z",{"src":6257},"/images/apple-di.webp",{"label":1286},[6260,6261,1288],"Azure","Azure SDK","Picture of \u003Ca href=\"https://unsplash.com/fr/@sarabakhshi\">Sara Bakhshi\u003C/a> on \u003Ca href=\"https://unsplash.com/fr/photos/pomme-fruit-avec-des-seringues-en-plastique-MfnX4XtGnvU\">Unsplash\u003C/a>",{"type":251,"children":6264,"toc":9244},[6265,6278,6295,6300,6318,6323,6329,6343,6348,6361,6723,6743,7159,7197,7203,7217,7497,7502,7527,7533,7545,7814,7827,8252,8263,8277,8283,8296,8301,8543,8548,8718,8724,8746,8790,9203,9215,9219,9235,9240],{"type":254,"tag":255,"props":6266,"children":6267},{},[6268,6270,6277],{"type":259,"value":6269},"I love how the Azure SDKs have evolved over the years. In the past, there was no consistency between the various Azure SDKs. However, that's not longer the case (at least for most Azure libraries), as they now adhere to the same principles and follow a set of well-defined ",{"type":254,"tag":262,"props":6271,"children":6274},{"href":6272,"rel":6273},"https://azure.github.io/azure-sdk/general_introduction.html",[266],[6275],{"type":259,"value":6276},"guidelines",{"type":259,"value":594},{"type":254,"tag":427,"props":6279,"children":6280},{"icon":567},[6281],{"type":254,"tag":255,"props":6282,"children":6283},{},[6284,6286,6293],{"type":259,"value":6285},"You can learn more about these guidelines and how the Azure .NET SDKs work in this ",{"type":254,"tag":262,"props":6287,"children":6290},{"href":6288,"rel":6289},"https://youtu.be/v36NXLU3TLY?si=L8e1ic898kDCisJ7",[266],[6291],{"type":259,"value":6292},"video",{"type":259,"value":6294}," from 2021 which is I think still relevant today.",{"type":254,"tag":255,"props":6296,"children":6297},{},[6298],{"type":259,"value":6299},"Having consistency between libraries, it's easier to handle things like authentication and dependency injection consistently when you are using multiple Azure SDKs in your project.",{"type":254,"tag":255,"props":6301,"children":6302},{},[6303,6305,6316],{"type":259,"value":6304},"One aspect often overlooked by people using Azure SDKs is the use of ",{"type":254,"tag":262,"props":6306,"children":6309},{"href":6307,"rel":6308},"https://www.nuget.org/packages/Microsoft.Extensions.Azure",[266],[6310],{"type":254,"tag":389,"props":6311,"children":6313},{"className":6312},[],[6314],{"type":259,"value":6315},"Microsoft.Extensions.Azure",{"type":259,"value":6317},". This package facilitates registering and configuring the service clients for interacting with Azure APIs.",{"type":254,"tag":255,"props":6319,"children":6320},{},[6321],{"type":259,"value":6322},"Let's see why using this package could be beneficial for your project.",{"type":254,"tag":282,"props":6324,"children":6326},{"id":6325},"avoid-making-mistakes-when-registering-service-clients",[6327],{"type":259,"value":6328},"Avoid making mistakes when registering service clients",{"type":254,"tag":255,"props":6330,"children":6331},{},[6332,6334,6341],{"type":259,"value":6333},"It's mentioned in the ",{"type":254,"tag":262,"props":6335,"children":6338},{"href":6336,"rel":6337},"https://learn.microsoft.com/en-us/dotnet/azure/sdk/dependency-injection?view=azure-dotnet&tabs=web-app-builder",[266],[6339],{"type":259,"value":6340},"documentation",{"type":259,"value":6342}," to use this package for dependency injection with the Azure SDK for .NET. Still, many people don't read the documentation and manually register the Azure service clients.",{"type":254,"tag":255,"props":6344,"children":6345},{},[6346],{"type":259,"value":6347},"It's not a problem in itself if you know what you are doing. Otherwise,",{"type":254,"tag":332,"props":6349,"children":6350},{},[6351,6356],{"type":254,"tag":336,"props":6352,"children":6353},{},[6354],{"type":259,"value":6355},"you might choose the wrong lifetime for the Azure service clients, they must be singleton",{"type":254,"tag":336,"props":6357,"children":6358},{},[6359],{"type":259,"value":6360},"you may forget to register a dependency that is needed for your use of the SDK",{"type":254,"tag":612,"props":6362,"children":6364},{"className":1423,"code":6363,"language":1425,"meta":205,"style":205},"using Azure.Identity;\nusing DIWithAzureSDK;\nusing Microsoft.Extensions.Azure;\n\nvar builder = Host.CreateApplicationBuilder(args);\nbuilder.Services.AddHostedService\u003CWorker>();\nbuilder.Services.AddAzureClients(clientBuilder =>\n{\n    clientBuilder.AddBlobServiceClient(new Uri(\"https://stdiwithazuresdk.blob.core.windows.net/\"));\n    clientBuilder.UseCredential(new DefaultAzureCredential());\n});\n\nvar host = builder.Build();\nhost.Run();\n",[6365],{"type":254,"tag":389,"props":6366,"children":6367},{"__ignoreMap":205},[6368,6393,6409,6442,6449,6492,6532,6570,6577,6625,6654,6662,6669,6702],{"type":254,"tag":622,"props":6369,"children":6370},{"class":624,"line":625},[6371,6375,6380,6384,6389],{"type":254,"tag":622,"props":6372,"children":6373},{"style":741},[6374],{"type":259,"value":1604},{"type":254,"tag":622,"props":6376,"children":6377},{"style":1202},[6378],{"type":259,"value":6379}," Azure",{"type":254,"tag":622,"props":6381,"children":6382},{"style":629},[6383],{"type":259,"value":594},{"type":254,"tag":622,"props":6385,"children":6386},{"style":1202},[6387],{"type":259,"value":6388},"Identity",{"type":254,"tag":622,"props":6390,"children":6391},{"style":629},[6392],{"type":259,"value":2350},{"type":254,"tag":622,"props":6394,"children":6395},{"class":624,"line":635},[6396,6400,6405],{"type":254,"tag":622,"props":6397,"children":6398},{"style":741},[6399],{"type":259,"value":1604},{"type":254,"tag":622,"props":6401,"children":6402},{"style":1202},[6403],{"type":259,"value":6404}," DIWithAzureSDK",{"type":254,"tag":622,"props":6406,"children":6407},{"style":629},[6408],{"type":259,"value":2350},{"type":254,"tag":622,"props":6410,"children":6411},{"class":624,"line":679},[6412,6416,6421,6425,6430,6434,6438],{"type":254,"tag":622,"props":6413,"children":6414},{"style":741},[6415],{"type":259,"value":1604},{"type":254,"tag":622,"props":6417,"children":6418},{"style":1202},[6419],{"type":259,"value":6420}," Microsoft",{"type":254,"tag":622,"props":6422,"children":6423},{"style":629},[6424],{"type":259,"value":594},{"type":254,"tag":622,"props":6426,"children":6427},{"style":1202},[6428],{"type":259,"value":6429},"Extensions",{"type":254,"tag":622,"props":6431,"children":6432},{"style":629},[6433],{"type":259,"value":594},{"type":254,"tag":622,"props":6435,"children":6436},{"style":1202},[6437],{"type":259,"value":6260},{"type":254,"tag":622,"props":6439,"children":6440},{"style":629},[6441],{"type":259,"value":2350},{"type":254,"tag":622,"props":6443,"children":6444},{"class":624,"line":705},[6445],{"type":254,"tag":622,"props":6446,"children":6447},{"emptyLinePlaceholder":1699},[6448],{"type":259,"value":1702},{"type":254,"tag":622,"props":6450,"children":6451},{"class":624,"line":732},[6452,6456,6461,6465,6470,6474,6479,6483,6488],{"type":254,"tag":622,"props":6453,"children":6454},{"style":714},[6455],{"type":259,"value":1710},{"type":254,"tag":622,"props":6457,"children":6458},{"style":714},[6459],{"type":259,"value":6460}," builder",{"type":254,"tag":622,"props":6462,"children":6463},{"style":629},[6464],{"type":259,"value":1619},{"type":254,"tag":622,"props":6466,"children":6467},{"style":1202},[6468],{"type":259,"value":6469}," Host",{"type":254,"tag":622,"props":6471,"children":6472},{"style":629},[6473],{"type":259,"value":594},{"type":254,"tag":622,"props":6475,"children":6476},{"style":1735},[6477],{"type":259,"value":6478},"CreateApplicationBuilder",{"type":254,"tag":622,"props":6480,"children":6481},{"style":629},[6482],{"type":259,"value":1668},{"type":254,"tag":622,"props":6484,"children":6485},{"style":1202},[6486],{"type":259,"value":6487},"args",{"type":254,"tag":622,"props":6489,"children":6490},{"style":629},[6491],{"type":259,"value":1554},{"type":254,"tag":622,"props":6493,"children":6494},{"class":624,"line":759},[6495,6500,6504,6509,6513,6518,6522,6527],{"type":254,"tag":622,"props":6496,"children":6497},{"style":1202},[6498],{"type":259,"value":6499},"builder",{"type":254,"tag":622,"props":6501,"children":6502},{"style":629},[6503],{"type":259,"value":594},{"type":254,"tag":622,"props":6505,"children":6506},{"style":1202},[6507],{"type":259,"value":6508},"Services",{"type":254,"tag":622,"props":6510,"children":6511},{"style":629},[6512],{"type":259,"value":594},{"type":254,"tag":622,"props":6514,"children":6515},{"style":1735},[6516],{"type":259,"value":6517},"AddHostedService",{"type":254,"tag":622,"props":6519,"children":6520},{"style":629},[6521],{"type":259,"value":1743},{"type":254,"tag":622,"props":6523,"children":6524},{"style":714},[6525],{"type":259,"value":6526},"Worker",{"type":254,"tag":622,"props":6528,"children":6529},{"style":629},[6530],{"type":259,"value":6531},">();\n",{"type":254,"tag":622,"props":6533,"children":6534},{"class":624,"line":798},[6535,6539,6543,6547,6551,6556,6560,6565],{"type":254,"tag":622,"props":6536,"children":6537},{"style":1202},[6538],{"type":259,"value":6499},{"type":254,"tag":622,"props":6540,"children":6541},{"style":629},[6542],{"type":259,"value":594},{"type":254,"tag":622,"props":6544,"children":6545},{"style":1202},[6546],{"type":259,"value":6508},{"type":254,"tag":622,"props":6548,"children":6549},{"style":629},[6550],{"type":259,"value":594},{"type":254,"tag":622,"props":6552,"children":6553},{"style":1735},[6554],{"type":259,"value":6555},"AddAzureClients",{"type":254,"tag":622,"props":6557,"children":6558},{"style":629},[6559],{"type":259,"value":1668},{"type":254,"tag":622,"props":6561,"children":6562},{"style":714},[6563],{"type":259,"value":6564},"clientBuilder",{"type":254,"tag":622,"props":6566,"children":6567},{"style":629},[6568],{"type":259,"value":6569}," =>\n",{"type":254,"tag":622,"props":6571,"children":6572},{"class":624,"line":836},[6573],{"type":254,"tag":622,"props":6574,"children":6575},{"style":629},[6576],{"type":259,"value":632},{"type":254,"tag":622,"props":6578,"children":6579},{"class":624,"line":862},[6580,6585,6589,6594,6599,6603,6607,6611,6616,6620],{"type":254,"tag":622,"props":6581,"children":6582},{"style":1202},[6583],{"type":259,"value":6584},"    clientBuilder",{"type":254,"tag":622,"props":6586,"children":6587},{"style":629},[6588],{"type":259,"value":594},{"type":254,"tag":622,"props":6590,"children":6591},{"style":1735},[6592],{"type":259,"value":6593},"AddBlobServiceClient",{"type":254,"tag":622,"props":6595,"children":6596},{"style":629},[6597],{"type":259,"value":6598},"(new",{"type":254,"tag":622,"props":6600,"children":6601},{"style":714},[6602],{"type":259,"value":1663},{"type":254,"tag":622,"props":6604,"children":6605},{"style":629},[6606],{"type":259,"value":1668},{"type":254,"tag":622,"props":6608,"children":6609},{"style":629},[6610],{"type":259,"value":652},{"type":254,"tag":622,"props":6612,"children":6613},{"style":664},[6614],{"type":259,"value":6615},"https://stdiwithazuresdk.blob.core.windows.net/",{"type":254,"tag":622,"props":6617,"children":6618},{"style":629},[6619],{"type":259,"value":652},{"type":254,"tag":622,"props":6621,"children":6622},{"style":629},[6623],{"type":259,"value":6624},"));\n",{"type":254,"tag":622,"props":6626,"children":6627},{"class":624,"line":900},[6628,6632,6636,6641,6645,6650],{"type":254,"tag":622,"props":6629,"children":6630},{"style":1202},[6631],{"type":259,"value":6584},{"type":254,"tag":622,"props":6633,"children":6634},{"style":629},[6635],{"type":259,"value":594},{"type":254,"tag":622,"props":6637,"children":6638},{"style":1735},[6639],{"type":259,"value":6640},"UseCredential",{"type":254,"tag":622,"props":6642,"children":6643},{"style":629},[6644],{"type":259,"value":6598},{"type":254,"tag":622,"props":6646,"children":6647},{"style":714},[6648],{"type":259,"value":6649}," DefaultAzureCredential",{"type":254,"tag":622,"props":6651,"children":6652},{"style":629},[6653],{"type":259,"value":2154},{"type":254,"tag":622,"props":6655,"children":6656},{"class":624,"line":938},[6657],{"type":254,"tag":622,"props":6658,"children":6659},{"style":629},[6660],{"type":259,"value":6661},"});\n",{"type":254,"tag":622,"props":6663,"children":6664},{"class":624,"line":976},[6665],{"type":254,"tag":622,"props":6666,"children":6667},{"emptyLinePlaceholder":1699},[6668],{"type":259,"value":1702},{"type":254,"tag":622,"props":6670,"children":6671},{"class":624,"line":1014},[6672,6676,6681,6685,6689,6693,6698],{"type":254,"tag":622,"props":6673,"children":6674},{"style":714},[6675],{"type":259,"value":1710},{"type":254,"tag":622,"props":6677,"children":6678},{"style":714},[6679],{"type":259,"value":6680}," host",{"type":254,"tag":622,"props":6682,"children":6683},{"style":629},[6684],{"type":259,"value":1619},{"type":254,"tag":622,"props":6686,"children":6687},{"style":1202},[6688],{"type":259,"value":6460},{"type":254,"tag":622,"props":6690,"children":6691},{"style":629},[6692],{"type":259,"value":594},{"type":254,"tag":622,"props":6694,"children":6695},{"style":1735},[6696],{"type":259,"value":6697},"Build",{"type":254,"tag":622,"props":6699,"children":6700},{"style":629},[6701],{"type":259,"value":2798},{"type":254,"tag":622,"props":6703,"children":6704},{"class":624,"line":1052},[6705,6710,6714,6719],{"type":254,"tag":622,"props":6706,"children":6707},{"style":1202},[6708],{"type":259,"value":6709},"host",{"type":254,"tag":622,"props":6711,"children":6712},{"style":629},[6713],{"type":259,"value":594},{"type":254,"tag":622,"props":6715,"children":6716},{"style":1735},[6717],{"type":259,"value":6718},"Run",{"type":254,"tag":622,"props":6720,"children":6721},{"style":629},[6722],{"type":259,"value":2798},{"type":254,"tag":255,"props":6724,"children":6725},{},[6726,6728,6733,6735,6741],{"type":259,"value":6727},"In this sample, the ",{"type":254,"tag":389,"props":6729,"children":6731},{"className":6730},[],[6732],{"type":259,"value":6593},{"type":259,"value":6734}," handles the registration of all dependencies for us so that the ",{"type":254,"tag":389,"props":6736,"children":6738},{"className":6737},[],[6739],{"type":259,"value":6740},"BlobServiceClient",{"type":259,"value":6742}," can then be injected directly where needed.",{"type":254,"tag":612,"props":6744,"children":6746},{"className":1423,"code":6745,"language":1425,"meta":205,"style":205},"public class Worker : BackgroundService\n{\n    private readonly ILogger\u003CWorker> _logger;\n    private readonly BlobServiceClient _blobServiceClient;\n\n    public Worker(ILogger\u003CWorker> logger, BlobServiceClient blobServiceClient)\n    {\n        _logger = logger;\n        _blobServiceClient = blobServiceClient;\n    }\n\n    protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n    {\n        await foreach (var blobContainer in _blobServiceClient.GetBlobContainersAsync(cancellationToken: stoppingToken))\n        {\n            _logger.LogInformation(blobContainer.Name);\n        }\n    }\n}\n",[6747],{"type":254,"tag":389,"props":6748,"children":6749},{"__ignoreMap":205},[6750,6776,6783,6823,6848,6855,6909,6916,6936,6956,6963,6970,7015,7022,7087,7095,7137,7145,7152],{"type":254,"tag":622,"props":6751,"children":6752},{"class":624,"line":625},[6753,6757,6762,6767,6771],{"type":254,"tag":622,"props":6754,"children":6755},{"style":644},[6756],{"type":259,"value":1437},{"type":254,"tag":622,"props":6758,"children":6759},{"style":714},[6760],{"type":259,"value":6761}," class",{"type":254,"tag":622,"props":6763,"children":6764},{"style":714},[6765],{"type":259,"value":6766}," Worker",{"type":254,"tag":622,"props":6768,"children":6769},{"style":629},[6770],{"type":259,"value":3274},{"type":254,"tag":622,"props":6772,"children":6773},{"style":714},[6774],{"type":259,"value":6775}," BackgroundService\n",{"type":254,"tag":622,"props":6777,"children":6778},{"class":624,"line":635},[6779],{"type":254,"tag":622,"props":6780,"children":6781},{"style":629},[6782],{"type":259,"value":632},{"type":254,"tag":622,"props":6784,"children":6785},{"class":624,"line":679},[6786,6791,6796,6801,6805,6809,6814,6819],{"type":254,"tag":622,"props":6787,"children":6788},{"style":644},[6789],{"type":259,"value":6790},"    private",{"type":254,"tag":622,"props":6792,"children":6793},{"style":644},[6794],{"type":259,"value":6795}," readonly",{"type":254,"tag":622,"props":6797,"children":6798},{"style":714},[6799],{"type":259,"value":6800}," ILogger",{"type":254,"tag":622,"props":6802,"children":6803},{"style":629},[6804],{"type":259,"value":1743},{"type":254,"tag":622,"props":6806,"children":6807},{"style":714},[6808],{"type":259,"value":6526},{"type":254,"tag":622,"props":6810,"children":6811},{"style":629},[6812],{"type":259,"value":6813},">",{"type":254,"tag":622,"props":6815,"children":6816},{"style":714},[6817],{"type":259,"value":6818}," _logger",{"type":254,"tag":622,"props":6820,"children":6821},{"style":629},[6822],{"type":259,"value":2350},{"type":254,"tag":622,"props":6824,"children":6825},{"class":624,"line":705},[6826,6830,6834,6839,6844],{"type":254,"tag":622,"props":6827,"children":6828},{"style":644},[6829],{"type":259,"value":6790},{"type":254,"tag":622,"props":6831,"children":6832},{"style":644},[6833],{"type":259,"value":6795},{"type":254,"tag":622,"props":6835,"children":6836},{"style":714},[6837],{"type":259,"value":6838}," BlobServiceClient",{"type":254,"tag":622,"props":6840,"children":6841},{"style":714},[6842],{"type":259,"value":6843}," _blobServiceClient",{"type":254,"tag":622,"props":6845,"children":6846},{"style":629},[6847],{"type":259,"value":2350},{"type":254,"tag":622,"props":6849,"children":6850},{"class":624,"line":732},[6851],{"type":254,"tag":622,"props":6852,"children":6853},{"emptyLinePlaceholder":1699},[6854],{"type":259,"value":1702},{"type":254,"tag":622,"props":6856,"children":6857},{"class":624,"line":759},[6858,6862,6866,6870,6875,6879,6883,6887,6892,6896,6900,6905],{"type":254,"tag":622,"props":6859,"children":6860},{"style":644},[6861],{"type":259,"value":1970},{"type":254,"tag":622,"props":6863,"children":6864},{"style":1735},[6865],{"type":259,"value":6766},{"type":254,"tag":622,"props":6867,"children":6868},{"style":629},[6869],{"type":259,"value":1668},{"type":254,"tag":622,"props":6871,"children":6872},{"style":714},[6873],{"type":259,"value":6874},"ILogger",{"type":254,"tag":622,"props":6876,"children":6877},{"style":629},[6878],{"type":259,"value":1743},{"type":254,"tag":622,"props":6880,"children":6881},{"style":714},[6882],{"type":259,"value":6526},{"type":254,"tag":622,"props":6884,"children":6885},{"style":629},[6886],{"type":259,"value":6813},{"type":254,"tag":622,"props":6888,"children":6889},{"style":714},[6890],{"type":259,"value":6891}," logger",{"type":254,"tag":622,"props":6893,"children":6894},{"style":629},[6895],{"type":259,"value":3149},{"type":254,"tag":622,"props":6897,"children":6898},{"style":714},[6899],{"type":259,"value":6838},{"type":254,"tag":622,"props":6901,"children":6902},{"style":714},[6903],{"type":259,"value":6904}," blobServiceClient",{"type":254,"tag":622,"props":6906,"children":6907},{"style":629},[6908],{"type":259,"value":1685},{"type":254,"tag":622,"props":6910,"children":6911},{"class":624,"line":798},[6912],{"type":254,"tag":622,"props":6913,"children":6914},{"style":629},[6915],{"type":259,"value":3574},{"type":254,"tag":622,"props":6917,"children":6918},{"class":624,"line":836},[6919,6924,6928,6932],{"type":254,"tag":622,"props":6920,"children":6921},{"style":1202},[6922],{"type":259,"value":6923},"        _logger ",{"type":254,"tag":622,"props":6925,"children":6926},{"style":629},[6927],{"type":259,"value":1654},{"type":254,"tag":622,"props":6929,"children":6930},{"style":1202},[6931],{"type":259,"value":6891},{"type":254,"tag":622,"props":6933,"children":6934},{"style":629},[6935],{"type":259,"value":2350},{"type":254,"tag":622,"props":6937,"children":6938},{"class":624,"line":862},[6939,6944,6948,6952],{"type":254,"tag":622,"props":6940,"children":6941},{"style":1202},[6942],{"type":259,"value":6943},"        _blobServiceClient ",{"type":254,"tag":622,"props":6945,"children":6946},{"style":629},[6947],{"type":259,"value":1654},{"type":254,"tag":622,"props":6949,"children":6950},{"style":1202},[6951],{"type":259,"value":6904},{"type":254,"tag":622,"props":6953,"children":6954},{"style":629},[6955],{"type":259,"value":2350},{"type":254,"tag":622,"props":6957,"children":6958},{"class":624,"line":900},[6959],{"type":254,"tag":622,"props":6960,"children":6961},{"style":629},[6962],{"type":259,"value":1093},{"type":254,"tag":622,"props":6964,"children":6965},{"class":624,"line":938},[6966],{"type":254,"tag":622,"props":6967,"children":6968},{"emptyLinePlaceholder":1699},[6969],{"type":259,"value":1702},{"type":254,"tag":622,"props":6971,"children":6972},{"class":624,"line":976},[6973,6978,6983,6988,6992,6997,7001,7006,7011],{"type":254,"tag":622,"props":6974,"children":6975},{"style":644},[6976],{"type":259,"value":6977},"    protected",{"type":254,"tag":622,"props":6979,"children":6980},{"style":644},[6981],{"type":259,"value":6982}," override",{"type":254,"tag":622,"props":6984,"children":6985},{"style":644},[6986],{"type":259,"value":6987}," async",{"type":254,"tag":622,"props":6989,"children":6990},{"style":714},[6991],{"type":259,"value":2842},{"type":254,"tag":622,"props":6993,"children":6994},{"style":1735},[6995],{"type":259,"value":6996}," ExecuteAsync",{"type":254,"tag":622,"props":6998,"children":6999},{"style":629},[7000],{"type":259,"value":1668},{"type":254,"tag":622,"props":7002,"children":7003},{"style":714},[7004],{"type":259,"value":7005},"CancellationToken",{"type":254,"tag":622,"props":7007,"children":7008},{"style":714},[7009],{"type":259,"value":7010}," stoppingToken",{"type":254,"tag":622,"props":7012,"children":7013},{"style":629},[7014],{"type":259,"value":1685},{"type":254,"tag":622,"props":7016,"children":7017},{"class":624,"line":1014},[7018],{"type":254,"tag":622,"props":7019,"children":7020},{"style":629},[7021],{"type":259,"value":3574},{"type":254,"tag":622,"props":7023,"children":7024},{"class":624,"line":1052},[7025,7030,7035,7039,7043,7048,7052,7056,7060,7065,7069,7074,7078,7082],{"type":254,"tag":622,"props":7026,"children":7027},{"style":629},[7028],{"type":259,"value":7029},"        await",{"type":254,"tag":622,"props":7031,"children":7032},{"style":1196},[7033],{"type":259,"value":7034}," foreach",{"type":254,"tag":622,"props":7036,"children":7037},{"style":629},[7038],{"type":259,"value":3547},{"type":254,"tag":622,"props":7040,"children":7041},{"style":714},[7042],{"type":259,"value":1710},{"type":254,"tag":622,"props":7044,"children":7045},{"style":714},[7046],{"type":259,"value":7047}," blobContainer",{"type":254,"tag":622,"props":7049,"children":7050},{"style":1196},[7051],{"type":259,"value":4113},{"type":254,"tag":622,"props":7053,"children":7054},{"style":1202},[7055],{"type":259,"value":6843},{"type":254,"tag":622,"props":7057,"children":7058},{"style":629},[7059],{"type":259,"value":594},{"type":254,"tag":622,"props":7061,"children":7062},{"style":1735},[7063],{"type":259,"value":7064},"GetBlobContainersAsync",{"type":254,"tag":622,"props":7066,"children":7067},{"style":629},[7068],{"type":259,"value":1668},{"type":254,"tag":622,"props":7070,"children":7071},{"style":714},[7072],{"type":259,"value":7073},"cancellationToken",{"type":254,"tag":622,"props":7075,"children":7076},{"style":629},[7077],{"type":259,"value":474},{"type":254,"tag":622,"props":7079,"children":7080},{"style":1202},[7081],{"type":259,"value":7010},{"type":254,"tag":622,"props":7083,"children":7084},{"style":629},[7085],{"type":259,"value":7086},"))\n",{"type":254,"tag":622,"props":7088,"children":7089},{"class":624,"line":1078},[7090],{"type":254,"tag":622,"props":7091,"children":7092},{"style":629},[7093],{"type":259,"value":7094},"        {\n",{"type":254,"tag":622,"props":7096,"children":7097},{"class":624,"line":1087},[7098,7103,7107,7112,7116,7121,7125,7129,7133],{"type":254,"tag":622,"props":7099,"children":7100},{"style":1202},[7101],{"type":259,"value":7102},"            _logger",{"type":254,"tag":622,"props":7104,"children":7105},{"style":629},[7106],{"type":259,"value":594},{"type":254,"tag":622,"props":7108,"children":7109},{"style":1735},[7110],{"type":259,"value":7111},"LogInformation",{"type":254,"tag":622,"props":7113,"children":7114},{"style":629},[7115],{"type":259,"value":1668},{"type":254,"tag":622,"props":7117,"children":7118},{"style":1202},[7119],{"type":259,"value":7120},"blobContainer",{"type":254,"tag":622,"props":7122,"children":7123},{"style":629},[7124],{"type":259,"value":594},{"type":254,"tag":622,"props":7126,"children":7127},{"style":1202},[7128],{"type":259,"value":2254},{"type":254,"tag":622,"props":7130,"children":7131},{"style":629},[7132],{"type":259,"value":2309},{"type":254,"tag":622,"props":7134,"children":7135},{"style":1202},[7136],{"type":259,"value":2350},{"type":254,"tag":622,"props":7138,"children":7139},{"class":624,"line":1096},[7140],{"type":254,"tag":622,"props":7141,"children":7142},{"style":629},[7143],{"type":259,"value":7144},"        }\n",{"type":254,"tag":622,"props":7146,"children":7147},{"class":624,"line":1105},[7148],{"type":254,"tag":622,"props":7149,"children":7150},{"style":629},[7151],{"type":259,"value":1093},{"type":254,"tag":622,"props":7153,"children":7154},{"class":624,"line":4131},[7155],{"type":254,"tag":622,"props":7156,"children":7157},{"style":629},[7158],{"type":259,"value":1111},{"type":254,"tag":427,"props":7160,"children":7161},{"icon":429},[7162],{"type":254,"tag":255,"props":7163,"children":7164},{},[7165,7167,7172,7174,7180,7182,7188,7190,7195],{"type":259,"value":7166},"You may find it convenient to configure the dependency injection for all Azure service clients in a central place with the ",{"type":254,"tag":389,"props":7168,"children":7170},{"className":7169},[],[7171],{"type":259,"value":6555},{"type":259,"value":7173}," method. When applications become larger with different ",{"type":254,"tag":389,"props":7175,"children":7177},{"className":7176},[],[7178],{"type":259,"value":7179},"csproj,",{"type":259,"value":7181}," I often prefer to separate service registration by business domain/module so having everything in a central place does not always suit my needs. That's not a problem, as the internal methods of the library make use of the ",{"type":254,"tag":389,"props":7183,"children":7185},{"className":7184},[],[7186],{"type":259,"value":7187},"TryAddd",{"type":259,"value":7189}," methods for registering services, I can call ",{"type":254,"tag":389,"props":7191,"children":7193},{"className":7192},[],[7194],{"type":259,"value":6555},{"type":259,"value":7196}," in multiple places with only the services I want to register.",{"type":254,"tag":282,"props":7198,"children":7200},{"id":7199},"easily-manage-the-authentication-to-azure-services",[7201],{"type":259,"value":7202},"Easily manage the authentication to Azure services",{"type":254,"tag":255,"props":7204,"children":7205},{},[7206,7208,7215],{"type":259,"value":7207},"All the SDKs use the ",{"type":254,"tag":262,"props":7209,"children":7212},{"href":7210,"rel":7211},"https://www.nuget.org/packages/Azure.Identity",[266],[7213],{"type":259,"value":7214},"Azure.Identity",{"type":259,"value":7216}," package to authenticate to Azure. There are different authentication methods available and you can easily specify which one to use with each client. Additionally, you can define a default authentication method for all clients, as demonstrated in the previous example.",{"type":254,"tag":612,"props":7218,"children":7220},{"className":1423,"code":7219,"language":1425,"meta":205,"style":205},"builder.Services.AddAzureClients(clientBuilder =>\n{\n    clientBuilder.AddServiceBusClient(\"https://sb-diwithazuresdk.servicebus.windows.net/\")\n        .WithCredential(new ManagedIdentityCredential());\n    \n    clientBuilder.AddTableServiceClient(new Uri(\"https://stdiwithazuresdk.table.core.windows.net\"))\n        .WithCredential(new EnvironmentCredential());\n\n    clientBuilder.AddBlobServiceClient(new Uri(\"https://stdiwithazuresdk.blob.core.windows.net/\"));\n    \n    clientBuilder.UseCredential(new DefaultAzureCredential());\n});\n",[7221],{"type":254,"tag":389,"props":7222,"children":7223},{"__ignoreMap":205},[7224,7259,7266,7303,7329,7337,7382,7406,7413,7456,7463,7490],{"type":254,"tag":622,"props":7225,"children":7226},{"class":624,"line":625},[7227,7231,7235,7239,7243,7247,7251,7255],{"type":254,"tag":622,"props":7228,"children":7229},{"style":1202},[7230],{"type":259,"value":6499},{"type":254,"tag":622,"props":7232,"children":7233},{"style":629},[7234],{"type":259,"value":594},{"type":254,"tag":622,"props":7236,"children":7237},{"style":1202},[7238],{"type":259,"value":6508},{"type":254,"tag":622,"props":7240,"children":7241},{"style":629},[7242],{"type":259,"value":594},{"type":254,"tag":622,"props":7244,"children":7245},{"style":1735},[7246],{"type":259,"value":6555},{"type":254,"tag":622,"props":7248,"children":7249},{"style":629},[7250],{"type":259,"value":1668},{"type":254,"tag":622,"props":7252,"children":7253},{"style":714},[7254],{"type":259,"value":6564},{"type":254,"tag":622,"props":7256,"children":7257},{"style":629},[7258],{"type":259,"value":6569},{"type":254,"tag":622,"props":7260,"children":7261},{"class":624,"line":635},[7262],{"type":254,"tag":622,"props":7263,"children":7264},{"style":629},[7265],{"type":259,"value":632},{"type":254,"tag":622,"props":7267,"children":7268},{"class":624,"line":679},[7269,7273,7277,7282,7286,7290,7295,7299],{"type":254,"tag":622,"props":7270,"children":7271},{"style":1202},[7272],{"type":259,"value":6584},{"type":254,"tag":622,"props":7274,"children":7275},{"style":629},[7276],{"type":259,"value":594},{"type":254,"tag":622,"props":7278,"children":7279},{"style":1735},[7280],{"type":259,"value":7281},"AddServiceBusClient",{"type":254,"tag":622,"props":7283,"children":7284},{"style":629},[7285],{"type":259,"value":1668},{"type":254,"tag":622,"props":7287,"children":7288},{"style":629},[7289],{"type":259,"value":652},{"type":254,"tag":622,"props":7291,"children":7292},{"style":664},[7293],{"type":259,"value":7294},"https://sb-diwithazuresdk.servicebus.windows.net/",{"type":254,"tag":622,"props":7296,"children":7297},{"style":629},[7298],{"type":259,"value":652},{"type":254,"tag":622,"props":7300,"children":7301},{"style":629},[7302],{"type":259,"value":1685},{"type":254,"tag":622,"props":7304,"children":7305},{"class":624,"line":705},[7306,7311,7316,7320,7325],{"type":254,"tag":622,"props":7307,"children":7308},{"style":629},[7309],{"type":259,"value":7310},"        .",{"type":254,"tag":622,"props":7312,"children":7313},{"style":1735},[7314],{"type":259,"value":7315},"WithCredential",{"type":254,"tag":622,"props":7317,"children":7318},{"style":629},[7319],{"type":259,"value":6598},{"type":254,"tag":622,"props":7321,"children":7322},{"style":714},[7323],{"type":259,"value":7324}," ManagedIdentityCredential",{"type":254,"tag":622,"props":7326,"children":7327},{"style":629},[7328],{"type":259,"value":2154},{"type":254,"tag":622,"props":7330,"children":7331},{"class":624,"line":732},[7332],{"type":254,"tag":622,"props":7333,"children":7334},{"style":1202},[7335],{"type":259,"value":7336},"    \n",{"type":254,"tag":622,"props":7338,"children":7339},{"class":624,"line":759},[7340,7344,7348,7353,7357,7361,7365,7369,7374,7378],{"type":254,"tag":622,"props":7341,"children":7342},{"style":1202},[7343],{"type":259,"value":6584},{"type":254,"tag":622,"props":7345,"children":7346},{"style":629},[7347],{"type":259,"value":594},{"type":254,"tag":622,"props":7349,"children":7350},{"style":1735},[7351],{"type":259,"value":7352},"AddTableServiceClient",{"type":254,"tag":622,"props":7354,"children":7355},{"style":629},[7356],{"type":259,"value":6598},{"type":254,"tag":622,"props":7358,"children":7359},{"style":714},[7360],{"type":259,"value":1663},{"type":254,"tag":622,"props":7362,"children":7363},{"style":629},[7364],{"type":259,"value":1668},{"type":254,"tag":622,"props":7366,"children":7367},{"style":629},[7368],{"type":259,"value":652},{"type":254,"tag":622,"props":7370,"children":7371},{"style":664},[7372],{"type":259,"value":7373},"https://stdiwithazuresdk.table.core.windows.net",{"type":254,"tag":622,"props":7375,"children":7376},{"style":629},[7377],{"type":259,"value":652},{"type":254,"tag":622,"props":7379,"children":7380},{"style":629},[7381],{"type":259,"value":7086},{"type":254,"tag":622,"props":7383,"children":7384},{"class":624,"line":798},[7385,7389,7393,7397,7402],{"type":254,"tag":622,"props":7386,"children":7387},{"style":629},[7388],{"type":259,"value":7310},{"type":254,"tag":622,"props":7390,"children":7391},{"style":1735},[7392],{"type":259,"value":7315},{"type":254,"tag":622,"props":7394,"children":7395},{"style":629},[7396],{"type":259,"value":6598},{"type":254,"tag":622,"props":7398,"children":7399},{"style":714},[7400],{"type":259,"value":7401}," EnvironmentCredential",{"type":254,"tag":622,"props":7403,"children":7404},{"style":629},[7405],{"type":259,"value":2154},{"type":254,"tag":622,"props":7407,"children":7408},{"class":624,"line":836},[7409],{"type":254,"tag":622,"props":7410,"children":7411},{"emptyLinePlaceholder":1699},[7412],{"type":259,"value":1702},{"type":254,"tag":622,"props":7414,"children":7415},{"class":624,"line":862},[7416,7420,7424,7428,7432,7436,7440,7444,7448,7452],{"type":254,"tag":622,"props":7417,"children":7418},{"style":1202},[7419],{"type":259,"value":6584},{"type":254,"tag":622,"props":7421,"children":7422},{"style":629},[7423],{"type":259,"value":594},{"type":254,"tag":622,"props":7425,"children":7426},{"style":1735},[7427],{"type":259,"value":6593},{"type":254,"tag":622,"props":7429,"children":7430},{"style":629},[7431],{"type":259,"value":6598},{"type":254,"tag":622,"props":7433,"children":7434},{"style":714},[7435],{"type":259,"value":1663},{"type":254,"tag":622,"props":7437,"children":7438},{"style":629},[7439],{"type":259,"value":1668},{"type":254,"tag":622,"props":7441,"children":7442},{"style":629},[7443],{"type":259,"value":652},{"type":254,"tag":622,"props":7445,"children":7446},{"style":664},[7447],{"type":259,"value":6615},{"type":254,"tag":622,"props":7449,"children":7450},{"style":629},[7451],{"type":259,"value":652},{"type":254,"tag":622,"props":7453,"children":7454},{"style":629},[7455],{"type":259,"value":6624},{"type":254,"tag":622,"props":7457,"children":7458},{"class":624,"line":900},[7459],{"type":254,"tag":622,"props":7460,"children":7461},{"style":1202},[7462],{"type":259,"value":7336},{"type":254,"tag":622,"props":7464,"children":7465},{"class":624,"line":938},[7466,7470,7474,7478,7482,7486],{"type":254,"tag":622,"props":7467,"children":7468},{"style":1202},[7469],{"type":259,"value":6584},{"type":254,"tag":622,"props":7471,"children":7472},{"style":629},[7473],{"type":259,"value":594},{"type":254,"tag":622,"props":7475,"children":7476},{"style":1735},[7477],{"type":259,"value":6640},{"type":254,"tag":622,"props":7479,"children":7480},{"style":629},[7481],{"type":259,"value":6598},{"type":254,"tag":622,"props":7483,"children":7484},{"style":714},[7485],{"type":259,"value":6649},{"type":254,"tag":622,"props":7487,"children":7488},{"style":629},[7489],{"type":259,"value":2154},{"type":254,"tag":622,"props":7491,"children":7492},{"class":624,"line":976},[7493],{"type":254,"tag":622,"props":7494,"children":7495},{"style":629},[7496],{"type":259,"value":6661},{"type":254,"tag":255,"props":7498,"children":7499},{},[7500],{"type":259,"value":7501},"In the example above, we configured:",{"type":254,"tag":332,"props":7503,"children":7504},{},[7505,7510,7515],{"type":254,"tag":336,"props":7506,"children":7507},{},[7508],{"type":259,"value":7509},"the service bus client to use the managed identity of the application to obtain a valid token for the service bus",{"type":254,"tag":336,"props":7511,"children":7512},{},[7513],{"type":259,"value":7514},"the table client to use environment variables to obtain a valid token for the storage table",{"type":254,"tag":336,"props":7516,"children":7517},{},[7518,7520,7525],{"type":259,"value":7519},"the blob client without any credentials so that it will use the one that we configured by default (with the ",{"type":254,"tag":389,"props":7521,"children":7523},{"className":7522},[],[7524],{"type":259,"value":6640},{"type":259,"value":7526}," method)",{"type":254,"tag":282,"props":7528,"children":7530},{"id":7529},"effortlessly-configure-the-azure-clients-options",[7531],{"type":259,"value":7532},"Effortlessly configure the Azure clients' options",{"type":254,"tag":255,"props":7534,"children":7535},{},[7536,7538,7543],{"type":259,"value":7537},"All Azure clients have options that can be effortlessly configured when registering them in the ",{"type":254,"tag":389,"props":7539,"children":7541},{"className":7540},[],[7542],{"type":259,"value":6555},{"type":259,"value":7544}," method.",{"type":254,"tag":612,"props":7546,"children":7548},{"className":1423,"code":7547,"language":1425,"meta":205,"style":205},"builder.Services.AddAzureClients(clientBuilder =>\n{\n    clientBuilder.AddBlobServiceClient(new Uri(\"https://stdiwithazuresdk.blob.core.windows.net/\"))\n        .WithCredential(new DefaultAzureCredential())\n        .ConfigureOptions(options =>\n        {\n            options.TrimBlobNameSlashes = true;\n            options.Retry.MaxRetries = 10;\n            options.Diagnostics.IsLoggingEnabled = false;\n        });\n});\n",[7549],{"type":254,"tag":389,"props":7550,"children":7551},{"__ignoreMap":205},[7552,7587,7594,7637,7660,7685,7692,7723,7761,7799,7807],{"type":254,"tag":622,"props":7553,"children":7554},{"class":624,"line":625},[7555,7559,7563,7567,7571,7575,7579,7583],{"type":254,"tag":622,"props":7556,"children":7557},{"style":1202},[7558],{"type":259,"value":6499},{"type":254,"tag":622,"props":7560,"children":7561},{"style":629},[7562],{"type":259,"value":594},{"type":254,"tag":622,"props":7564,"children":7565},{"style":1202},[7566],{"type":259,"value":6508},{"type":254,"tag":622,"props":7568,"children":7569},{"style":629},[7570],{"type":259,"value":594},{"type":254,"tag":622,"props":7572,"children":7573},{"style":1735},[7574],{"type":259,"value":6555},{"type":254,"tag":622,"props":7576,"children":7577},{"style":629},[7578],{"type":259,"value":1668},{"type":254,"tag":622,"props":7580,"children":7581},{"style":714},[7582],{"type":259,"value":6564},{"type":254,"tag":622,"props":7584,"children":7585},{"style":629},[7586],{"type":259,"value":6569},{"type":254,"tag":622,"props":7588,"children":7589},{"class":624,"line":635},[7590],{"type":254,"tag":622,"props":7591,"children":7592},{"style":629},[7593],{"type":259,"value":632},{"type":254,"tag":622,"props":7595,"children":7596},{"class":624,"line":679},[7597,7601,7605,7609,7613,7617,7621,7625,7629,7633],{"type":254,"tag":622,"props":7598,"children":7599},{"style":1202},[7600],{"type":259,"value":6584},{"type":254,"tag":622,"props":7602,"children":7603},{"style":629},[7604],{"type":259,"value":594},{"type":254,"tag":622,"props":7606,"children":7607},{"style":1735},[7608],{"type":259,"value":6593},{"type":254,"tag":622,"props":7610,"children":7611},{"style":629},[7612],{"type":259,"value":6598},{"type":254,"tag":622,"props":7614,"children":7615},{"style":714},[7616],{"type":259,"value":1663},{"type":254,"tag":622,"props":7618,"children":7619},{"style":629},[7620],{"type":259,"value":1668},{"type":254,"tag":622,"props":7622,"children":7623},{"style":629},[7624],{"type":259,"value":652},{"type":254,"tag":622,"props":7626,"children":7627},{"style":664},[7628],{"type":259,"value":6615},{"type":254,"tag":622,"props":7630,"children":7631},{"style":629},[7632],{"type":259,"value":652},{"type":254,"tag":622,"props":7634,"children":7635},{"style":629},[7636],{"type":259,"value":7086},{"type":254,"tag":622,"props":7638,"children":7639},{"class":624,"line":705},[7640,7644,7648,7652,7656],{"type":254,"tag":622,"props":7641,"children":7642},{"style":629},[7643],{"type":259,"value":7310},{"type":254,"tag":622,"props":7645,"children":7646},{"style":1735},[7647],{"type":259,"value":7315},{"type":254,"tag":622,"props":7649,"children":7650},{"style":629},[7651],{"type":259,"value":6598},{"type":254,"tag":622,"props":7653,"children":7654},{"style":714},[7655],{"type":259,"value":6649},{"type":254,"tag":622,"props":7657,"children":7658},{"style":629},[7659],{"type":259,"value":2756},{"type":254,"tag":622,"props":7661,"children":7662},{"class":624,"line":732},[7663,7667,7672,7676,7681],{"type":254,"tag":622,"props":7664,"children":7665},{"style":629},[7666],{"type":259,"value":7310},{"type":254,"tag":622,"props":7668,"children":7669},{"style":1735},[7670],{"type":259,"value":7671},"ConfigureOptions",{"type":254,"tag":622,"props":7673,"children":7674},{"style":629},[7675],{"type":259,"value":1668},{"type":254,"tag":622,"props":7677,"children":7678},{"style":714},[7679],{"type":259,"value":7680},"options",{"type":254,"tag":622,"props":7682,"children":7683},{"style":629},[7684],{"type":259,"value":6569},{"type":254,"tag":622,"props":7686,"children":7687},{"class":624,"line":759},[7688],{"type":254,"tag":622,"props":7689,"children":7690},{"style":629},[7691],{"type":259,"value":7094},{"type":254,"tag":622,"props":7693,"children":7694},{"class":624,"line":798},[7695,7700,7704,7709,7713,7719],{"type":254,"tag":622,"props":7696,"children":7697},{"style":1202},[7698],{"type":259,"value":7699},"            options",{"type":254,"tag":622,"props":7701,"children":7702},{"style":629},[7703],{"type":259,"value":594},{"type":254,"tag":622,"props":7705,"children":7706},{"style":1202},[7707],{"type":259,"value":7708},"TrimBlobNameSlashes ",{"type":254,"tag":622,"props":7710,"children":7711},{"style":629},[7712],{"type":259,"value":1654},{"type":254,"tag":622,"props":7714,"children":7716},{"style":7715},"--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC",[7717],{"type":259,"value":7718}," true",{"type":254,"tag":622,"props":7720,"children":7721},{"style":629},[7722],{"type":259,"value":2350},{"type":254,"tag":622,"props":7724,"children":7725},{"class":624,"line":836},[7726,7730,7734,7739,7743,7748,7752,7757],{"type":254,"tag":622,"props":7727,"children":7728},{"style":1202},[7729],{"type":259,"value":7699},{"type":254,"tag":622,"props":7731,"children":7732},{"style":629},[7733],{"type":259,"value":594},{"type":254,"tag":622,"props":7735,"children":7736},{"style":1202},[7737],{"type":259,"value":7738},"Retry",{"type":254,"tag":622,"props":7740,"children":7741},{"style":629},[7742],{"type":259,"value":594},{"type":254,"tag":622,"props":7744,"children":7745},{"style":1202},[7746],{"type":259,"value":7747},"MaxRetries ",{"type":254,"tag":622,"props":7749,"children":7750},{"style":629},[7751],{"type":259,"value":1654},{"type":254,"tag":622,"props":7753,"children":7754},{"style":741},[7755],{"type":259,"value":7756}," 10",{"type":254,"tag":622,"props":7758,"children":7759},{"style":629},[7760],{"type":259,"value":2350},{"type":254,"tag":622,"props":7762,"children":7763},{"class":624,"line":862},[7764,7768,7772,7777,7781,7786,7790,7795],{"type":254,"tag":622,"props":7765,"children":7766},{"style":1202},[7767],{"type":259,"value":7699},{"type":254,"tag":622,"props":7769,"children":7770},{"style":629},[7771],{"type":259,"value":594},{"type":254,"tag":622,"props":7773,"children":7774},{"style":1202},[7775],{"type":259,"value":7776},"Diagnostics",{"type":254,"tag":622,"props":7778,"children":7779},{"style":629},[7780],{"type":259,"value":594},{"type":254,"tag":622,"props":7782,"children":7783},{"style":1202},[7784],{"type":259,"value":7785},"IsLoggingEnabled ",{"type":254,"tag":622,"props":7787,"children":7788},{"style":629},[7789],{"type":259,"value":1654},{"type":254,"tag":622,"props":7791,"children":7792},{"style":7715},[7793],{"type":259,"value":7794}," false",{"type":254,"tag":622,"props":7796,"children":7797},{"style":629},[7798],{"type":259,"value":2350},{"type":254,"tag":622,"props":7800,"children":7801},{"class":624,"line":900},[7802],{"type":254,"tag":622,"props":7803,"children":7804},{"style":629},[7805],{"type":259,"value":7806},"        });\n",{"type":254,"tag":622,"props":7808,"children":7809},{"class":624,"line":938},[7810],{"type":254,"tag":622,"props":7811,"children":7812},{"style":629},[7813],{"type":259,"value":6661},{"type":254,"tag":255,"props":7815,"children":7816},{},[7817,7819,7825],{"type":259,"value":7818},"Some options are specific to the client (like the ",{"type":254,"tag":389,"props":7820,"children":7822},{"className":7821},[],[7823],{"type":259,"value":7824},"TrimBlobNameSlashes",{"type":259,"value":7826}," here for Blob client). Others can be configured globally and overridden on a client if necessary.",{"type":254,"tag":612,"props":7828,"children":7830},{"className":1423,"code":7829,"language":1425,"meta":205,"style":205},"builder.Services.AddAzureClients(clientBuilder =>\n{\n   clientBuilder.AddBlobServiceClient(new Uri(\"https://stdiwithazuresdk.blob.core.windows.net/\"))\n        .WithCredential(new DefaultAzureCredential())\n        .ConfigureOptions(options =>\n        {\n            options.TrimBlobNameSlashes = true;\n            options.Retry.MaxRetries = 10;\n            options.Diagnostics.IsLoggingEnabled = false;\n        });\n    \n    clientBuilder.ConfigureDefaults(options =>\n    {\n        options.Retry.MaxRetries = 5;\n        options.Retry.Mode = RetryMode.Exponential;\n        options.Diagnostics.IsDistributedTracingEnabled = true;\n    });\n});\n",[7831],{"type":254,"tag":389,"props":7832,"children":7833},{"__ignoreMap":205},[7834,7869,7876,7920,7943,7966,7973,8000,8035,8070,8077,8084,8112,8119,8155,8201,8237,8245],{"type":254,"tag":622,"props":7835,"children":7836},{"class":624,"line":625},[7837,7841,7845,7849,7853,7857,7861,7865],{"type":254,"tag":622,"props":7838,"children":7839},{"style":1202},[7840],{"type":259,"value":6499},{"type":254,"tag":622,"props":7842,"children":7843},{"style":629},[7844],{"type":259,"value":594},{"type":254,"tag":622,"props":7846,"children":7847},{"style":1202},[7848],{"type":259,"value":6508},{"type":254,"tag":622,"props":7850,"children":7851},{"style":629},[7852],{"type":259,"value":594},{"type":254,"tag":622,"props":7854,"children":7855},{"style":1735},[7856],{"type":259,"value":6555},{"type":254,"tag":622,"props":7858,"children":7859},{"style":629},[7860],{"type":259,"value":1668},{"type":254,"tag":622,"props":7862,"children":7863},{"style":714},[7864],{"type":259,"value":6564},{"type":254,"tag":622,"props":7866,"children":7867},{"style":629},[7868],{"type":259,"value":6569},{"type":254,"tag":622,"props":7870,"children":7871},{"class":624,"line":635},[7872],{"type":254,"tag":622,"props":7873,"children":7874},{"style":629},[7875],{"type":259,"value":632},{"type":254,"tag":622,"props":7877,"children":7878},{"class":624,"line":679},[7879,7884,7888,7892,7896,7900,7904,7908,7912,7916],{"type":254,"tag":622,"props":7880,"children":7881},{"style":1202},[7882],{"type":259,"value":7883},"   clientBuilder",{"type":254,"tag":622,"props":7885,"children":7886},{"style":629},[7887],{"type":259,"value":594},{"type":254,"tag":622,"props":7889,"children":7890},{"style":1735},[7891],{"type":259,"value":6593},{"type":254,"tag":622,"props":7893,"children":7894},{"style":629},[7895],{"type":259,"value":6598},{"type":254,"tag":622,"props":7897,"children":7898},{"style":714},[7899],{"type":259,"value":1663},{"type":254,"tag":622,"props":7901,"children":7902},{"style":629},[7903],{"type":259,"value":1668},{"type":254,"tag":622,"props":7905,"children":7906},{"style":629},[7907],{"type":259,"value":652},{"type":254,"tag":622,"props":7909,"children":7910},{"style":664},[7911],{"type":259,"value":6615},{"type":254,"tag":622,"props":7913,"children":7914},{"style":629},[7915],{"type":259,"value":652},{"type":254,"tag":622,"props":7917,"children":7918},{"style":629},[7919],{"type":259,"value":7086},{"type":254,"tag":622,"props":7921,"children":7922},{"class":624,"line":705},[7923,7927,7931,7935,7939],{"type":254,"tag":622,"props":7924,"children":7925},{"style":629},[7926],{"type":259,"value":7310},{"type":254,"tag":622,"props":7928,"children":7929},{"style":1735},[7930],{"type":259,"value":7315},{"type":254,"tag":622,"props":7932,"children":7933},{"style":629},[7934],{"type":259,"value":6598},{"type":254,"tag":622,"props":7936,"children":7937},{"style":714},[7938],{"type":259,"value":6649},{"type":254,"tag":622,"props":7940,"children":7941},{"style":629},[7942],{"type":259,"value":2756},{"type":254,"tag":622,"props":7944,"children":7945},{"class":624,"line":732},[7946,7950,7954,7958,7962],{"type":254,"tag":622,"props":7947,"children":7948},{"style":629},[7949],{"type":259,"value":7310},{"type":254,"tag":622,"props":7951,"children":7952},{"style":1735},[7953],{"type":259,"value":7671},{"type":254,"tag":622,"props":7955,"children":7956},{"style":629},[7957],{"type":259,"value":1668},{"type":254,"tag":622,"props":7959,"children":7960},{"style":714},[7961],{"type":259,"value":7680},{"type":254,"tag":622,"props":7963,"children":7964},{"style":629},[7965],{"type":259,"value":6569},{"type":254,"tag":622,"props":7967,"children":7968},{"class":624,"line":759},[7969],{"type":254,"tag":622,"props":7970,"children":7971},{"style":629},[7972],{"type":259,"value":7094},{"type":254,"tag":622,"props":7974,"children":7975},{"class":624,"line":798},[7976,7980,7984,7988,7992,7996],{"type":254,"tag":622,"props":7977,"children":7978},{"style":1202},[7979],{"type":259,"value":7699},{"type":254,"tag":622,"props":7981,"children":7982},{"style":629},[7983],{"type":259,"value":594},{"type":254,"tag":622,"props":7985,"children":7986},{"style":1202},[7987],{"type":259,"value":7708},{"type":254,"tag":622,"props":7989,"children":7990},{"style":629},[7991],{"type":259,"value":1654},{"type":254,"tag":622,"props":7993,"children":7994},{"style":7715},[7995],{"type":259,"value":7718},{"type":254,"tag":622,"props":7997,"children":7998},{"style":629},[7999],{"type":259,"value":2350},{"type":254,"tag":622,"props":8001,"children":8002},{"class":624,"line":836},[8003,8007,8011,8015,8019,8023,8027,8031],{"type":254,"tag":622,"props":8004,"children":8005},{"style":1202},[8006],{"type":259,"value":7699},{"type":254,"tag":622,"props":8008,"children":8009},{"style":629},[8010],{"type":259,"value":594},{"type":254,"tag":622,"props":8012,"children":8013},{"style":1202},[8014],{"type":259,"value":7738},{"type":254,"tag":622,"props":8016,"children":8017},{"style":629},[8018],{"type":259,"value":594},{"type":254,"tag":622,"props":8020,"children":8021},{"style":1202},[8022],{"type":259,"value":7747},{"type":254,"tag":622,"props":8024,"children":8025},{"style":629},[8026],{"type":259,"value":1654},{"type":254,"tag":622,"props":8028,"children":8029},{"style":741},[8030],{"type":259,"value":7756},{"type":254,"tag":622,"props":8032,"children":8033},{"style":629},[8034],{"type":259,"value":2350},{"type":254,"tag":622,"props":8036,"children":8037},{"class":624,"line":862},[8038,8042,8046,8050,8054,8058,8062,8066],{"type":254,"tag":622,"props":8039,"children":8040},{"style":1202},[8041],{"type":259,"value":7699},{"type":254,"tag":622,"props":8043,"children":8044},{"style":629},[8045],{"type":259,"value":594},{"type":254,"tag":622,"props":8047,"children":8048},{"style":1202},[8049],{"type":259,"value":7776},{"type":254,"tag":622,"props":8051,"children":8052},{"style":629},[8053],{"type":259,"value":594},{"type":254,"tag":622,"props":8055,"children":8056},{"style":1202},[8057],{"type":259,"value":7785},{"type":254,"tag":622,"props":8059,"children":8060},{"style":629},[8061],{"type":259,"value":1654},{"type":254,"tag":622,"props":8063,"children":8064},{"style":7715},[8065],{"type":259,"value":7794},{"type":254,"tag":622,"props":8067,"children":8068},{"style":629},[8069],{"type":259,"value":2350},{"type":254,"tag":622,"props":8071,"children":8072},{"class":624,"line":900},[8073],{"type":254,"tag":622,"props":8074,"children":8075},{"style":629},[8076],{"type":259,"value":7806},{"type":254,"tag":622,"props":8078,"children":8079},{"class":624,"line":938},[8080],{"type":254,"tag":622,"props":8081,"children":8082},{"style":1202},[8083],{"type":259,"value":7336},{"type":254,"tag":622,"props":8085,"children":8086},{"class":624,"line":976},[8087,8091,8095,8100,8104,8108],{"type":254,"tag":622,"props":8088,"children":8089},{"style":1202},[8090],{"type":259,"value":6584},{"type":254,"tag":622,"props":8092,"children":8093},{"style":629},[8094],{"type":259,"value":594},{"type":254,"tag":622,"props":8096,"children":8097},{"style":1735},[8098],{"type":259,"value":8099},"ConfigureDefaults",{"type":254,"tag":622,"props":8101,"children":8102},{"style":629},[8103],{"type":259,"value":1668},{"type":254,"tag":622,"props":8105,"children":8106},{"style":714},[8107],{"type":259,"value":7680},{"type":254,"tag":622,"props":8109,"children":8110},{"style":629},[8111],{"type":259,"value":6569},{"type":254,"tag":622,"props":8113,"children":8114},{"class":624,"line":1014},[8115],{"type":254,"tag":622,"props":8116,"children":8117},{"style":629},[8118],{"type":259,"value":3574},{"type":254,"tag":622,"props":8120,"children":8121},{"class":624,"line":1052},[8122,8127,8131,8135,8139,8143,8147,8151],{"type":254,"tag":622,"props":8123,"children":8124},{"style":1202},[8125],{"type":259,"value":8126},"        options",{"type":254,"tag":622,"props":8128,"children":8129},{"style":629},[8130],{"type":259,"value":594},{"type":254,"tag":622,"props":8132,"children":8133},{"style":1202},[8134],{"type":259,"value":7738},{"type":254,"tag":622,"props":8136,"children":8137},{"style":629},[8138],{"type":259,"value":594},{"type":254,"tag":622,"props":8140,"children":8141},{"style":1202},[8142],{"type":259,"value":7747},{"type":254,"tag":622,"props":8144,"children":8145},{"style":629},[8146],{"type":259,"value":1654},{"type":254,"tag":622,"props":8148,"children":8149},{"style":741},[8150],{"type":259,"value":6144},{"type":254,"tag":622,"props":8152,"children":8153},{"style":629},[8154],{"type":259,"value":2350},{"type":254,"tag":622,"props":8156,"children":8157},{"class":624,"line":1078},[8158,8162,8166,8170,8174,8179,8183,8188,8192,8197],{"type":254,"tag":622,"props":8159,"children":8160},{"style":1202},[8161],{"type":259,"value":8126},{"type":254,"tag":622,"props":8163,"children":8164},{"style":629},[8165],{"type":259,"value":594},{"type":254,"tag":622,"props":8167,"children":8168},{"style":1202},[8169],{"type":259,"value":7738},{"type":254,"tag":622,"props":8171,"children":8172},{"style":629},[8173],{"type":259,"value":594},{"type":254,"tag":622,"props":8175,"children":8176},{"style":1202},[8177],{"type":259,"value":8178},"Mode ",{"type":254,"tag":622,"props":8180,"children":8181},{"style":629},[8182],{"type":259,"value":1654},{"type":254,"tag":622,"props":8184,"children":8185},{"style":1202},[8186],{"type":259,"value":8187}," RetryMode",{"type":254,"tag":622,"props":8189,"children":8190},{"style":629},[8191],{"type":259,"value":594},{"type":254,"tag":622,"props":8193,"children":8194},{"style":1202},[8195],{"type":259,"value":8196},"Exponential",{"type":254,"tag":622,"props":8198,"children":8199},{"style":629},[8200],{"type":259,"value":2350},{"type":254,"tag":622,"props":8202,"children":8203},{"class":624,"line":1087},[8204,8208,8212,8216,8220,8225,8229,8233],{"type":254,"tag":622,"props":8205,"children":8206},{"style":1202},[8207],{"type":259,"value":8126},{"type":254,"tag":622,"props":8209,"children":8210},{"style":629},[8211],{"type":259,"value":594},{"type":254,"tag":622,"props":8213,"children":8214},{"style":1202},[8215],{"type":259,"value":7776},{"type":254,"tag":622,"props":8217,"children":8218},{"style":629},[8219],{"type":259,"value":594},{"type":254,"tag":622,"props":8221,"children":8222},{"style":1202},[8223],{"type":259,"value":8224},"IsDistributedTracingEnabled ",{"type":254,"tag":622,"props":8226,"children":8227},{"style":629},[8228],{"type":259,"value":1654},{"type":254,"tag":622,"props":8230,"children":8231},{"style":7715},[8232],{"type":259,"value":7718},{"type":254,"tag":622,"props":8234,"children":8235},{"style":629},[8236],{"type":259,"value":2350},{"type":254,"tag":622,"props":8238,"children":8239},{"class":624,"line":1096},[8240],{"type":254,"tag":622,"props":8241,"children":8242},{"style":629},[8243],{"type":259,"value":8244},"    });\n",{"type":254,"tag":622,"props":8246,"children":8247},{"class":624,"line":1105},[8248],{"type":254,"tag":622,"props":8249,"children":8250},{"style":629},[8251],{"type":259,"value":6661},{"type":254,"tag":255,"props":8253,"children":8254},{},[8255,8257,8262],{"type":259,"value":8256},"That's the purpose of the ",{"type":254,"tag":389,"props":8258,"children":8260},{"className":8259},[],[8261],{"type":259,"value":8099},{"type":259,"value":7544},{"type":254,"tag":427,"props":8264,"children":8265},{"icon":567},[8266],{"type":254,"tag":255,"props":8267,"children":8268},{},[8269,8271],{"type":259,"value":8270},"Please note that all this configuration (as well as the Uris of each client) can be loaded from the configuration like this ",{"type":254,"tag":389,"props":8272,"children":8274},{"className":8273},[],[8275],{"type":259,"value":8276},"clientBuilder.AddTableServiceClient(builder.Configuration.GetSection(\"Inventory:Tables\"));",{"type":254,"tag":282,"props":8278,"children":8280},{"id":8279},"use-named-clients-for-different-azure-resources",[8281],{"type":259,"value":8282},"Use named clients for different Azure resources",{"type":254,"tag":255,"props":8284,"children":8285},{},[8286,8288,8294],{"type":259,"value":8287},"Usually, you only need one client of each SDK in your application. Let's say you have multiple Azure Storage tables that are used in your application, you will only need to have one ",{"type":254,"tag":389,"props":8289,"children":8291},{"className":8290},[],[8292],{"type":259,"value":8293},"TableServiceClient",{"type":259,"value":8295},". However, if you are interacting with tables in two different storage accounts, you will need multiple table clients.",{"type":254,"tag":255,"props":8297,"children":8298},{},[8299],{"type":259,"value":8300},"To do that you can register your clients with a specific name:",{"type":254,"tag":612,"props":8302,"children":8304},{"className":1423,"code":8303,"language":1425,"meta":205,"style":205},"builder.Services.AddAzureClients(clientBuilder =>\n{\n    clientBuilder.AddTableServiceClient(builder.Configuration.GetSection(\"Shop:Inventory\"))\n        .WithName(\"Shop\");\n    clientBuilder.AddTableServiceClient(builder.Configuration.GetSection(\"Warehouse:Inventory\"))\n        .WithName(\"Warehouse\");\n}\n",[8305],{"type":254,"tag":389,"props":8306,"children":8307},{"__ignoreMap":205},[8308,8343,8350,8411,8444,8504,8536],{"type":254,"tag":622,"props":8309,"children":8310},{"class":624,"line":625},[8311,8315,8319,8323,8327,8331,8335,8339],{"type":254,"tag":622,"props":8312,"children":8313},{"style":1202},[8314],{"type":259,"value":6499},{"type":254,"tag":622,"props":8316,"children":8317},{"style":629},[8318],{"type":259,"value":594},{"type":254,"tag":622,"props":8320,"children":8321},{"style":1202},[8322],{"type":259,"value":6508},{"type":254,"tag":622,"props":8324,"children":8325},{"style":629},[8326],{"type":259,"value":594},{"type":254,"tag":622,"props":8328,"children":8329},{"style":1735},[8330],{"type":259,"value":6555},{"type":254,"tag":622,"props":8332,"children":8333},{"style":629},[8334],{"type":259,"value":1668},{"type":254,"tag":622,"props":8336,"children":8337},{"style":714},[8338],{"type":259,"value":6564},{"type":254,"tag":622,"props":8340,"children":8341},{"style":629},[8342],{"type":259,"value":6569},{"type":254,"tag":622,"props":8344,"children":8345},{"class":624,"line":635},[8346],{"type":254,"tag":622,"props":8347,"children":8348},{"style":629},[8349],{"type":259,"value":632},{"type":254,"tag":622,"props":8351,"children":8352},{"class":624,"line":679},[8353,8357,8361,8365,8369,8373,8377,8381,8385,8390,8394,8398,8403,8407],{"type":254,"tag":622,"props":8354,"children":8355},{"style":1202},[8356],{"type":259,"value":6584},{"type":254,"tag":622,"props":8358,"children":8359},{"style":629},[8360],{"type":259,"value":594},{"type":254,"tag":622,"props":8362,"children":8363},{"style":1735},[8364],{"type":259,"value":7352},{"type":254,"tag":622,"props":8366,"children":8367},{"style":629},[8368],{"type":259,"value":1668},{"type":254,"tag":622,"props":8370,"children":8371},{"style":1202},[8372],{"type":259,"value":6499},{"type":254,"tag":622,"props":8374,"children":8375},{"style":629},[8376],{"type":259,"value":594},{"type":254,"tag":622,"props":8378,"children":8379},{"style":1202},[8380],{"type":259,"value":2924},{"type":254,"tag":622,"props":8382,"children":8383},{"style":629},[8384],{"type":259,"value":594},{"type":254,"tag":622,"props":8386,"children":8387},{"style":1735},[8388],{"type":259,"value":8389},"GetSection",{"type":254,"tag":622,"props":8391,"children":8392},{"style":629},[8393],{"type":259,"value":1668},{"type":254,"tag":622,"props":8395,"children":8396},{"style":629},[8397],{"type":259,"value":652},{"type":254,"tag":622,"props":8399,"children":8400},{"style":664},[8401],{"type":259,"value":8402},"Shop:Inventory",{"type":254,"tag":622,"props":8404,"children":8405},{"style":629},[8406],{"type":259,"value":652},{"type":254,"tag":622,"props":8408,"children":8409},{"style":629},[8410],{"type":259,"value":7086},{"type":254,"tag":622,"props":8412,"children":8413},{"class":624,"line":705},[8414,8418,8423,8427,8431,8436,8440],{"type":254,"tag":622,"props":8415,"children":8416},{"style":629},[8417],{"type":259,"value":7310},{"type":254,"tag":622,"props":8419,"children":8420},{"style":1735},[8421],{"type":259,"value":8422},"WithName",{"type":254,"tag":622,"props":8424,"children":8425},{"style":629},[8426],{"type":259,"value":1668},{"type":254,"tag":622,"props":8428,"children":8429},{"style":629},[8430],{"type":259,"value":652},{"type":254,"tag":622,"props":8432,"children":8433},{"style":664},[8434],{"type":259,"value":8435},"Shop",{"type":254,"tag":622,"props":8437,"children":8438},{"style":629},[8439],{"type":259,"value":652},{"type":254,"tag":622,"props":8441,"children":8442},{"style":629},[8443],{"type":259,"value":1554},{"type":254,"tag":622,"props":8445,"children":8446},{"class":624,"line":732},[8447,8451,8455,8459,8463,8467,8471,8475,8479,8483,8487,8491,8496,8500],{"type":254,"tag":622,"props":8448,"children":8449},{"style":1202},[8450],{"type":259,"value":6584},{"type":254,"tag":622,"props":8452,"children":8453},{"style":629},[8454],{"type":259,"value":594},{"type":254,"tag":622,"props":8456,"children":8457},{"style":1735},[8458],{"type":259,"value":7352},{"type":254,"tag":622,"props":8460,"children":8461},{"style":629},[8462],{"type":259,"value":1668},{"type":254,"tag":622,"props":8464,"children":8465},{"style":1202},[8466],{"type":259,"value":6499},{"type":254,"tag":622,"props":8468,"children":8469},{"style":629},[8470],{"type":259,"value":594},{"type":254,"tag":622,"props":8472,"children":8473},{"style":1202},[8474],{"type":259,"value":2924},{"type":254,"tag":622,"props":8476,"children":8477},{"style":629},[8478],{"type":259,"value":594},{"type":254,"tag":622,"props":8480,"children":8481},{"style":1735},[8482],{"type":259,"value":8389},{"type":254,"tag":622,"props":8484,"children":8485},{"style":629},[8486],{"type":259,"value":1668},{"type":254,"tag":622,"props":8488,"children":8489},{"style":629},[8490],{"type":259,"value":652},{"type":254,"tag":622,"props":8492,"children":8493},{"style":664},[8494],{"type":259,"value":8495},"Warehouse:Inventory",{"type":254,"tag":622,"props":8497,"children":8498},{"style":629},[8499],{"type":259,"value":652},{"type":254,"tag":622,"props":8501,"children":8502},{"style":629},[8503],{"type":259,"value":7086},{"type":254,"tag":622,"props":8505,"children":8506},{"class":624,"line":759},[8507,8511,8515,8519,8523,8528,8532],{"type":254,"tag":622,"props":8508,"children":8509},{"style":629},[8510],{"type":259,"value":7310},{"type":254,"tag":622,"props":8512,"children":8513},{"style":1735},[8514],{"type":259,"value":8422},{"type":254,"tag":622,"props":8516,"children":8517},{"style":629},[8518],{"type":259,"value":1668},{"type":254,"tag":622,"props":8520,"children":8521},{"style":629},[8522],{"type":259,"value":652},{"type":254,"tag":622,"props":8524,"children":8525},{"style":664},[8526],{"type":259,"value":8527},"Warehouse",{"type":254,"tag":622,"props":8529,"children":8530},{"style":629},[8531],{"type":259,"value":652},{"type":254,"tag":622,"props":8533,"children":8534},{"style":629},[8535],{"type":259,"value":1554},{"type":254,"tag":622,"props":8537,"children":8538},{"class":624,"line":798},[8539],{"type":254,"tag":622,"props":8540,"children":8541},{"style":629},[8542],{"type":259,"value":1111},{"type":254,"tag":255,"props":8544,"children":8545},{},[8546],{"type":259,"value":8547},"This way, you will be able to retrieve the specific client you need in your code:",{"type":254,"tag":612,"props":8549,"children":8551},{"className":1423,"code":8550,"language":1425,"meta":205,"style":205},"public class WarehouseDeliveryService\n{\n    private readonly TableServiceClient _tableServiceClient;\n\n    public WarehouseDeliveryService(IAzureClientFactory\u003CTableServiceClient> azureClientFactory)\n    {\n        _tableServiceClient = azureClientFactory.CreateClient(\"Warehouse\");\n    }\n}\n",[8552],{"type":254,"tag":389,"props":8553,"children":8554},{"__ignoreMap":205},[8555,8571,8578,8603,8610,8652,8659,8704,8711],{"type":254,"tag":622,"props":8556,"children":8557},{"class":624,"line":625},[8558,8562,8566],{"type":254,"tag":622,"props":8559,"children":8560},{"style":644},[8561],{"type":259,"value":1437},{"type":254,"tag":622,"props":8563,"children":8564},{"style":714},[8565],{"type":259,"value":6761},{"type":254,"tag":622,"props":8567,"children":8568},{"style":714},[8569],{"type":259,"value":8570}," WarehouseDeliveryService\n",{"type":254,"tag":622,"props":8572,"children":8573},{"class":624,"line":635},[8574],{"type":254,"tag":622,"props":8575,"children":8576},{"style":629},[8577],{"type":259,"value":632},{"type":254,"tag":622,"props":8579,"children":8580},{"class":624,"line":679},[8581,8585,8589,8594,8599],{"type":254,"tag":622,"props":8582,"children":8583},{"style":644},[8584],{"type":259,"value":6790},{"type":254,"tag":622,"props":8586,"children":8587},{"style":644},[8588],{"type":259,"value":6795},{"type":254,"tag":622,"props":8590,"children":8591},{"style":714},[8592],{"type":259,"value":8593}," TableServiceClient",{"type":254,"tag":622,"props":8595,"children":8596},{"style":714},[8597],{"type":259,"value":8598}," _tableServiceClient",{"type":254,"tag":622,"props":8600,"children":8601},{"style":629},[8602],{"type":259,"value":2350},{"type":254,"tag":622,"props":8604,"children":8605},{"class":624,"line":705},[8606],{"type":254,"tag":622,"props":8607,"children":8608},{"emptyLinePlaceholder":1699},[8609],{"type":259,"value":1702},{"type":254,"tag":622,"props":8611,"children":8612},{"class":624,"line":732},[8613,8617,8622,8626,8631,8635,8639,8643,8648],{"type":254,"tag":622,"props":8614,"children":8615},{"style":644},[8616],{"type":259,"value":1970},{"type":254,"tag":622,"props":8618,"children":8619},{"style":1735},[8620],{"type":259,"value":8621}," WarehouseDeliveryService",{"type":254,"tag":622,"props":8623,"children":8624},{"style":629},[8625],{"type":259,"value":1668},{"type":254,"tag":622,"props":8627,"children":8628},{"style":714},[8629],{"type":259,"value":8630},"IAzureClientFactory",{"type":254,"tag":622,"props":8632,"children":8633},{"style":629},[8634],{"type":259,"value":1743},{"type":254,"tag":622,"props":8636,"children":8637},{"style":714},[8638],{"type":259,"value":8293},{"type":254,"tag":622,"props":8640,"children":8641},{"style":629},[8642],{"type":259,"value":6813},{"type":254,"tag":622,"props":8644,"children":8645},{"style":714},[8646],{"type":259,"value":8647}," azureClientFactory",{"type":254,"tag":622,"props":8649,"children":8650},{"style":629},[8651],{"type":259,"value":1685},{"type":254,"tag":622,"props":8653,"children":8654},{"class":624,"line":759},[8655],{"type":254,"tag":622,"props":8656,"children":8657},{"style":629},[8658],{"type":259,"value":3574},{"type":254,"tag":622,"props":8660,"children":8661},{"class":624,"line":798},[8662,8667,8671,8675,8679,8684,8688,8692,8696,8700],{"type":254,"tag":622,"props":8663,"children":8664},{"style":1202},[8665],{"type":259,"value":8666},"        _tableServiceClient ",{"type":254,"tag":622,"props":8668,"children":8669},{"style":629},[8670],{"type":259,"value":1654},{"type":254,"tag":622,"props":8672,"children":8673},{"style":1202},[8674],{"type":259,"value":8647},{"type":254,"tag":622,"props":8676,"children":8677},{"style":629},[8678],{"type":259,"value":594},{"type":254,"tag":622,"props":8680,"children":8681},{"style":1735},[8682],{"type":259,"value":8683},"CreateClient",{"type":254,"tag":622,"props":8685,"children":8686},{"style":629},[8687],{"type":259,"value":1668},{"type":254,"tag":622,"props":8689,"children":8690},{"style":629},[8691],{"type":259,"value":652},{"type":254,"tag":622,"props":8693,"children":8694},{"style":664},[8695],{"type":259,"value":8527},{"type":254,"tag":622,"props":8697,"children":8698},{"style":629},[8699],{"type":259,"value":652},{"type":254,"tag":622,"props":8701,"children":8702},{"style":629},[8703],{"type":259,"value":1554},{"type":254,"tag":622,"props":8705,"children":8706},{"class":624,"line":836},[8707],{"type":254,"tag":622,"props":8708,"children":8709},{"style":629},[8710],{"type":259,"value":1093},{"type":254,"tag":622,"props":8712,"children":8713},{"class":624,"line":862},[8714],{"type":254,"tag":622,"props":8715,"children":8716},{"style":629},[8717],{"type":259,"value":1111},{"type":254,"tag":282,"props":8719,"children":8721},{"id":8720},"register-a-custom-client-factory",[8722],{"type":259,"value":8723},"Register a custom client factory",{"type":254,"tag":255,"props":8725,"children":8726},{},[8727,8729,8736,8738,8744],{"type":259,"value":8728},"If you have ",{"type":254,"tag":262,"props":8730,"children":8733},{"href":8731,"rel":8732},"https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/extensions/Microsoft.Extensions.Azure/README.md#registering-a-custom-client-factory",[266],[8734],{"type":259,"value":8735},"specific needs",{"type":259,"value":8737},", the ",{"type":254,"tag":389,"props":8739,"children":8741},{"className":8740},[],[8742],{"type":259,"value":8743},"AddClient",{"type":259,"value":8745}," method can help you register your Azure client while letting you control how you instantiate the client.",{"type":254,"tag":255,"props":8747,"children":8748},{},[8749,8751,8757,8759,8765,8767,8772,8774,8781,8783,8788],{"type":259,"value":8750},"For instance, the Azure Cosmos Db .NET SDK is not built on the same foundation (",{"type":254,"tag":389,"props":8752,"children":8754},{"className":8753},[],[8755],{"type":259,"value":8756},"Azure.Core",{"type":259,"value":8758},") as the other SDKs. So at the time of writing, there is no ",{"type":254,"tag":389,"props":8760,"children":8762},{"className":8761},[],[8763],{"type":259,"value":8764},"AddCosmosServiceClient",{"type":259,"value":8766}," you can use in the ",{"type":254,"tag":389,"props":8768,"children":8770},{"className":8769},[],[8771],{"type":259,"value":6555},{"type":259,"value":8773}," (there is an ",{"type":254,"tag":262,"props":8775,"children":8778},{"href":8776,"rel":8777},"https://github.com/Azure/azure-cosmos-dotnet-v3/issues/4002",[266],[8779],{"type":259,"value":8780},"issue",{"type":259,"value":8782}," about that though). However, you can use the ",{"type":254,"tag":389,"props":8784,"children":8786},{"className":8785},[],[8787],{"type":259,"value":8743},{"type":259,"value":8789}," I've just mentioned.",{"type":254,"tag":612,"props":8791,"children":8793},{"className":1423,"code":8792,"language":1425,"meta":205,"style":205},"builder.Services.AddOptions\u003CCosmosDbConfiguration>().BindConfiguration(\"Warehouse:CosmosDb\");\nbuilder.Services.AddAzureClients(clientBuilder =>\n{\n    clientBuilder.AddClient\u003CCosmosClient, CosmosClientOptions>((_, _, serviceProvider) =>\n    {\n        var cosmosConfiguration = serviceProvider.GetRequiredService\u003CIOptions\u003CCosmosDbConfiguration>>().Value;\n        return new CosmosClientBuilder(cosmosConfiguration.Endpoint, cosmosConfiguration.AuthKey)\n            .WithSerializerOptions(new () { PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase })\n            .Build();\n    }).WithName(\"Warehouse\");\n}\n",[8794],{"type":254,"tag":389,"props":8795,"children":8796},{"__ignoreMap":205},[8797,8861,8896,8903,8972,8979,9039,9095,9149,9164,9196],{"type":254,"tag":622,"props":8798,"children":8799},{"class":624,"line":625},[8800,8804,8808,8812,8816,8821,8825,8830,8835,8840,8844,8848,8853,8857],{"type":254,"tag":622,"props":8801,"children":8802},{"style":1202},[8803],{"type":259,"value":6499},{"type":254,"tag":622,"props":8805,"children":8806},{"style":629},[8807],{"type":259,"value":594},{"type":254,"tag":622,"props":8809,"children":8810},{"style":1202},[8811],{"type":259,"value":6508},{"type":254,"tag":622,"props":8813,"children":8814},{"style":629},[8815],{"type":259,"value":594},{"type":254,"tag":622,"props":8817,"children":8818},{"style":1735},[8819],{"type":259,"value":8820},"AddOptions",{"type":254,"tag":622,"props":8822,"children":8823},{"style":629},[8824],{"type":259,"value":1743},{"type":254,"tag":622,"props":8826,"children":8827},{"style":714},[8828],{"type":259,"value":8829},"CosmosDbConfiguration",{"type":254,"tag":622,"props":8831,"children":8832},{"style":629},[8833],{"type":259,"value":8834},">().",{"type":254,"tag":622,"props":8836,"children":8837},{"style":1735},[8838],{"type":259,"value":8839},"BindConfiguration",{"type":254,"tag":622,"props":8841,"children":8842},{"style":629},[8843],{"type":259,"value":1668},{"type":254,"tag":622,"props":8845,"children":8846},{"style":629},[8847],{"type":259,"value":652},{"type":254,"tag":622,"props":8849,"children":8850},{"style":664},[8851],{"type":259,"value":8852},"Warehouse:CosmosDb",{"type":254,"tag":622,"props":8854,"children":8855},{"style":629},[8856],{"type":259,"value":652},{"type":254,"tag":622,"props":8858,"children":8859},{"style":629},[8860],{"type":259,"value":1554},{"type":254,"tag":622,"props":8862,"children":8863},{"class":624,"line":635},[8864,8868,8872,8876,8880,8884,8888,8892],{"type":254,"tag":622,"props":8865,"children":8866},{"style":1202},[8867],{"type":259,"value":6499},{"type":254,"tag":622,"props":8869,"children":8870},{"style":629},[8871],{"type":259,"value":594},{"type":254,"tag":622,"props":8873,"children":8874},{"style":1202},[8875],{"type":259,"value":6508},{"type":254,"tag":622,"props":8877,"children":8878},{"style":629},[8879],{"type":259,"value":594},{"type":254,"tag":622,"props":8881,"children":8882},{"style":1735},[8883],{"type":259,"value":6555},{"type":254,"tag":622,"props":8885,"children":8886},{"style":629},[8887],{"type":259,"value":1668},{"type":254,"tag":622,"props":8889,"children":8890},{"style":714},[8891],{"type":259,"value":6564},{"type":254,"tag":622,"props":8893,"children":8894},{"style":629},[8895],{"type":259,"value":6569},{"type":254,"tag":622,"props":8897,"children":8898},{"class":624,"line":679},[8899],{"type":254,"tag":622,"props":8900,"children":8901},{"style":629},[8902],{"type":259,"value":632},{"type":254,"tag":622,"props":8904,"children":8905},{"class":624,"line":705},[8906,8910,8914,8918,8922,8927,8931,8936,8941,8946,8950,8955,8959,8964,8968],{"type":254,"tag":622,"props":8907,"children":8908},{"style":1202},[8909],{"type":259,"value":6584},{"type":254,"tag":622,"props":8911,"children":8912},{"style":629},[8913],{"type":259,"value":594},{"type":254,"tag":622,"props":8915,"children":8916},{"style":1735},[8917],{"type":259,"value":8743},{"type":254,"tag":622,"props":8919,"children":8920},{"style":629},[8921],{"type":259,"value":1743},{"type":254,"tag":622,"props":8923,"children":8924},{"style":714},[8925],{"type":259,"value":8926},"CosmosClient",{"type":254,"tag":622,"props":8928,"children":8929},{"style":629},[8930],{"type":259,"value":3149},{"type":254,"tag":622,"props":8932,"children":8933},{"style":714},[8934],{"type":259,"value":8935}," CosmosClientOptions",{"type":254,"tag":622,"props":8937,"children":8938},{"style":629},[8939],{"type":259,"value":8940},">((",{"type":254,"tag":622,"props":8942,"children":8943},{"style":714},[8944],{"type":259,"value":8945},"_",{"type":254,"tag":622,"props":8947,"children":8948},{"style":629},[8949],{"type":259,"value":3149},{"type":254,"tag":622,"props":8951,"children":8952},{"style":714},[8953],{"type":259,"value":8954}," _",{"type":254,"tag":622,"props":8956,"children":8957},{"style":629},[8958],{"type":259,"value":3149},{"type":254,"tag":622,"props":8960,"children":8961},{"style":714},[8962],{"type":259,"value":8963}," serviceProvider",{"type":254,"tag":622,"props":8965,"children":8966},{"style":629},[8967],{"type":259,"value":2309},{"type":254,"tag":622,"props":8969,"children":8970},{"style":629},[8971],{"type":259,"value":6569},{"type":254,"tag":622,"props":8973,"children":8974},{"class":624,"line":732},[8975],{"type":254,"tag":622,"props":8976,"children":8977},{"style":629},[8978],{"type":259,"value":3574},{"type":254,"tag":622,"props":8980,"children":8981},{"class":624,"line":759},[8982,8986,8991,8995,8999,9003,9008,9012,9017,9021,9025,9030,9035],{"type":254,"tag":622,"props":8983,"children":8984},{"style":714},[8985],{"type":259,"value":3582},{"type":254,"tag":622,"props":8987,"children":8988},{"style":714},[8989],{"type":259,"value":8990}," cosmosConfiguration",{"type":254,"tag":622,"props":8992,"children":8993},{"style":629},[8994],{"type":259,"value":1619},{"type":254,"tag":622,"props":8996,"children":8997},{"style":1202},[8998],{"type":259,"value":8963},{"type":254,"tag":622,"props":9000,"children":9001},{"style":629},[9002],{"type":259,"value":594},{"type":254,"tag":622,"props":9004,"children":9005},{"style":1735},[9006],{"type":259,"value":9007},"GetRequiredService",{"type":254,"tag":622,"props":9009,"children":9010},{"style":629},[9011],{"type":259,"value":1743},{"type":254,"tag":622,"props":9013,"children":9014},{"style":714},[9015],{"type":259,"value":9016},"IOptions",{"type":254,"tag":622,"props":9018,"children":9019},{"style":629},[9020],{"type":259,"value":1743},{"type":254,"tag":622,"props":9022,"children":9023},{"style":714},[9024],{"type":259,"value":8829},{"type":254,"tag":622,"props":9026,"children":9027},{"style":629},[9028],{"type":259,"value":9029},">>().",{"type":254,"tag":622,"props":9031,"children":9032},{"style":1202},[9033],{"type":259,"value":9034},"Value",{"type":254,"tag":622,"props":9036,"children":9037},{"style":629},[9038],{"type":259,"value":2350},{"type":254,"tag":622,"props":9040,"children":9041},{"class":624,"line":798},[9042,9047,9051,9056,9060,9065,9069,9074,9078,9082,9086,9091],{"type":254,"tag":622,"props":9043,"children":9044},{"style":1196},[9045],{"type":259,"value":9046},"        return",{"type":254,"tag":622,"props":9048,"children":9049},{"style":629},[9050],{"type":259,"value":1624},{"type":254,"tag":622,"props":9052,"children":9053},{"style":714},[9054],{"type":259,"value":9055}," CosmosClientBuilder",{"type":254,"tag":622,"props":9057,"children":9058},{"style":629},[9059],{"type":259,"value":1668},{"type":254,"tag":622,"props":9061,"children":9062},{"style":1202},[9063],{"type":259,"value":9064},"cosmosConfiguration",{"type":254,"tag":622,"props":9066,"children":9067},{"style":629},[9068],{"type":259,"value":594},{"type":254,"tag":622,"props":9070,"children":9071},{"style":1202},[9072],{"type":259,"value":9073},"Endpoint",{"type":254,"tag":622,"props":9075,"children":9076},{"style":629},[9077],{"type":259,"value":3149},{"type":254,"tag":622,"props":9079,"children":9080},{"style":1202},[9081],{"type":259,"value":8990},{"type":254,"tag":622,"props":9083,"children":9084},{"style":629},[9085],{"type":259,"value":594},{"type":254,"tag":622,"props":9087,"children":9088},{"style":1202},[9089],{"type":259,"value":9090},"AuthKey",{"type":254,"tag":622,"props":9092,"children":9093},{"style":629},[9094],{"type":259,"value":1685},{"type":254,"tag":622,"props":9096,"children":9097},{"class":624,"line":836},[9098,9103,9108,9112,9117,9121,9126,9130,9135,9139,9144],{"type":254,"tag":622,"props":9099,"children":9100},{"style":629},[9101],{"type":259,"value":9102},"            .",{"type":254,"tag":622,"props":9104,"children":9105},{"style":1735},[9106],{"type":259,"value":9107},"WithSerializerOptions",{"type":254,"tag":622,"props":9109,"children":9110},{"style":629},[9111],{"type":259,"value":6598},{"type":254,"tag":622,"props":9113,"children":9114},{"style":629},[9115],{"type":259,"value":9116}," ()",{"type":254,"tag":622,"props":9118,"children":9119},{"style":629},[9120],{"type":259,"value":1990},{"type":254,"tag":622,"props":9122,"children":9123},{"style":1202},[9124],{"type":259,"value":9125}," PropertyNamingPolicy ",{"type":254,"tag":622,"props":9127,"children":9128},{"style":629},[9129],{"type":259,"value":1654},{"type":254,"tag":622,"props":9131,"children":9132},{"style":1202},[9133],{"type":259,"value":9134}," CosmosPropertyNamingPolicy",{"type":254,"tag":622,"props":9136,"children":9137},{"style":629},[9138],{"type":259,"value":594},{"type":254,"tag":622,"props":9140,"children":9141},{"style":1202},[9142],{"type":259,"value":9143},"CamelCase ",{"type":254,"tag":622,"props":9145,"children":9146},{"style":629},[9147],{"type":259,"value":9148},"})\n",{"type":254,"tag":622,"props":9150,"children":9151},{"class":624,"line":862},[9152,9156,9160],{"type":254,"tag":622,"props":9153,"children":9154},{"style":629},[9155],{"type":259,"value":9102},{"type":254,"tag":622,"props":9157,"children":9158},{"style":1735},[9159],{"type":259,"value":6697},{"type":254,"tag":622,"props":9161,"children":9162},{"style":629},[9163],{"type":259,"value":2798},{"type":254,"tag":622,"props":9165,"children":9166},{"class":624,"line":900},[9167,9172,9176,9180,9184,9188,9192],{"type":254,"tag":622,"props":9168,"children":9169},{"style":629},[9170],{"type":259,"value":9171},"    }).",{"type":254,"tag":622,"props":9173,"children":9174},{"style":1735},[9175],{"type":259,"value":8422},{"type":254,"tag":622,"props":9177,"children":9178},{"style":629},[9179],{"type":259,"value":1668},{"type":254,"tag":622,"props":9181,"children":9182},{"style":629},[9183],{"type":259,"value":652},{"type":254,"tag":622,"props":9185,"children":9186},{"style":664},[9187],{"type":259,"value":8527},{"type":254,"tag":622,"props":9189,"children":9190},{"style":629},[9191],{"type":259,"value":652},{"type":254,"tag":622,"props":9193,"children":9194},{"style":629},[9195],{"type":259,"value":1554},{"type":254,"tag":622,"props":9197,"children":9198},{"class":624,"line":938},[9199],{"type":254,"tag":622,"props":9200,"children":9201},{"style":629},[9202],{"type":259,"value":1111},{"type":254,"tag":255,"props":9204,"children":9205},{},[9206,9208,9213],{"type":259,"value":9207},"You can note that using the ",{"type":254,"tag":389,"props":9209,"children":9211},{"className":9210},[],[9212],{"type":259,"value":8743},{"type":259,"value":9214}," method allows us to take profit from the named clients' feature.",{"type":254,"tag":282,"props":9216,"children":9217},{"id":1230},[9218],{"type":259,"value":1233},{"type":254,"tag":255,"props":9220,"children":9221},{},[9222,9224,9233],{"type":259,"value":9223},"As you have seen, the use of the ",{"type":254,"tag":262,"props":9225,"children":9227},{"href":6307,"rel":9226},[266],[9228],{"type":254,"tag":389,"props":9229,"children":9231},{"className":9230},[],[9232],{"type":259,"value":6315},{"type":259,"value":9234}," package simplifies the registration and configuration of Azure clients. While providing you with a consistent way of handling the dependency injection for Azure SDKs, it also allows you to easily customize the authentication and other options available.",{"type":254,"tag":255,"props":9236,"children":9237},{},[9238],{"type":259,"value":9239},"I hope you learned something. Don't hesitate to share your tips or what you like about the Azure SDKs in the comments.",{"type":254,"tag":1260,"props":9241,"children":9242},{},[9243],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":9245},[9246,9247,9248,9249,9250,9251],{"id":6325,"depth":635,"text":6328},{"id":7199,"depth":635,"text":7202},{"id":7529,"depth":635,"text":7532},{"id":8279,"depth":635,"text":8282},{"id":8720,"depth":635,"text":8723},{"id":1230,"depth":635,"text":1233},"content:1.posts:59.azure-sdk-di.md","1.posts/59.azure-sdk-di.md",{"_path":181,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":180,"description":9255,"lead":9255,"date":9256,"image":9257,"badge":9259,"tags":9261,"ImageAttribution":9265,"body":9266,"_type":1275,"_id":10050,"_source":231,"_file":10051,"_extension":1278},"Some tips about Azure and Azure DevOps.","2024-01-29T00:00:00.000Z",{"src":9258},"/images/surface_1.jpg",{"label":9260},"Tips",[9262,1362,9263,9264],"tips learned this week","FinOps","Azure DevOps","Picture of \u003Ca href=\"https://unsplash.com/fr/@surface?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash\">Surface\u003C/a> on \u003Ca href=\"https://unsplash.com/fr/photos/appareil-surface-sur-la-table-taHYzvApW1o?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash\">Unsplash\u003C/a>",{"type":251,"children":9267,"toc":10045},[9268,9274,9279,9284,9674,9688,9697,9702,9707,9726,9735,9740,9917,9928,9937,9943,9948,9978,9983,9992,9998,10003,10008,10022,10031,10036,10041],{"type":254,"tag":282,"props":9269,"children":9271},{"id":9270},"easily-debug-a-non-http-triggered-azure-function",[9272],{"type":259,"value":9273},"Easily debug a non-HTTP-triggered Azure Function",{"type":254,"tag":255,"props":9275,"children":9276},{},[9277],{"type":259,"value":9278},"The other day, I wanted to locally debug a Queue-triggered function without manually adding a queue message to my local storage.",{"type":254,"tag":255,"props":9280,"children":9281},{},[9282],{"type":259,"value":9283},"My Azure Function looked like that:",{"type":254,"tag":612,"props":9285,"children":9287},{"className":1423,"code":9286,"language":1425,"meta":205,"style":205},"public record Order(string Product,int Count);\n\npublic class ProcessOrder\n{\n    private readonly ILogger\u003CProcessOrder> _logger;\n\n    public ProcessOrder(ILogger\u003CProcessOrder> logger)\n    {\n        _logger = logger;\n    }\n\n    [Function(nameof(ProcessOrder))]\n    public void Run([QueueTrigger(\"orders\")] Order sentOrder)\n    {\n        _logger.LogInformation($\"Order contains {sentOrder.Count} {sentOrder.Product}\");\n    }\n}\n",[9288],{"type":254,"tag":389,"props":9289,"children":9290},{"__ignoreMap":205},[9291,9334,9341,9357,9364,9400,9407,9447,9454,9473,9480,9487,9514,9576,9583,9660,9667],{"type":254,"tag":622,"props":9292,"children":9293},{"class":624,"line":625},[9294,9298,9302,9307,9311,9315,9320,9325,9330],{"type":254,"tag":622,"props":9295,"children":9296},{"style":644},[9297],{"type":259,"value":1437},{"type":254,"tag":622,"props":9299,"children":9300},{"style":714},[9301],{"type":259,"value":1442},{"type":254,"tag":622,"props":9303,"children":9304},{"style":714},[9305],{"type":259,"value":9306}," Order",{"type":254,"tag":622,"props":9308,"children":9309},{"style":629},[9310],{"type":259,"value":1668},{"type":254,"tag":622,"props":9312,"children":9313},{"style":629},[9314],{"type":259,"value":2851},{"type":254,"tag":622,"props":9316,"children":9317},{"style":714},[9318],{"type":259,"value":9319}," Product",{"type":254,"tag":622,"props":9321,"children":9322},{"style":629},[9323],{"type":259,"value":9324},",int",{"type":254,"tag":622,"props":9326,"children":9327},{"style":714},[9328],{"type":259,"value":9329}," Count",{"type":254,"tag":622,"props":9331,"children":9332},{"style":629},[9333],{"type":259,"value":1554},{"type":254,"tag":622,"props":9335,"children":9336},{"class":624,"line":635},[9337],{"type":254,"tag":622,"props":9338,"children":9339},{"emptyLinePlaceholder":1699},[9340],{"type":259,"value":1702},{"type":254,"tag":622,"props":9342,"children":9343},{"class":624,"line":679},[9344,9348,9352],{"type":254,"tag":622,"props":9345,"children":9346},{"style":644},[9347],{"type":259,"value":1437},{"type":254,"tag":622,"props":9349,"children":9350},{"style":714},[9351],{"type":259,"value":6761},{"type":254,"tag":622,"props":9353,"children":9354},{"style":714},[9355],{"type":259,"value":9356}," ProcessOrder\n",{"type":254,"tag":622,"props":9358,"children":9359},{"class":624,"line":705},[9360],{"type":254,"tag":622,"props":9361,"children":9362},{"style":629},[9363],{"type":259,"value":632},{"type":254,"tag":622,"props":9365,"children":9366},{"class":624,"line":732},[9367,9371,9375,9379,9383,9388,9392,9396],{"type":254,"tag":622,"props":9368,"children":9369},{"style":644},[9370],{"type":259,"value":6790},{"type":254,"tag":622,"props":9372,"children":9373},{"style":644},[9374],{"type":259,"value":6795},{"type":254,"tag":622,"props":9376,"children":9377},{"style":714},[9378],{"type":259,"value":6800},{"type":254,"tag":622,"props":9380,"children":9381},{"style":629},[9382],{"type":259,"value":1743},{"type":254,"tag":622,"props":9384,"children":9385},{"style":714},[9386],{"type":259,"value":9387},"ProcessOrder",{"type":254,"tag":622,"props":9389,"children":9390},{"style":629},[9391],{"type":259,"value":6813},{"type":254,"tag":622,"props":9393,"children":9394},{"style":714},[9395],{"type":259,"value":6818},{"type":254,"tag":622,"props":9397,"children":9398},{"style":629},[9399],{"type":259,"value":2350},{"type":254,"tag":622,"props":9401,"children":9402},{"class":624,"line":759},[9403],{"type":254,"tag":622,"props":9404,"children":9405},{"emptyLinePlaceholder":1699},[9406],{"type":259,"value":1702},{"type":254,"tag":622,"props":9408,"children":9409},{"class":624,"line":798},[9410,9414,9419,9423,9427,9431,9435,9439,9443],{"type":254,"tag":622,"props":9411,"children":9412},{"style":644},[9413],{"type":259,"value":1970},{"type":254,"tag":622,"props":9415,"children":9416},{"style":1735},[9417],{"type":259,"value":9418}," ProcessOrder",{"type":254,"tag":622,"props":9420,"children":9421},{"style":629},[9422],{"type":259,"value":1668},{"type":254,"tag":622,"props":9424,"children":9425},{"style":714},[9426],{"type":259,"value":6874},{"type":254,"tag":622,"props":9428,"children":9429},{"style":629},[9430],{"type":259,"value":1743},{"type":254,"tag":622,"props":9432,"children":9433},{"style":714},[9434],{"type":259,"value":9387},{"type":254,"tag":622,"props":9436,"children":9437},{"style":629},[9438],{"type":259,"value":6813},{"type":254,"tag":622,"props":9440,"children":9441},{"style":714},[9442],{"type":259,"value":6891},{"type":254,"tag":622,"props":9444,"children":9445},{"style":629},[9446],{"type":259,"value":1685},{"type":254,"tag":622,"props":9448,"children":9449},{"class":624,"line":836},[9450],{"type":254,"tag":622,"props":9451,"children":9452},{"style":629},[9453],{"type":259,"value":3574},{"type":254,"tag":622,"props":9455,"children":9456},{"class":624,"line":862},[9457,9461,9465,9469],{"type":254,"tag":622,"props":9458,"children":9459},{"style":1202},[9460],{"type":259,"value":6923},{"type":254,"tag":622,"props":9462,"children":9463},{"style":629},[9464],{"type":259,"value":1654},{"type":254,"tag":622,"props":9466,"children":9467},{"style":1202},[9468],{"type":259,"value":6891},{"type":254,"tag":622,"props":9470,"children":9471},{"style":629},[9472],{"type":259,"value":2350},{"type":254,"tag":622,"props":9474,"children":9475},{"class":624,"line":900},[9476],{"type":254,"tag":622,"props":9477,"children":9478},{"style":629},[9479],{"type":259,"value":1093},{"type":254,"tag":622,"props":9481,"children":9482},{"class":624,"line":938},[9483],{"type":254,"tag":622,"props":9484,"children":9485},{"emptyLinePlaceholder":1699},[9486],{"type":259,"value":1702},{"type":254,"tag":622,"props":9488,"children":9489},{"class":624,"line":976},[9490,9495,9500,9505,9509],{"type":254,"tag":622,"props":9491,"children":9492},{"style":629},[9493],{"type":259,"value":9494},"    [",{"type":254,"tag":622,"props":9496,"children":9497},{"style":714},[9498],{"type":259,"value":9499},"Function",{"type":254,"tag":622,"props":9501,"children":9502},{"style":629},[9503],{"type":259,"value":9504},"(nameof(",{"type":254,"tag":622,"props":9506,"children":9507},{"style":1202},[9508],{"type":259,"value":9387},{"type":254,"tag":622,"props":9510,"children":9511},{"style":629},[9512],{"type":259,"value":9513},"))]\n",{"type":254,"tag":622,"props":9515,"children":9516},{"class":624,"line":1014},[9517,9521,9526,9531,9536,9541,9545,9549,9554,9558,9563,9567,9572],{"type":254,"tag":622,"props":9518,"children":9519},{"style":644},[9520],{"type":259,"value":1970},{"type":254,"tag":622,"props":9522,"children":9523},{"style":629},[9524],{"type":259,"value":9525}," void",{"type":254,"tag":622,"props":9527,"children":9528},{"style":1735},[9529],{"type":259,"value":9530}," Run",{"type":254,"tag":622,"props":9532,"children":9533},{"style":629},[9534],{"type":259,"value":9535},"([",{"type":254,"tag":622,"props":9537,"children":9538},{"style":714},[9539],{"type":259,"value":9540},"QueueTrigger",{"type":254,"tag":622,"props":9542,"children":9543},{"style":629},[9544],{"type":259,"value":1668},{"type":254,"tag":622,"props":9546,"children":9547},{"style":629},[9548],{"type":259,"value":652},{"type":254,"tag":622,"props":9550,"children":9551},{"style":664},[9552],{"type":259,"value":9553},"orders",{"type":254,"tag":622,"props":9555,"children":9556},{"style":629},[9557],{"type":259,"value":652},{"type":254,"tag":622,"props":9559,"children":9560},{"style":629},[9561],{"type":259,"value":9562},")]",{"type":254,"tag":622,"props":9564,"children":9565},{"style":714},[9566],{"type":259,"value":9306},{"type":254,"tag":622,"props":9568,"children":9569},{"style":714},[9570],{"type":259,"value":9571}," sentOrder",{"type":254,"tag":622,"props":9573,"children":9574},{"style":629},[9575],{"type":259,"value":1685},{"type":254,"tag":622,"props":9577,"children":9578},{"class":624,"line":1052},[9579],{"type":254,"tag":622,"props":9580,"children":9581},{"style":629},[9582],{"type":259,"value":3574},{"type":254,"tag":622,"props":9584,"children":9585},{"class":624,"line":1078},[9586,9591,9595,9599,9603,9608,9613,9617,9622,9626,9631,9635,9639,9643,9647,9652,9656],{"type":254,"tag":622,"props":9587,"children":9588},{"style":1202},[9589],{"type":259,"value":9590},"        _logger",{"type":254,"tag":622,"props":9592,"children":9593},{"style":629},[9594],{"type":259,"value":594},{"type":254,"tag":622,"props":9596,"children":9597},{"style":1735},[9598],{"type":259,"value":7111},{"type":254,"tag":622,"props":9600,"children":9601},{"style":629},[9602],{"type":259,"value":1668},{"type":254,"tag":622,"props":9604,"children":9605},{"style":629},[9606],{"type":259,"value":9607},"$\"",{"type":254,"tag":622,"props":9609,"children":9610},{"style":664},[9611],{"type":259,"value":9612},"Order contains ",{"type":254,"tag":622,"props":9614,"children":9615},{"style":629},[9616],{"type":259,"value":3234},{"type":254,"tag":622,"props":9618,"children":9619},{"style":1202},[9620],{"type":259,"value":9621},"sentOrder",{"type":254,"tag":622,"props":9623,"children":9624},{"style":629},[9625],{"type":259,"value":594},{"type":254,"tag":622,"props":9627,"children":9628},{"style":1202},[9629],{"type":259,"value":9630},"Count",{"type":254,"tag":622,"props":9632,"children":9633},{"style":629},[9634],{"type":259,"value":2421},{"type":254,"tag":622,"props":9636,"children":9637},{"style":629},[9638],{"type":259,"value":1990},{"type":254,"tag":622,"props":9640,"children":9641},{"style":1202},[9642],{"type":259,"value":9621},{"type":254,"tag":622,"props":9644,"children":9645},{"style":629},[9646],{"type":259,"value":594},{"type":254,"tag":622,"props":9648,"children":9649},{"style":1202},[9650],{"type":259,"value":9651},"Product",{"type":254,"tag":622,"props":9653,"children":9654},{"style":629},[9655],{"type":259,"value":2443},{"type":254,"tag":622,"props":9657,"children":9658},{"style":629},[9659],{"type":259,"value":1554},{"type":254,"tag":622,"props":9661,"children":9662},{"class":624,"line":1087},[9663],{"type":254,"tag":622,"props":9664,"children":9665},{"style":629},[9666],{"type":259,"value":1093},{"type":254,"tag":622,"props":9668,"children":9669},{"class":624,"line":1096},[9670],{"type":254,"tag":622,"props":9671,"children":9672},{"style":629},[9673],{"type":259,"value":1111},{"type":254,"tag":255,"props":9675,"children":9676},{},[9677,9679,9686],{"type":259,"value":9678},"To trigger it, I could simply add a message in the order queue of my ",{"type":254,"tag":262,"props":9680,"children":9683},{"href":9681,"rel":9682},"https://github.com/Azure/Azurite",[266],[9684],{"type":259,"value":9685},"storage emulator",{"type":259,"value":9687}," like this:",{"type":254,"tag":255,"props":9689,"children":9690},{},[9691],{"type":254,"tag":1161,"props":9692,"children":9696},{"alt":9693,"className":9694,"src":9695},"Queue message in Azure Storage Explorer.",[1165,1166],"/posts/images/w042024tips_storage.webp",[],{"type":254,"tag":255,"props":9698,"children":9699},{},[9700],{"type":259,"value":9701},"You may notice that I don't even have to go to the Azure Storage Explorer to add the message, I can do it directly in the IDE. However, call me lazy but I wanted to execute the function just by making an HTTP call, like we do for HTTP-triggered functions.",{"type":254,"tag":255,"props":9703,"children":9704},{},[9705],{"type":259,"value":9706},"This way, I could write the HTTP request in an HTTP file, commit it, and push it to my repository to share it with my colleagues, so they don't have to guess what message they should put in the queue to trigger the function.",{"type":254,"tag":255,"props":9708,"children":9709},{},[9710,9712,9724],{"type":259,"value":9711},"Fortunately, the ",{"type":254,"tag":262,"props":9713,"children":9716},{"href":9714,"rel":9715},"https://learn.microsoft.com/en-us/azure/azure-functions/functions-manually-run-non-http?tabs=azure-portal#define-the-request-location",[266],[9717,9722],{"type":254,"tag":9718,"props":9719,"children":9720},"strong",{},[9721],{"type":259,"value":6340},{"type":259,"value":9723}," explains",{"type":259,"value":9725}," how to do this.",{"type":254,"tag":255,"props":9727,"children":9728},{},[9729],{"type":254,"tag":1161,"props":9730,"children":9734},{"alt":9731,"className":9732,"src":9733},"Define the request location: host name + folder path + function name.",[1165,1166],"/posts/images/w042024tips_function.webp",[],{"type":254,"tag":255,"props":9736,"children":9737},{},[9738],{"type":259,"value":9739},"Thus, for my use case, the resulting request is as follows:",{"type":254,"tag":612,"props":9741,"children":9743},{"className":1184,"code":9742,"language":1186,"meta":205,"style":205},"POST http://localhost:7071/admin/functions/ProcessOrder HTTP/1.1\nContent-Type: application/json\n\n{\n  \"input\": \"{\\n  \\\"product\\\": \\\"laptop\\\",\\n  \\\"count\\\": 3\\n}\"\n}\n",[9744],{"type":254,"tag":389,"props":9745,"children":9746},{"__ignoreMap":205},[9747,9774,9791,9798,9805,9910],{"type":254,"tag":622,"props":9748,"children":9749},{"class":624,"line":625},[9750,9755,9760,9764,9769],{"type":254,"tag":622,"props":9751,"children":9752},{"style":1196},[9753],{"type":259,"value":9754},"POST",{"type":254,"tag":622,"props":9756,"children":9757},{"style":1202},[9758],{"type":259,"value":9759}," http://localhost:7071/admin/functions/ProcessOrder ",{"type":254,"tag":622,"props":9761,"children":9762},{"style":741},[9763],{"type":259,"value":246},{"type":254,"tag":622,"props":9765,"children":9766},{"style":1202},[9767],{"type":259,"value":9768},"/",{"type":254,"tag":622,"props":9770,"children":9771},{"style":741},[9772],{"type":259,"value":9773},"1.1\n",{"type":254,"tag":622,"props":9775,"children":9776},{"class":624,"line":635},[9777,9782,9786],{"type":254,"tag":622,"props":9778,"children":9779},{"style":768},[9780],{"type":259,"value":9781},"Content-Type",{"type":254,"tag":622,"props":9783,"children":9784},{"style":741},[9785],{"type":259,"value":474},{"type":254,"tag":622,"props":9787,"children":9788},{"style":664},[9789],{"type":259,"value":9790}," application/json\n",{"type":254,"tag":622,"props":9792,"children":9793},{"class":624,"line":679},[9794],{"type":254,"tag":622,"props":9795,"children":9796},{"emptyLinePlaceholder":1699},[9797],{"type":259,"value":1702},{"type":254,"tag":622,"props":9799,"children":9800},{"class":624,"line":705},[9801],{"type":254,"tag":622,"props":9802,"children":9803},{"style":629},[9804],{"type":259,"value":632},{"type":254,"tag":622,"props":9806,"children":9807},{"class":624,"line":732},[9808,9812,9817,9821,9825,9829,9833,9838,9843,9848,9853,9858,9862,9867,9871,9875,9879,9883,9888,9892,9897,9901,9905],{"type":254,"tag":622,"props":9809,"children":9810},{"style":629},[9811],{"type":259,"value":641},{"type":254,"tag":622,"props":9813,"children":9814},{"style":644},[9815],{"type":259,"value":9816},"input",{"type":254,"tag":622,"props":9818,"children":9819},{"style":629},[9820],{"type":259,"value":652},{"type":254,"tag":622,"props":9822,"children":9823},{"style":629},[9824],{"type":259,"value":474},{"type":254,"tag":622,"props":9826,"children":9827},{"style":629},[9828],{"type":259,"value":661},{"type":254,"tag":622,"props":9830,"children":9831},{"style":664},[9832],{"type":259,"value":3234},{"type":254,"tag":622,"props":9834,"children":9835},{"style":1202},[9836],{"type":259,"value":9837},"\\n",{"type":254,"tag":622,"props":9839,"children":9840},{"style":1202},[9841],{"type":259,"value":9842},"  \\\"",{"type":254,"tag":622,"props":9844,"children":9845},{"style":664},[9846],{"type":259,"value":9847},"product",{"type":254,"tag":622,"props":9849,"children":9850},{"style":1202},[9851],{"type":259,"value":9852},"\\\"",{"type":254,"tag":622,"props":9854,"children":9855},{"style":664},[9856],{"type":259,"value":9857},": ",{"type":254,"tag":622,"props":9859,"children":9860},{"style":1202},[9861],{"type":259,"value":9852},{"type":254,"tag":622,"props":9863,"children":9864},{"style":664},[9865],{"type":259,"value":9866},"laptop",{"type":254,"tag":622,"props":9868,"children":9869},{"style":1202},[9870],{"type":259,"value":9852},{"type":254,"tag":622,"props":9872,"children":9873},{"style":664},[9874],{"type":259,"value":3149},{"type":254,"tag":622,"props":9876,"children":9877},{"style":1202},[9878],{"type":259,"value":9837},{"type":254,"tag":622,"props":9880,"children":9881},{"style":1202},[9882],{"type":259,"value":9842},{"type":254,"tag":622,"props":9884,"children":9885},{"style":664},[9886],{"type":259,"value":9887},"count",{"type":254,"tag":622,"props":9889,"children":9890},{"style":1202},[9891],{"type":259,"value":9852},{"type":254,"tag":622,"props":9893,"children":9894},{"style":664},[9895],{"type":259,"value":9896},": 3",{"type":254,"tag":622,"props":9898,"children":9899},{"style":1202},[9900],{"type":259,"value":9837},{"type":254,"tag":622,"props":9902,"children":9903},{"style":664},[9904],{"type":259,"value":2421},{"type":254,"tag":622,"props":9906,"children":9907},{"style":629},[9908],{"type":259,"value":9909},"\"\n",{"type":254,"tag":622,"props":9911,"children":9912},{"class":624,"line":759},[9913],{"type":254,"tag":622,"props":9914,"children":9915},{"style":629},[9916],{"type":259,"value":1111},{"type":254,"tag":255,"props":9918,"children":9919},{},[9920,9922,9927],{"type":259,"value":9921},"The content of your queue message goes in the value of the key \"input\" and ",{"type":254,"tag":9718,"props":9923,"children":9924},{},[9925],{"type":259,"value":9926},"must be escaped",{"type":259,"value":594},{"type":254,"tag":427,"props":9929,"children":9931},{"icon":9930},"i-fluent-emoji-flat-construction",[9932],{"type":254,"tag":255,"props":9933,"children":9934},{},[9935],{"type":259,"value":9936},"If like me, you skim through the documentation, you might miss the \"escape\" requirement and your request will fail so be sure to properly escape your content.",{"type":254,"tag":282,"props":9938,"children":9940},{"id":9939},"the-azure-devops-tip-you-did-not-know-about-azure-pipelines-tasks-name-conflicts",[9941],{"type":259,"value":9942},"The Azure DevOps tip you did not know about: Azure Pipelines tasks name conflicts",{"type":254,"tag":255,"props":9944,"children":9945},{},[9946],{"type":259,"value":9947},"I recently discovered that when you install extensions from the Azure DevOps marketplace, several Azure Pipelines tasks can have the same name. And if you use that name in your pipelines, Azure Pipelines won't know which task you are referring to and will prevent your pipeline from running.",{"type":254,"tag":255,"props":9949,"children":9950},{},[9951,9953,9960,9962,9969,9971,9977],{"type":259,"value":9952},"This can easily occur if you install multiple extensions for Terraform in your Azure DevOps organization. For instance, the extensions ",{"type":254,"tag":262,"props":9954,"children":9957},{"href":9955,"rel":9956},"https://marketplace.visualstudio.com/items?itemName=JasonBJohnson.azure-pipelines-tasks-terraform",[266],[9958],{"type":259,"value":9959},"Azure Pipelines Terraform Tasks",{"type":259,"value":9961}," from Jason Johnson and ",{"type":254,"tag":262,"props":9963,"children":9966},{"href":9964,"rel":9965},"https://marketplace.visualstudio.com/items?itemName=ms-devlabs.custom-terraform-tasks",[266],[9967],{"type":259,"value":9968},"Terraform",{"type":259,"value":9970}," from Microsoft Dev Labs both have a task named the same way: ",{"type":254,"tag":389,"props":9972,"children":9974},{"className":9973},[],[9975],{"type":259,"value":9976},"TerraformInstaller",{"type":259,"value":594},{"type":254,"tag":255,"props":9979,"children":9980},{},[9981],{"type":259,"value":9982},"To avoid these conflicts, you must use the full name of the tasks in your pipelines. You can find their full names in the GitHub repository of the extensions. Another way is to use these tasks in a test Release and click on the \"View YAML\" button to see the full name of the task you added.",{"type":254,"tag":255,"props":9984,"children":9985},{},[9986],{"type":254,"tag":1161,"props":9987,"children":9991},{"alt":9988,"className":9989,"src":9990},"Screenshot of a release in Azure DevOps.",[1165,1166],"/posts/images/w042024tips_ado_release.webp",[],{"type":254,"tag":282,"props":9993,"children":9995},{"id":9994},"using-metrics-to-understand-your-usage-of-azure-resources",[9996],{"type":259,"value":9997},"Using metrics to understand your usage of Azure resources",{"type":254,"tag":255,"props":9999,"children":10000},{},[10001],{"type":259,"value":10002},"I don't often use all my monthly free credits of my Azure subscription, but this month my spending limit was quickly reached and my subscription was disabled!",{"type":254,"tag":255,"props":10004,"children":10005},{},[10006],{"type":259,"value":10007},"The cost analysis tab of my subscription showed me that an Azure Maps Account resource was responsible for consuming most of my credits but didn't provide more details.",{"type":254,"tag":255,"props":10009,"children":10010},{},[10011,10013,10020],{"type":259,"value":10012},"So, I went to the Metrics tab of my resource and discovered that I could split the Usage metric by API name to determine exactly which Azure Maps API was heavily used by my applications. Combined with the ",{"type":254,"tag":262,"props":10014,"children":10017},{"href":10015,"rel":10016},"https://azure.microsoft.com/en-us/pricing/details/azure-maps/",[266],[10018],{"type":259,"value":10019},"pricing page",{"type":259,"value":10021},", I can deduce which API requests I'm making too frequently and, therefore how to optimize costs.",{"type":254,"tag":255,"props":10023,"children":10024},{},[10025],{"type":254,"tag":1161,"props":10026,"children":10030},{"alt":10027,"className":10028,"src":10029},"Azure Maps usage metrics by API name.",[1165,1166],"/posts/images/w042024tips_azuremaps_metrics.webp",[],{"type":254,"tag":255,"props":10032,"children":10033},{},[10034],{"type":259,"value":10035},"Depending on the type of resource, you will use different metrics and split on different properties. Regardless, metrics can help you comprehend your resource usage and its associated cost.",{"type":254,"tag":255,"props":10037,"children":10038},{},[10039],{"type":259,"value":10040},"And that's it for this week, happy learning!",{"type":254,"tag":1260,"props":10042,"children":10043},{},[10044],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":10046},[10047,10048,10049],{"id":9270,"depth":635,"text":9273},{"id":9939,"depth":635,"text":9942},{"id":9994,"depth":635,"text":9997},"content:1.posts:58.w04-2024-tips-learned-this-week.md","1.posts/58.w04-2024-tips-learned-this-week.md",{"_path":178,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":177,"description":10053,"lead":10054,"date":10055,"image":10056,"badge":10058,"tags":10060,"ImageAttribution":10063,"body":10064,"_type":1275,"_id":10617,"_source":231,"_file":10618,"_extension":1278},"Last year, I wrote my first annual retrospective. It was an interesting exercise that I intend to do every year. So for 2023, here is my year in review.","Challenges and achievements of 2023","2024-01-02T00:00:00.000Z",{"src":10057},"/images/review_1.jpg",{"label":10059},"Essay",[10061,10062],"thoughts","learning","Picture of \u003Ca href=\"https://unsplash.com/fr/@markuswinkler?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Markus Winkler\u003C/a> on \u003Ca href=\"https://unsplash.com/fr/photos/-fRAIQHKcc0?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Unsplash\u003C/a>",{"type":251,"children":10065,"toc":10609},[10066,10080,10086,10099,10132,10137,10170,10175,10181,10186,10214,10228,10237,10260,10265,10281,10286,10291,10297,10302,10307,10316,10321,10334,10364,10387,10396,10410,10416,10421,10444,10449,10454,10463,10486,10491,10570,10576,10581,10586,10604],{"type":254,"tag":255,"props":10067,"children":10068},{},[10069,10071,10078],{"type":259,"value":10070},"Last year, I wrote my ",{"type":254,"tag":262,"props":10072,"children":10075},{"href":10073,"rel":10074},"https://www.techwatching.dev/posts/2022-retro",[266],[10076],{"type":259,"value":10077},"first annual retrospective",{"type":259,"value":10079},". It was an interesting exercise that I intend to do every year. So for 2023, here is my year in review.",{"type":254,"tag":282,"props":10081,"children":10083},{"id":10082},"plans-for-2023-versus-reality",[10084],{"type":259,"value":10085},"Plans for 2023 versus reality",{"type":254,"tag":255,"props":10087,"children":10088},{},[10089,10091,10097],{"type":259,"value":10090},"My plans for 2023 that I shared in ",{"type":254,"tag":262,"props":10092,"children":10094},{"href":10073,"rel":10093},[266],[10095],{"type":259,"value":10096},"my 2022 retro",{"type":259,"value":10098}," were to:",{"type":254,"tag":332,"props":10100,"children":10101},{},[10102,10107,10112,10117,10122,10127],{"type":254,"tag":336,"props":10103,"children":10104},{},[10105],{"type":259,"value":10106},"keep learning about Vue.js and Nuxt.js",{"type":254,"tag":336,"props":10108,"children":10109},{},[10110],{"type":259,"value":10111},"explore Azure Container Apps and Dapr",{"type":254,"tag":336,"props":10113,"children":10114},{},[10115],{"type":259,"value":10116},"keep writing articles on my blog about topics I am interested in",{"type":254,"tag":336,"props":10118,"children":10119},{},[10120],{"type":259,"value":10121},"keep sharing links and tips on social networks",{"type":254,"tag":336,"props":10123,"children":10124},{},[10125],{"type":259,"value":10126},"improve my use of PKM tools like Obsidian",{"type":254,"tag":336,"props":10128,"children":10129},{},[10130],{"type":259,"value":10131},"give at least 1 talk at a developer conference",{"type":254,"tag":255,"props":10133,"children":10134},{},[10135],{"type":259,"value":10136},"I must admit that I didn't fully achieve my goals:",{"type":254,"tag":332,"props":10138,"children":10139},{},[10140,10145,10150,10155,10160,10165],{"type":254,"tag":336,"props":10141,"children":10142},{},[10143],{"type":259,"value":10144},"I continued learning Vue.js and Nuxt.js but not as extensively as I would have hoped",{"type":254,"tag":336,"props":10146,"children":10147},{},[10148],{"type":259,"value":10149},"I didn't dive deeply into Azure Container Apps and Dapr although I did experiment with them a bit",{"type":254,"tag":336,"props":10151,"children":10152},{},[10153],{"type":259,"value":10154},"I wrote articles on my blog but fewer than in the previous years",{"type":254,"tag":336,"props":10156,"children":10157},{},[10158],{"type":259,"value":10159},"I shared links and tips on social networks but not consistently",{"type":254,"tag":336,"props":10161,"children":10162},{},[10163],{"type":259,"value":10164},"I took my notes using Obsidian, but I haven't utilized it as a true PKM tool",{"type":254,"tag":336,"props":10166,"children":10167},{},[10168],{"type":259,"value":10169},"I did give several talks at various developer conferences (more on that later)",{"type":254,"tag":255,"props":10171,"children":10172},{},[10173],{"type":259,"value":10174},"It's not a big deal that I didn't accomplish everything. My primary goal for 2023 was for it to be a year of learning and sharing, just like in 2022. And I succeeded in doing that. 2023 was another year of learning and sharing, and it also provided numerous speaking opportunities. This is one of the reasons why I didn't have the time to do everything I planned.",{"type":254,"tag":282,"props":10176,"children":10178},{"id":10177},"public-speaking",[10179],{"type":259,"value":10180},"Public speaking",{"type":254,"tag":255,"props":10182,"children":10183},{},[10184],{"type":259,"value":10185},"In 2022, I gave my first talk at a developer conference (online). In 2023, I had the opportunity to speaker at five French developer conferences:",{"type":254,"tag":332,"props":10187,"children":10188},{},[10189,10194,10199,10204,10209],{"type":254,"tag":336,"props":10190,"children":10191},{},[10192],{"type":259,"value":10193},"Global Azure France in Paris - May 2023",{"type":254,"tag":336,"props":10195,"children":10196},{},[10197],{"type":259,"value":10198},"Cloud Est in Lyon - June 2023",{"type":254,"tag":336,"props":10200,"children":10201},{},[10202],{"type":259,"value":10203},"Breizh Camp in Rennes - June 2023",{"type":254,"tag":336,"props":10205,"children":10206},{},[10207],{"type":259,"value":10208},"BDX I/O in Bordeaux - November 2023",{"type":254,"tag":336,"props":10210,"children":10211},{},[10212],{"type":259,"value":10213},".NET Conf 2023 with MTG (online) - December 2023",{"type":254,"tag":255,"props":10215,"children":10216},{},[10217,10219,10226],{"type":259,"value":10218},"The first three talks focused on Infrastructure as Code in general, and more specifically on ",{"type":254,"tag":262,"props":10220,"children":10223},{"href":10221,"rel":10222},"https://www.pulumi.com/",[266],[10224],{"type":259,"value":10225},"Pulumi",{"type":259,"value":10227},". I particularly enjoyed speaking at Breizh Camp, as many people attended my talk 🥰 and the organization was excellent.",{"type":254,"tag":255,"props":10229,"children":10230},{},[10231],{"type":254,"tag":1161,"props":10232,"children":10236},{"alt":10233,"className":10234,"src":10235},"Screenshot of talk record at Breizh Camp",[1165,1166],"/posts/images/2022_retro_talk.webp",[],{"type":254,"tag":255,"props":10238,"children":10239},{},[10240,10242,10249,10251,10258],{"type":259,"value":10241},"The fourth talk showcased ",{"type":254,"tag":262,"props":10243,"children":10246},{"href":10244,"rel":10245},"https://sli.dev/",[266],[10247],{"type":259,"value":10248},"slidev",{"type":259,"value":10250},", a tool for developers by ",{"type":254,"tag":262,"props":10252,"children":10255},{"href":10253,"rel":10254},"https://antfu.me/",[266],[10256],{"type":259,"value":10257},"Anthony Fu",{"type":259,"value":10259}," that allows creating slides in markdown (and using web technologies). It was a 15-minute talk titled \"Oops, I Forgot to Make My Slides,\" during which a friend helped me create my slides about Vue 3 on stage. It was an incredibly fun talk to prepare and deliver. I cannot thank my co-speaker Xavier Noya enough for agreeing to do this talk with me. Additionally, I was delighted to be a speaker at this fantastic conference that takes place in my hometown.",{"type":254,"tag":255,"props":10261,"children":10262},{},[10263],{"type":259,"value":10264},"The last talk was online (for a French event related to the .NET Conf 2023). It concentrated on the new features of C# 12 and .NET 8, and demonstrated how to implement Infrastructure as Code (IaC) using .NET.",{"type":254,"tag":427,"props":10266,"children":10268},{"icon":10267},"i-heroicons-film",[10269],{"type":254,"tag":255,"props":10270,"children":10271},{},[10272,10274],{"type":259,"value":10273},"The talks I gave are all in French, but if you are interested some of them have been ",{"type":254,"tag":262,"props":10275,"children":10278},{"href":10276,"rel":10277},"https://drp.li/f7I9N",[266],[10279],{"type":259,"value":10280},"recorded",{"type":254,"tag":255,"props":10282,"children":10283},{},[10284],{"type":259,"value":10285},"I am incredibly proud of the numerous speaking opportunities I've had. While it may not seem impressive to experienced speakers, it means a lot to me. I am truly grateful for the chance to speak at these events and to have attended some fantastic talks as well.",{"type":254,"tag":255,"props":10287,"children":10288},{},[10289],{"type":259,"value":10290},"Developer conferences' Call for Papers are highly selective, and they always receive many excellent proposals. Thus, I have no idea if I will be able to speak at multiple conferences in 2024, but I will certainly do my best.",{"type":254,"tag":282,"props":10292,"children":10294},{"id":10293},"blogging",[10295],{"type":259,"value":10296},"Blogging",{"type":254,"tag":255,"props":10298,"children":10299},{},[10300],{"type":259,"value":10301},"In 2023, I \"only\" wrote 11 articles on my blog, which is fewer than the 15 articles I wrote in 2022 and significantly less than the 19 articles in 2021.",{"type":254,"tag":255,"props":10303,"children":10304},{},[10305],{"type":259,"value":10306},"For some of my articles, I created a GitHub repository with the code samples used in the article. That's something I intend to do more.",{"type":254,"tag":255,"props":10308,"children":10309},{},[10310],{"type":254,"tag":1161,"props":10311,"children":10315},{"alt":10312,"className":10313,"src":10314},"Example of GitHub repository sample code for article.",[1165,1166],"/posts/images/2022_retro_github.webp",[],{"type":254,"tag":255,"props":10317,"children":10318},{},[10319],{"type":259,"value":10320},"My blog's traffic decreased a little (not enough articles this year I guess):",{"type":254,"tag":332,"props":10322,"children":10323},{},[10324,10329],{"type":254,"tag":336,"props":10325,"children":10326},{},[10327],{"type":259,"value":10328},"27K users vs 28K",{"type":254,"tag":336,"props":10330,"children":10331},{},[10332],{"type":259,"value":10333},"27K pages seen vs 37K",{"type":254,"tag":255,"props":10335,"children":10336},{},[10337,10339,10346,10348,10355,10357,10363],{"type":259,"value":10338},"I kept cross-posting all my articles on ",{"type":254,"tag":262,"props":10340,"children":10343},{"href":10341,"rel":10342},"https://techwatching.hashnode.dev/",[266],[10344],{"type":259,"value":10345},"Hashnode",{"type":259,"value":10347}," and ",{"type":254,"tag":262,"props":10349,"children":10352},{"href":10350,"rel":10351},"https://dzone.com/users/4682620/techwatching.html",[266],[10353],{"type":259,"value":10354},"dev.to",{"type":259,"value":10356},", and published two of them on ",{"type":254,"tag":262,"props":10358,"children":10360},{"href":10350,"rel":10359},[266],[10361],{"type":259,"value":10362},"DZone",{"type":259,"value":594},{"type":254,"tag":255,"props":10365,"children":10366},{},[10367,10369,10376,10378,10385],{"type":259,"value":10368},"Together with a friend, we initiated a ",{"type":254,"tag":262,"props":10370,"children":10373},{"href":10371,"rel":10372},"https://bordeauxcoders.com/series/pnpm-101",[266],[10374],{"type":259,"value":10375},"blog post series about pnpm",{"type":259,"value":10377}," on a new team blog called \"",{"type":254,"tag":262,"props":10379,"children":10382},{"href":10380,"rel":10381},"https://bordeauxcoders.com/",[266],[10383],{"type":259,"value":10384},"Bordeaux Coders",{"type":259,"value":10386},"\". It was enjoyable, but our motivation waned after the summer. We need to regain our motivation, start writing again, and perhaps find others interested in collaborating on this blog.",{"type":254,"tag":255,"props":10388,"children":10389},{},[10390],{"type":254,"tag":1161,"props":10391,"children":10395},{"alt":10392,"className":10393,"src":10394},"Screenshot of th Bordeaux Coders' blog",[1165,1166],"/posts/images/2022_retro_blog.webp",[],{"type":254,"tag":255,"props":10397,"children":10398},{},[10399,10401,10408],{"type":259,"value":10400},"I have also co-authored an ",{"type":254,"tag":262,"props":10402,"children":10405},{"href":10403,"rel":10404},"https://www.avanade.com/fr-fr/blogs/le-blog/life-at-avanade/notre-expertise-au-service-des-nouvelles-generations",[266],[10406],{"type":259,"value":10407},"article on my company's blog",{"type":259,"value":10409}," about something I have been doing for 5 years now: overseeing student projects at my former engineering school.",{"type":254,"tag":282,"props":10411,"children":10413},{"id":10412},"school-relationships-and-teaching",[10414],{"type":259,"value":10415},"School Relationships and Teaching",{"type":254,"tag":255,"props":10417,"children":10418},{},[10419],{"type":259,"value":10420},"As I mentioned, this year I once again supervised a group of students on a small software development project over a few months. This experience provided the opportunity to:",{"type":254,"tag":332,"props":10422,"children":10423},{},[10424,10429,10434,10439],{"type":254,"tag":336,"props":10425,"children":10426},{},[10427],{"type":259,"value":10428},"Explore new tools and technologies",{"type":254,"tag":336,"props":10430,"children":10431},{},[10432],{"type":259,"value":10433},"Share knowledge with students and learn from them as well",{"type":254,"tag":336,"props":10435,"children":10436},{},[10437],{"type":259,"value":10438},"Grow (by wearing different hats and utilizing educational management skills)",{"type":254,"tag":336,"props":10440,"children":10441},{},[10442],{"type":259,"value":10443},"Promote my company's expertise and attract future talent",{"type":254,"tag":255,"props":10445,"children":10446},{},[10447],{"type":259,"value":10448},"For the first time, in 2023, I taught a DevOps course at the same engineering school. It was an optional module on DevOps practices for 2nd-year students.",{"type":254,"tag":255,"props":10450,"children":10451},{},[10452],{"type":259,"value":10453},"Building relationships with schools takes time and isn't always easy, but I enjoy doing it (and I'm not alone, as I have colleagues who help me). Unfortunately, I am uncertain whether my company will continue supporting me in this area next year, so I don't know what I will be able to do in 2024.",{"type":254,"tag":282,"props":10455,"children":10457},{"id":10456},"whats-next",[10458],{"type":254,"tag":9718,"props":10459,"children":10460},{},[10461],{"type":259,"value":10462},"What's next?",{"type":254,"tag":255,"props":10464,"children":10465},{},[10466,10468,10475,10477,10484],{"type":259,"value":10467},"Together with two friends, we have started a tech community called \"",{"type":254,"tag":262,"props":10469,"children":10472},{"href":10470,"rel":10471},"https://www.meetup.com/mtg-bordeaux/",[266],[10473],{"type":259,"value":10474},"MTG:Bordeaux",{"type":259,"value":10476},",\" which will host meetups in Bordeaux to discuss Microsoft technologies (among others) several times a year. It is affiliated with ",{"type":254,"tag":262,"props":10478,"children":10481},{"href":10479,"rel":10480},"https://www.mtg-france.org/",[266],[10482],{"type":259,"value":10483},"MTG:France",{"type":259,"value":10485},", which already encompasses numerous local communities in various French cities. The inaugural meetup is scheduled for February 1, 2024, and I hope it will be the first of many.",{"type":254,"tag":255,"props":10487,"children":10488},{},[10489],{"type":259,"value":10490},"Instead of setting vague plans for 2024 that I might not fully achieve, I prefer creating a list of small, tangible goals for the year. I know I won't be able to accomplish all of them, but it will provide me with achievable objectives to work on throughout the year:",{"type":254,"tag":332,"props":10492,"children":10493},{},[10494,10499,10504,10509,10514,10519,10538,10543,10548,10560,10565],{"type":254,"tag":336,"props":10495,"children":10496},{},[10497],{"type":259,"value":10498},"Organize 3 meetups for MTG:Bordeaux",{"type":254,"tag":336,"props":10500,"children":10501},{},[10502],{"type":259,"value":10503},"Obtain the official Vue.js certification",{"type":254,"tag":336,"props":10505,"children":10506},{},[10507],{"type":259,"value":10508},"Create a small speaker website in Nuxt, listing my previous talks",{"type":254,"tag":336,"props":10510,"children":10511},{},[10512],{"type":259,"value":10513},"Build a small application using Dapr and running in Azure Container Apps",{"type":254,"tag":336,"props":10515,"children":10516},{},[10517],{"type":259,"value":10518},"Write a blog post about Obsidian",{"type":254,"tag":336,"props":10520,"children":10521},{},[10522,10524,10531,10533],{"type":259,"value":10523},"Write 2 articles for the ",{"type":254,"tag":262,"props":10525,"children":10528},{"href":10526,"rel":10527},"https://bordeauxcoders.com/series/vuejs-cicd",[266],[10529],{"type":259,"value":10530},"Vue CI/CD series",{"type":259,"value":10532}," on ",{"type":254,"tag":262,"props":10534,"children":10536},{"href":10380,"rel":10535},[266],[10537],{"type":259,"value":10384},{"type":254,"tag":336,"props":10539,"children":10540},{},[10541],{"type":259,"value":10542},"Present at least 2 different talks at developer conferences",{"type":254,"tag":336,"props":10544,"children":10545},{},[10546],{"type":259,"value":10547},"Reach 1K followers on LinkedIn",{"type":254,"tag":336,"props":10549,"children":10550},{},[10551,10553],{"type":259,"value":10552},"Add missing sections to the ",{"type":254,"tag":262,"props":10554,"children":10557},{"href":10555,"rel":10556},"https://github.com/TechWatching/pulumi-azure-workshop",[266],[10558],{"type":259,"value":10559},"Pulumi Azure Workshop",{"type":254,"tag":336,"props":10561,"children":10562},{},[10563],{"type":259,"value":10564},"Develop a 1-day Pulumi training course",{"type":254,"tag":336,"props":10566,"children":10567},{},[10568],{"type":259,"value":10569},"Create a YouTube video about a developer tool or technology",{"type":254,"tag":282,"props":10571,"children":10573},{"id":10572},"to-conclude",[10574],{"type":259,"value":10575},"To conclude",{"type":254,"tag":255,"props":10577,"children":10578},{},[10579],{"type":259,"value":10580},"Despite not fully achieving my goals, 2023 was an interesting year, especially regarding public speaking. Looking ahead to 2024, I have outlined a series of concrete goals that emphasize continuous learning and community involvement.",{"type":254,"tag":255,"props":10582,"children":10583},{},[10584],{"type":259,"value":10585},"As I close the 2023 chapter, I want to thank 3 people:",{"type":254,"tag":332,"props":10587,"children":10588},{},[10589,10594,10599],{"type":254,"tag":336,"props":10590,"children":10591},{},[10592],{"type":259,"value":10593},"Christian Bonnaud - you played a role in many aspects I mentioned in this article (school relationships, blogging, tech community, ...), and it's always a pleasure to collaborate with you.",{"type":254,"tag":336,"props":10595,"children":10596},{},[10597],{"type":259,"value":10598},"Xavier Noya - it was nice to give a talk alongside you this year at BDX I/O",{"type":254,"tag":336,"props":10600,"children":10601},{},[10602],{"type":259,"value":10603},"My life partner - I wouldn't be able to write these articles, prepare these talks, or accomplish everything I do without your support and understanding",{"type":254,"tag":255,"props":10605,"children":10606},{},[10607],{"type":259,"value":10608},"Enjoy 2024, and keep learning.",{"title":205,"searchDepth":635,"depth":635,"links":10610},[10611,10612,10613,10614,10615,10616],{"id":10082,"depth":635,"text":10085},{"id":10177,"depth":635,"text":10180},{"id":10293,"depth":635,"text":10296},{"id":10412,"depth":635,"text":10415},{"id":10456,"depth":635,"text":10462},{"id":10572,"depth":635,"text":10575},"content:1.posts:57.2023-retro.md","1.posts/57.2023-retro.md",{"_path":175,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":174,"description":10620,"lead":10621,"date":10622,"image":10623,"badge":10625,"tags":10626,"body":10629,"_type":1275,"_id":16340,"_source":231,"_file":16341,"_extension":1278},"In this article, we will explore the latest C# 12 and .NET 8 features by applying them to the basic dotnet Web API template.","Enhanced weather forecast API","2023-12-19T00:00:00.000Z",{"src":10624},"/images/weather_flag.webp",{"label":1286},[1425,1288,10627,246,10628],"ASP.NET Core","advent",{"type":251,"children":10630,"toc":16335},[10631,10635,10641,10654,10686,10691,10700,10705,10739,10752,10761,10774,10834,10855,10871,10890,10899,10905,10910,10915,10957,10966,10979,11335,11363,11521,11533,11562,12695,12700,12855,12876,13395,13405,13620,13634,13838,13843,14105,14110,14121,14441,14449,14462,14692,14720,14725,14854,14859,14991,15004,15330,15342,15365,15669,15688,16093,16099,16104,16106,16268,16296,16317,16331],{"type":254,"tag":255,"props":10632,"children":10633},{},[10634],{"type":259,"value":10620},{"type":254,"tag":282,"props":10636,"children":10638},{"id":10637},"getting-started-with-the-aspnet-core-web-api-template",[10639],{"type":259,"value":10640},"Getting started with the ASP.NET Core Web API template",{"type":254,"tag":255,"props":10642,"children":10643},{},[10644,10646,10653],{"type":259,"value":10645},"First, let's install the latest ",{"type":254,"tag":262,"props":10647,"children":10650},{"href":10648,"rel":10649},"https://dotnet.microsoft.com/en-us/download/dotnet/8.0",[266],[10651],{"type":259,"value":10652},".NET 8 SDK",{"type":259,"value":474},{"type":254,"tag":612,"props":10655,"children":10659},{"className":10656,"code":10657,"language":10658,"meta":205,"style":205},"language-powershell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","winget install --id Microsoft.DotNet.SDK.8\n","powershell",[10660],{"type":254,"tag":389,"props":10661,"children":10662},{"__ignoreMap":205},[10663],{"type":254,"tag":622,"props":10664,"children":10665},{"class":624,"line":625},[10666,10671,10676,10681],{"type":254,"tag":622,"props":10667,"children":10668},{"style":1202},[10669],{"type":259,"value":10670},"winget install ",{"type":254,"tag":622,"props":10672,"children":10673},{"style":629},[10674],{"type":259,"value":10675},"--",{"type":254,"tag":622,"props":10677,"children":10678},{"style":1202},[10679],{"type":259,"value":10680},"id Microsoft.DotNet.SDK.",{"type":254,"tag":622,"props":10682,"children":10683},{"style":741},[10684],{"type":259,"value":10685},"8\n",{"type":254,"tag":255,"props":10687,"children":10688},{},[10689],{"type":259,"value":10690},"We can list the available templates:",{"type":254,"tag":255,"props":10692,"children":10693},{},[10694],{"type":254,"tag":1161,"props":10695,"children":10699},{"alt":10696,"className":10697,"src":10698},"List of the available dotnet templates",[1165,1166],"/posts/images/dontnet8_templates.webp",[],{"type":254,"tag":255,"props":10701,"children":10702},{},[10703],{"type":259,"value":10704},"Let's go for the basic ASP.NET Core Web API template but with the controllers:",{"type":254,"tag":612,"props":10706,"children":10708},{"className":10656,"code":10707,"language":10658,"meta":205,"style":205},"dotnet new webapi --use-controllers -n WeatherApi\n",[10709],{"type":254,"tag":389,"props":10710,"children":10711},{"__ignoreMap":205},[10712],{"type":254,"tag":622,"props":10713,"children":10714},{"class":624,"line":625},[10715,10720,10724,10729,10734],{"type":254,"tag":622,"props":10716,"children":10717},{"style":1202},[10718],{"type":259,"value":10719},"dotnet new webapi ",{"type":254,"tag":622,"props":10721,"children":10722},{"style":629},[10723],{"type":259,"value":10675},{"type":254,"tag":622,"props":10725,"children":10726},{"style":1735},[10727],{"type":259,"value":10728},"use-controllers",{"type":254,"tag":622,"props":10730,"children":10731},{"style":629},[10732],{"type":259,"value":10733}," -",{"type":254,"tag":622,"props":10735,"children":10736},{"style":1202},[10737],{"type":259,"value":10738},"n WeatherApi\n",{"type":254,"tag":427,"props":10740,"children":10741},{"icon":567},[10742],{"type":254,"tag":255,"props":10743,"children":10744},{},[10745,10750],{"type":254,"tag":622,"props":10746,"children":10747},{},[10748],{"type":259,"value":10749},"Minimal APIs",{"type":259,"value":10751}," are great too but having controllers is more suited to what I want to show in this article.",{"type":254,"tag":255,"props":10753,"children":10754},{},[10755],{"type":254,"tag":1161,"props":10756,"children":10760},{"alt":10757,"className":10758,"src":10759},"Screenshot of the generated project in Rider",[1165,1166],"/posts/images/dontnet8_webapi_template.webp",[],{"type":254,"tag":255,"props":10762,"children":10763},{},[10764,10766,10772],{"type":259,"value":10765},"We can run the API and test the ",{"type":254,"tag":389,"props":10767,"children":10769},{"className":10768},[],[10770],{"type":259,"value":10771},"GET /weatherforecast",{"type":259,"value":10773}," endpoint using the generated request file:",{"type":254,"tag":612,"props":10775,"children":10777},{"className":1184,"code":10776,"language":1186,"meta":205,"style":205},"@WeatherApi_HostAddress = http://localhost:5103\n\nGET {{WeatherApi_HostAddress}}/weatherforecast/\nAccept: application/json\n",[10778],{"type":254,"tag":389,"props":10779,"children":10780},{"__ignoreMap":205},[10781,10799,10806,10818],{"type":254,"tag":622,"props":10782,"children":10783},{"class":624,"line":625},[10784,10789,10794],{"type":254,"tag":622,"props":10785,"children":10786},{"style":741},[10787],{"type":259,"value":10788},"@",{"type":254,"tag":622,"props":10790,"children":10791},{"style":1202},[10792],{"type":259,"value":10793},"WeatherApi_HostAddress = ",{"type":254,"tag":622,"props":10795,"children":10796},{"style":664},[10797],{"type":259,"value":10798},"http://localhost:5103\n",{"type":254,"tag":622,"props":10800,"children":10801},{"class":624,"line":635},[10802],{"type":254,"tag":622,"props":10803,"children":10804},{"emptyLinePlaceholder":1699},[10805],{"type":259,"value":1702},{"type":254,"tag":622,"props":10807,"children":10808},{"class":624,"line":679},[10809,10813],{"type":254,"tag":622,"props":10810,"children":10811},{"style":1196},[10812],{"type":259,"value":1199},{"type":254,"tag":622,"props":10814,"children":10815},{"style":1202},[10816],{"type":259,"value":10817}," {{WeatherApi_HostAddress}}/weatherforecast/\n",{"type":254,"tag":622,"props":10819,"children":10820},{"class":624,"line":705},[10821,10826,10830],{"type":254,"tag":622,"props":10822,"children":10823},{"style":768},[10824],{"type":259,"value":10825},"Accept",{"type":254,"tag":622,"props":10827,"children":10828},{"style":741},[10829],{"type":259,"value":474},{"type":254,"tag":622,"props":10831,"children":10832},{"style":664},[10833],{"type":259,"value":9790},{"type":254,"tag":255,"props":10835,"children":10836},{},[10837,10839,10845,10847,10854],{"type":259,"value":10838},"This is included in the dotnet ",{"type":254,"tag":389,"props":10840,"children":10842},{"className":10841},[],[10843],{"type":259,"value":10844},"webapi",{"type":259,"value":10846}," template and is supported by Visual Studio, Rider, and vscode (using the ",{"type":254,"tag":262,"props":10848,"children":10851},{"href":10849,"rel":10850},"https://marketplace.visualstudio.com/items?itemName=humao.rest-client",[266],[10852],{"type":259,"value":10853},"REST Client extension",{"type":259,"value":2309},{"type":254,"tag":427,"props":10856,"children":10857},{"icon":567},[10858],{"type":254,"tag":255,"props":10859,"children":10860},{},[10861,10863,10869],{"type":259,"value":10862},"Read my article about ",{"type":254,"tag":262,"props":10864,"children":10866},{"href":264,"rel":10865},[266],[10867],{"type":259,"value":10868},"choosing an API Client",{"type":259,"value":10870}," and why I prefer versioned HTTP files rather than GUI tools like Postman.",{"type":254,"tag":255,"props":10872,"children":10873},{},[10874,10876,10889],{"type":259,"value":10875},"If we put a breakpoint in the controller we can see one small ASP.NET 8 improvement concerning the debugging experience: ",{"type":254,"tag":262,"props":10877,"children":10880},{"href":10878,"rel":10879},"https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-8.0?view=aspnetcore-8.0#improved-debugging-experience",[266],[10881,10883],{"type":259,"value":10882},"better debug summaries are displayed for types like ",{"type":254,"tag":389,"props":10884,"children":10886},{"className":10885},[],[10887],{"type":259,"value":10888},"HttpContext",{"type":259,"value":594},{"type":254,"tag":255,"props":10891,"children":10892},{},[10893],{"type":254,"tag":1161,"props":10894,"children":10898},{"alt":10895,"className":10896,"src":10897},"Debugging display of the HTTPContext class",[1165,1166],"/posts/images/dontnet8_httpcontext.webp",[],{"type":254,"tag":282,"props":10900,"children":10902},{"id":10901},"enhancing-the-weather-forecast-api",[10903],{"type":259,"value":10904},"Enhancing the Weather Forecast API",{"type":254,"tag":255,"props":10906,"children":10907},{},[10908],{"type":259,"value":10909},"Currently, the template randomly generates weather forecasts in the controller. It would be nice to retrieve real weather data from a weather API.",{"type":254,"tag":255,"props":10911,"children":10912},{},[10913],{"type":259,"value":10914},"To do that we can:",{"type":254,"tag":332,"props":10916,"children":10917},{},[10918,10931,10944],{"type":254,"tag":336,"props":10919,"children":10920},{},[10921,10923,10929],{"type":259,"value":10922},"introduce an ",{"type":254,"tag":389,"props":10924,"children":10926},{"className":10925},[],[10927],{"type":259,"value":10928},"IWeatherService",{"type":259,"value":10930}," interface that contains a method to retrieve weather forecasts",{"type":254,"tag":336,"props":10932,"children":10933},{},[10934,10936,10942],{"type":259,"value":10935},"extract the current logic that generates the random weather forecasts in a ",{"type":254,"tag":389,"props":10937,"children":10939},{"className":10938},[],[10940],{"type":259,"value":10941},"RandomWeatherService.cs",{"type":259,"value":10943}," that implements this interface",{"type":254,"tag":336,"props":10945,"children":10946},{},[10947,10949,10955],{"type":259,"value":10948},"creates a new implementation ",{"type":254,"tag":389,"props":10950,"children":10952},{"className":10951},[],[10953],{"type":259,"value":10954},"OpenWeatherService",{"type":259,"value":10956}," of this interface that retrieves the weather data from the Open Weather Map API",{"type":254,"tag":255,"props":10958,"children":10959},{},[10960],{"type":254,"tag":1161,"props":10961,"children":10965},{"alt":10962,"className":10963,"src":10964},"A diagram of the ASP.NETCore Weather API",[1165,1166],"/posts/images/dontnet8_webapi_diagram.webp",[],{"type":254,"tag":255,"props":10967,"children":10968},{},[10969,10971,10977],{"type":259,"value":10970},"The ",{"type":254,"tag":389,"props":10972,"children":10974},{"className":10973},[],[10975],{"type":259,"value":10976},"WeatherForecastController",{"type":259,"value":10978}," becomes:",{"type":254,"tag":612,"props":10980,"children":10982},{"className":1423,"code":10981,"language":1425,"meta":205,"style":205},"[ApiController]\n[Route(\"[controller]\")]\npublic class WeatherForecastController : ControllerBase\n{\n    private readonly IWeatherService _weatherService;\n\n    public WeatherForecastController(IWeatherService weatherService)\n    {\n        _weatherService = weatherService;\n    }\n\n    [HttpGet(Name = \"GetWeatherForecast\")]\n    [ProducesResponseType(typeof(WeatherForecast), StatusCodes.Status200OK)]\n    public Task\u003CWeatherForecast[]> Get()\n    {\n        return _weatherService.GetWeatherForecasts();\n    }\n}\n",[10983],{"type":254,"tag":389,"props":10984,"children":10985},{"__ignoreMap":205},[10986,11004,11038,11063,11070,11095,11102,11130,11137,11157,11164,11171,11212,11257,11290,11297,11321,11328],{"type":254,"tag":622,"props":10987,"children":10988},{"class":624,"line":625},[10989,10994,10999],{"type":254,"tag":622,"props":10990,"children":10991},{"style":629},[10992],{"type":259,"value":10993},"[",{"type":254,"tag":622,"props":10995,"children":10996},{"style":714},[10997],{"type":259,"value":10998},"ApiController",{"type":254,"tag":622,"props":11000,"children":11001},{"style":629},[11002],{"type":259,"value":11003},"]\n",{"type":254,"tag":622,"props":11005,"children":11006},{"class":624,"line":635},[11007,11011,11016,11020,11024,11029,11033],{"type":254,"tag":622,"props":11008,"children":11009},{"style":629},[11010],{"type":259,"value":10993},{"type":254,"tag":622,"props":11012,"children":11013},{"style":714},[11014],{"type":259,"value":11015},"Route",{"type":254,"tag":622,"props":11017,"children":11018},{"style":629},[11019],{"type":259,"value":1668},{"type":254,"tag":622,"props":11021,"children":11022},{"style":629},[11023],{"type":259,"value":652},{"type":254,"tag":622,"props":11025,"children":11026},{"style":664},[11027],{"type":259,"value":11028},"[controller]",{"type":254,"tag":622,"props":11030,"children":11031},{"style":629},[11032],{"type":259,"value":652},{"type":254,"tag":622,"props":11034,"children":11035},{"style":629},[11036],{"type":259,"value":11037},")]\n",{"type":254,"tag":622,"props":11039,"children":11040},{"class":624,"line":679},[11041,11045,11049,11054,11058],{"type":254,"tag":622,"props":11042,"children":11043},{"style":644},[11044],{"type":259,"value":1437},{"type":254,"tag":622,"props":11046,"children":11047},{"style":714},[11048],{"type":259,"value":6761},{"type":254,"tag":622,"props":11050,"children":11051},{"style":714},[11052],{"type":259,"value":11053}," WeatherForecastController",{"type":254,"tag":622,"props":11055,"children":11056},{"style":629},[11057],{"type":259,"value":3274},{"type":254,"tag":622,"props":11059,"children":11060},{"style":714},[11061],{"type":259,"value":11062}," ControllerBase\n",{"type":254,"tag":622,"props":11064,"children":11065},{"class":624,"line":705},[11066],{"type":254,"tag":622,"props":11067,"children":11068},{"style":629},[11069],{"type":259,"value":632},{"type":254,"tag":622,"props":11071,"children":11072},{"class":624,"line":732},[11073,11077,11081,11086,11091],{"type":254,"tag":622,"props":11074,"children":11075},{"style":644},[11076],{"type":259,"value":6790},{"type":254,"tag":622,"props":11078,"children":11079},{"style":644},[11080],{"type":259,"value":6795},{"type":254,"tag":622,"props":11082,"children":11083},{"style":714},[11084],{"type":259,"value":11085}," IWeatherService",{"type":254,"tag":622,"props":11087,"children":11088},{"style":714},[11089],{"type":259,"value":11090}," _weatherService",{"type":254,"tag":622,"props":11092,"children":11093},{"style":629},[11094],{"type":259,"value":2350},{"type":254,"tag":622,"props":11096,"children":11097},{"class":624,"line":759},[11098],{"type":254,"tag":622,"props":11099,"children":11100},{"emptyLinePlaceholder":1699},[11101],{"type":259,"value":1702},{"type":254,"tag":622,"props":11103,"children":11104},{"class":624,"line":798},[11105,11109,11113,11117,11121,11126],{"type":254,"tag":622,"props":11106,"children":11107},{"style":644},[11108],{"type":259,"value":1970},{"type":254,"tag":622,"props":11110,"children":11111},{"style":1735},[11112],{"type":259,"value":11053},{"type":254,"tag":622,"props":11114,"children":11115},{"style":629},[11116],{"type":259,"value":1668},{"type":254,"tag":622,"props":11118,"children":11119},{"style":714},[11120],{"type":259,"value":10928},{"type":254,"tag":622,"props":11122,"children":11123},{"style":714},[11124],{"type":259,"value":11125}," weatherService",{"type":254,"tag":622,"props":11127,"children":11128},{"style":629},[11129],{"type":259,"value":1685},{"type":254,"tag":622,"props":11131,"children":11132},{"class":624,"line":836},[11133],{"type":254,"tag":622,"props":11134,"children":11135},{"style":629},[11136],{"type":259,"value":3574},{"type":254,"tag":622,"props":11138,"children":11139},{"class":624,"line":862},[11140,11145,11149,11153],{"type":254,"tag":622,"props":11141,"children":11142},{"style":1202},[11143],{"type":259,"value":11144},"        _weatherService ",{"type":254,"tag":622,"props":11146,"children":11147},{"style":629},[11148],{"type":259,"value":1654},{"type":254,"tag":622,"props":11150,"children":11151},{"style":1202},[11152],{"type":259,"value":11125},{"type":254,"tag":622,"props":11154,"children":11155},{"style":629},[11156],{"type":259,"value":2350},{"type":254,"tag":622,"props":11158,"children":11159},{"class":624,"line":900},[11160],{"type":254,"tag":622,"props":11161,"children":11162},{"style":629},[11163],{"type":259,"value":1093},{"type":254,"tag":622,"props":11165,"children":11166},{"class":624,"line":938},[11167],{"type":254,"tag":622,"props":11168,"children":11169},{"emptyLinePlaceholder":1699},[11170],{"type":259,"value":1702},{"type":254,"tag":622,"props":11172,"children":11173},{"class":624,"line":976},[11174,11178,11183,11187,11191,11195,11199,11204,11208],{"type":254,"tag":622,"props":11175,"children":11176},{"style":629},[11177],{"type":259,"value":9494},{"type":254,"tag":622,"props":11179,"children":11180},{"style":714},[11181],{"type":259,"value":11182},"HttpGet",{"type":254,"tag":622,"props":11184,"children":11185},{"style":629},[11186],{"type":259,"value":1668},{"type":254,"tag":622,"props":11188,"children":11189},{"style":714},[11190],{"type":259,"value":2254},{"type":254,"tag":622,"props":11192,"children":11193},{"style":629},[11194],{"type":259,"value":1619},{"type":254,"tag":622,"props":11196,"children":11197},{"style":629},[11198],{"type":259,"value":661},{"type":254,"tag":622,"props":11200,"children":11201},{"style":664},[11202],{"type":259,"value":11203},"GetWeatherForecast",{"type":254,"tag":622,"props":11205,"children":11206},{"style":629},[11207],{"type":259,"value":652},{"type":254,"tag":622,"props":11209,"children":11210},{"style":629},[11211],{"type":259,"value":11037},{"type":254,"tag":622,"props":11213,"children":11214},{"class":624,"line":1014},[11215,11219,11224,11229,11234,11239,11244,11248,11253],{"type":254,"tag":622,"props":11216,"children":11217},{"style":629},[11218],{"type":259,"value":9494},{"type":254,"tag":622,"props":11220,"children":11221},{"style":714},[11222],{"type":259,"value":11223},"ProducesResponseType",{"type":254,"tag":622,"props":11225,"children":11226},{"style":629},[11227],{"type":259,"value":11228},"(typeof(",{"type":254,"tag":622,"props":11230,"children":11231},{"style":714},[11232],{"type":259,"value":11233},"WeatherForecast",{"type":254,"tag":622,"props":11235,"children":11236},{"style":629},[11237],{"type":259,"value":11238},"),",{"type":254,"tag":622,"props":11240,"children":11241},{"style":1202},[11242],{"type":259,"value":11243}," StatusCodes",{"type":254,"tag":622,"props":11245,"children":11246},{"style":629},[11247],{"type":259,"value":594},{"type":254,"tag":622,"props":11249,"children":11250},{"style":1202},[11251],{"type":259,"value":11252},"Status200OK",{"type":254,"tag":622,"props":11254,"children":11255},{"style":629},[11256],{"type":259,"value":11037},{"type":254,"tag":622,"props":11258,"children":11259},{"class":624,"line":1052},[11260,11264,11268,11272,11276,11281,11286],{"type":254,"tag":622,"props":11261,"children":11262},{"style":644},[11263],{"type":259,"value":1970},{"type":254,"tag":622,"props":11265,"children":11266},{"style":714},[11267],{"type":259,"value":2842},{"type":254,"tag":622,"props":11269,"children":11270},{"style":629},[11271],{"type":259,"value":1743},{"type":254,"tag":622,"props":11273,"children":11274},{"style":714},[11275],{"type":259,"value":11233},{"type":254,"tag":622,"props":11277,"children":11278},{"style":629},[11279],{"type":259,"value":11280},"[]>",{"type":254,"tag":622,"props":11282,"children":11283},{"style":1735},[11284],{"type":259,"value":11285}," Get",{"type":254,"tag":622,"props":11287,"children":11288},{"style":629},[11289],{"type":259,"value":1634},{"type":254,"tag":622,"props":11291,"children":11292},{"class":624,"line":1078},[11293],{"type":254,"tag":622,"props":11294,"children":11295},{"style":629},[11296],{"type":259,"value":3574},{"type":254,"tag":622,"props":11298,"children":11299},{"class":624,"line":1087},[11300,11304,11308,11312,11317],{"type":254,"tag":622,"props":11301,"children":11302},{"style":1196},[11303],{"type":259,"value":9046},{"type":254,"tag":622,"props":11305,"children":11306},{"style":1202},[11307],{"type":259,"value":11090},{"type":254,"tag":622,"props":11309,"children":11310},{"style":629},[11311],{"type":259,"value":594},{"type":254,"tag":622,"props":11313,"children":11314},{"style":1735},[11315],{"type":259,"value":11316},"GetWeatherForecasts",{"type":254,"tag":622,"props":11318,"children":11319},{"style":629},[11320],{"type":259,"value":2798},{"type":254,"tag":622,"props":11322,"children":11323},{"class":624,"line":1096},[11324],{"type":254,"tag":622,"props":11325,"children":11326},{"style":629},[11327],{"type":259,"value":1093},{"type":254,"tag":622,"props":11329,"children":11330},{"class":624,"line":1105},[11331],{"type":254,"tag":622,"props":11332,"children":11333},{"style":629},[11334],{"type":259,"value":1111},{"type":254,"tag":255,"props":11336,"children":11337},{},[11338,11340,11346,11348,11355,11357],{"type":259,"value":11339},"We can get rid of the ",{"type":254,"tag":389,"props":11341,"children":11343},{"className":11342},[],[11344],{"type":259,"value":11345},"typeof",{"type":259,"value":11347}," because there are now ",{"type":254,"tag":262,"props":11349,"children":11352},{"href":11350,"rel":11351},"https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-8.0?view=aspnetcore-8.0#support-for-generic-attributes",[266],[11353],{"type":259,"value":11354},"generic attributes for some common ASP.NET Core attributes",{"type":259,"value":11356}," like ",{"type":254,"tag":389,"props":11358,"children":11360},{"className":11359},[],[11361],{"type":259,"value":11362},"ProducesResponseType.",{"type":254,"tag":612,"props":11364,"children":11366},{"className":1423,"code":11365,"language":1425,"meta":205,"style":205},"    [HttpGet(Name = \"GetWeatherForecast\")]\n    [ProducesResponseType\u003CWeatherForecast>(StatusCodes.Status200OK)]\n    public Task\u003CWeatherForecast[]> Get()\n    {\n        return _weatherService.GetWeatherForecasts();\n    }\n",[11367],{"type":254,"tag":389,"props":11368,"children":11369},{"__ignoreMap":205},[11370,11409,11453,11484,11491,11514],{"type":254,"tag":622,"props":11371,"children":11372},{"class":624,"line":625},[11373,11377,11381,11385,11389,11393,11397,11401,11405],{"type":254,"tag":622,"props":11374,"children":11375},{"style":629},[11376],{"type":259,"value":9494},{"type":254,"tag":622,"props":11378,"children":11379},{"style":714},[11380],{"type":259,"value":11182},{"type":254,"tag":622,"props":11382,"children":11383},{"style":629},[11384],{"type":259,"value":1668},{"type":254,"tag":622,"props":11386,"children":11387},{"style":714},[11388],{"type":259,"value":2254},{"type":254,"tag":622,"props":11390,"children":11391},{"style":629},[11392],{"type":259,"value":1619},{"type":254,"tag":622,"props":11394,"children":11395},{"style":629},[11396],{"type":259,"value":661},{"type":254,"tag":622,"props":11398,"children":11399},{"style":664},[11400],{"type":259,"value":11203},{"type":254,"tag":622,"props":11402,"children":11403},{"style":629},[11404],{"type":259,"value":652},{"type":254,"tag":622,"props":11406,"children":11407},{"style":629},[11408],{"type":259,"value":11037},{"type":254,"tag":622,"props":11410,"children":11411},{"class":624,"line":635},[11412,11416,11420,11424,11428,11432,11436,11441,11445,11449],{"type":254,"tag":622,"props":11413,"children":11414},{"style":629},[11415],{"type":259,"value":9494},{"type":254,"tag":622,"props":11417,"children":11418},{"style":714},[11419],{"type":259,"value":11223},{"type":254,"tag":622,"props":11421,"children":11422},{"style":1202},[11423],{"type":259,"value":1743},{"type":254,"tag":622,"props":11425,"children":11426},{"style":714},[11427],{"type":259,"value":11233},{"type":254,"tag":622,"props":11429,"children":11430},{"style":1202},[11431],{"type":259,"value":6813},{"type":254,"tag":622,"props":11433,"children":11434},{"style":629},[11435],{"type":259,"value":1668},{"type":254,"tag":622,"props":11437,"children":11438},{"style":1202},[11439],{"type":259,"value":11440},"StatusCodes",{"type":254,"tag":622,"props":11442,"children":11443},{"style":629},[11444],{"type":259,"value":594},{"type":254,"tag":622,"props":11446,"children":11447},{"style":1202},[11448],{"type":259,"value":11252},{"type":254,"tag":622,"props":11450,"children":11451},{"style":629},[11452],{"type":259,"value":11037},{"type":254,"tag":622,"props":11454,"children":11455},{"class":624,"line":679},[11456,11460,11464,11468,11472,11476,11480],{"type":254,"tag":622,"props":11457,"children":11458},{"style":644},[11459],{"type":259,"value":1970},{"type":254,"tag":622,"props":11461,"children":11462},{"style":714},[11463],{"type":259,"value":2842},{"type":254,"tag":622,"props":11465,"children":11466},{"style":629},[11467],{"type":259,"value":1743},{"type":254,"tag":622,"props":11469,"children":11470},{"style":714},[11471],{"type":259,"value":11233},{"type":254,"tag":622,"props":11473,"children":11474},{"style":629},[11475],{"type":259,"value":11280},{"type":254,"tag":622,"props":11477,"children":11478},{"style":1735},[11479],{"type":259,"value":11285},{"type":254,"tag":622,"props":11481,"children":11482},{"style":629},[11483],{"type":259,"value":1634},{"type":254,"tag":622,"props":11485,"children":11486},{"class":624,"line":705},[11487],{"type":254,"tag":622,"props":11488,"children":11489},{"style":629},[11490],{"type":259,"value":3574},{"type":254,"tag":622,"props":11492,"children":11493},{"class":624,"line":732},[11494,11498,11502,11506,11510],{"type":254,"tag":622,"props":11495,"children":11496},{"style":1196},[11497],{"type":259,"value":9046},{"type":254,"tag":622,"props":11499,"children":11500},{"style":1202},[11501],{"type":259,"value":11090},{"type":254,"tag":622,"props":11503,"children":11504},{"style":629},[11505],{"type":259,"value":594},{"type":254,"tag":622,"props":11507,"children":11508},{"style":1735},[11509],{"type":259,"value":11316},{"type":254,"tag":622,"props":11511,"children":11512},{"style":629},[11513],{"type":259,"value":2798},{"type":254,"tag":622,"props":11515,"children":11516},{"class":624,"line":759},[11517],{"type":254,"tag":622,"props":11518,"children":11519},{"style":629},[11520],{"type":259,"value":1093},{"type":254,"tag":255,"props":11522,"children":11523},{},[11524,11526,11531],{"type":259,"value":11525},"There are now 2 implementations of the ",{"type":254,"tag":389,"props":11527,"children":11529},{"className":11528},[],[11530],{"type":259,"value":10928},{"type":259,"value":11532}," interface:",{"type":254,"tag":332,"props":11534,"children":11535},{},[11536,11547],{"type":254,"tag":336,"props":11537,"children":11538},{},[11539,11545],{"type":254,"tag":389,"props":11540,"children":11542},{"className":11541},[],[11543],{"type":259,"value":11544},"RandomWeatherService",{"type":259,"value":11546}," that contains the code that previously was in the controller",{"type":254,"tag":336,"props":11548,"children":11549},{},[11550,11555,11557],{"type":254,"tag":389,"props":11551,"children":11553},{"className":11552},[],[11554],{"type":259,"value":10954},{"type":259,"value":11556}," that makes a call to the Open Weather Map API to retrieve the weather forecasts and then maps the obtained data to a list of ",{"type":254,"tag":389,"props":11558,"children":11560},{"className":11559},[],[11561],{"type":259,"value":11233},{"type":254,"tag":612,"props":11563,"children":11565},{"className":1423,"code":11564,"language":1425,"meta":205,"style":205},"public class OpenWeatherService : IWeatherService\n{\n    private readonly IOpenWeatherMapApi _openWeatherMapApi;\n    private static readonly (double Latitude, double Longitude) BordeauxCoordinates = (44.837789, -0.57918);\n\n    public OpenWeatherService(IOpenWeatherMapApi openWeatherMapApi)\n    {\n        _openWeatherMapApi = openWeatherMapApi;\n    }\n\n    public async Task\u003CWeatherForecast[]> GetWeatherForecasts()\n    {\n        var weatherApiResponse = await _openWeatherMapApi.GetWeatherForecast(BordeauxCoordinates.Latitude, BordeauxCoordinates.Longitude);\n        \n        var computeWeatherSummary = (double temperature) =>\n            temperature switch\n            {\n                \u003C 0 => \"Freezing\",\n                >= 0 and \u003C 5 => \"Bracing\",\n                >= 5 and \u003C 12 => \"Chilly\",\n                >= 12 and \u003C 18 => \"Cool\",\n                >= 18 and \u003C 24 => \"Mild\",\n                >= 24 and \u003C 30 => \"Warm\",\n                >= 30 and \u003C 35 => \"Balmy\",\n                >= 35 and \u003C 40 => \"Hot\",\n                >= 40 and \u003C 45 => \"Sweltering\",\n                >= 45 => \"Scorching\",\n                _ => \"Warm\"\n            };\n        return weatherApiResponse.List\n            .Select(x =>\n                new WeatherForecast\n                {\n                    Date = DateOnly.FromDateTime(DateTimeOffset.FromUnixTimeSeconds(x.Dt).Date),\n                    TemperatureC = Convert.ToInt32(x.Main.Temp),\n                    Summary = computeWeatherSummary(x.Main.Temp)\n                })\n            .ToArray();\n    }\n}\n",[11566],{"type":254,"tag":389,"props":11567,"children":11568},{"__ignoreMap":205},[11569,11594,11601,11626,11705,11712,11741,11748,11768,11775,11782,11818,11825,11896,11904,11937,11950,11958,11991,12038,12083,12128,12173,12218,12263,12308,12353,12385,12410,12418,12438,12462,12475,12483,12557,12613,12657,12665,12681,12688],{"type":254,"tag":622,"props":11570,"children":11571},{"class":624,"line":625},[11572,11576,11580,11585,11589],{"type":254,"tag":622,"props":11573,"children":11574},{"style":644},[11575],{"type":259,"value":1437},{"type":254,"tag":622,"props":11577,"children":11578},{"style":714},[11579],{"type":259,"value":6761},{"type":254,"tag":622,"props":11581,"children":11582},{"style":714},[11583],{"type":259,"value":11584}," OpenWeatherService",{"type":254,"tag":622,"props":11586,"children":11587},{"style":629},[11588],{"type":259,"value":3274},{"type":254,"tag":622,"props":11590,"children":11591},{"style":714},[11592],{"type":259,"value":11593}," IWeatherService\n",{"type":254,"tag":622,"props":11595,"children":11596},{"class":624,"line":635},[11597],{"type":254,"tag":622,"props":11598,"children":11599},{"style":629},[11600],{"type":259,"value":632},{"type":254,"tag":622,"props":11602,"children":11603},{"class":624,"line":679},[11604,11608,11612,11617,11622],{"type":254,"tag":622,"props":11605,"children":11606},{"style":644},[11607],{"type":259,"value":6790},{"type":254,"tag":622,"props":11609,"children":11610},{"style":644},[11611],{"type":259,"value":6795},{"type":254,"tag":622,"props":11613,"children":11614},{"style":714},[11615],{"type":259,"value":11616}," IOpenWeatherMapApi",{"type":254,"tag":622,"props":11618,"children":11619},{"style":714},[11620],{"type":259,"value":11621}," _openWeatherMapApi",{"type":254,"tag":622,"props":11623,"children":11624},{"style":629},[11625],{"type":259,"value":2350},{"type":254,"tag":622,"props":11627,"children":11628},{"class":624,"line":705},[11629,11633,11638,11642,11647,11652,11656,11661,11666,11670,11675,11679,11683,11688,11692,11696,11701],{"type":254,"tag":622,"props":11630,"children":11631},{"style":644},[11632],{"type":259,"value":6790},{"type":254,"tag":622,"props":11634,"children":11635},{"style":644},[11636],{"type":259,"value":11637}," static",{"type":254,"tag":622,"props":11639,"children":11640},{"style":644},[11641],{"type":259,"value":6795},{"type":254,"tag":622,"props":11643,"children":11644},{"style":629},[11645],{"type":259,"value":11646}," (double",{"type":254,"tag":622,"props":11648,"children":11649},{"style":714},[11650],{"type":259,"value":11651}," Latitude",{"type":254,"tag":622,"props":11653,"children":11654},{"style":629},[11655],{"type":259,"value":3149},{"type":254,"tag":622,"props":11657,"children":11658},{"style":629},[11659],{"type":259,"value":11660}," double",{"type":254,"tag":622,"props":11662,"children":11663},{"style":714},[11664],{"type":259,"value":11665}," Longitude",{"type":254,"tag":622,"props":11667,"children":11668},{"style":629},[11669],{"type":259,"value":2309},{"type":254,"tag":622,"props":11671,"children":11672},{"style":714},[11673],{"type":259,"value":11674}," BordeauxCoordinates",{"type":254,"tag":622,"props":11676,"children":11677},{"style":629},[11678],{"type":259,"value":1619},{"type":254,"tag":622,"props":11680,"children":11681},{"style":629},[11682],{"type":259,"value":3547},{"type":254,"tag":622,"props":11684,"children":11685},{"style":741},[11686],{"type":259,"value":11687},"44.837789",{"type":254,"tag":622,"props":11689,"children":11690},{"style":629},[11691],{"type":259,"value":3149},{"type":254,"tag":622,"props":11693,"children":11694},{"style":629},[11695],{"type":259,"value":10733},{"type":254,"tag":622,"props":11697,"children":11698},{"style":741},[11699],{"type":259,"value":11700},"0.57918",{"type":254,"tag":622,"props":11702,"children":11703},{"style":629},[11704],{"type":259,"value":1554},{"type":254,"tag":622,"props":11706,"children":11707},{"class":624,"line":732},[11708],{"type":254,"tag":622,"props":11709,"children":11710},{"emptyLinePlaceholder":1699},[11711],{"type":259,"value":1702},{"type":254,"tag":622,"props":11713,"children":11714},{"class":624,"line":759},[11715,11719,11723,11727,11732,11737],{"type":254,"tag":622,"props":11716,"children":11717},{"style":644},[11718],{"type":259,"value":1970},{"type":254,"tag":622,"props":11720,"children":11721},{"style":1735},[11722],{"type":259,"value":11584},{"type":254,"tag":622,"props":11724,"children":11725},{"style":629},[11726],{"type":259,"value":1668},{"type":254,"tag":622,"props":11728,"children":11729},{"style":714},[11730],{"type":259,"value":11731},"IOpenWeatherMapApi",{"type":254,"tag":622,"props":11733,"children":11734},{"style":714},[11735],{"type":259,"value":11736}," openWeatherMapApi",{"type":254,"tag":622,"props":11738,"children":11739},{"style":629},[11740],{"type":259,"value":1685},{"type":254,"tag":622,"props":11742,"children":11743},{"class":624,"line":798},[11744],{"type":254,"tag":622,"props":11745,"children":11746},{"style":629},[11747],{"type":259,"value":3574},{"type":254,"tag":622,"props":11749,"children":11750},{"class":624,"line":836},[11751,11756,11760,11764],{"type":254,"tag":622,"props":11752,"children":11753},{"style":1202},[11754],{"type":259,"value":11755},"        _openWeatherMapApi ",{"type":254,"tag":622,"props":11757,"children":11758},{"style":629},[11759],{"type":259,"value":1654},{"type":254,"tag":622,"props":11761,"children":11762},{"style":1202},[11763],{"type":259,"value":11736},{"type":254,"tag":622,"props":11765,"children":11766},{"style":629},[11767],{"type":259,"value":2350},{"type":254,"tag":622,"props":11769,"children":11770},{"class":624,"line":862},[11771],{"type":254,"tag":622,"props":11772,"children":11773},{"style":629},[11774],{"type":259,"value":1093},{"type":254,"tag":622,"props":11776,"children":11777},{"class":624,"line":900},[11778],{"type":254,"tag":622,"props":11779,"children":11780},{"emptyLinePlaceholder":1699},[11781],{"type":259,"value":1702},{"type":254,"tag":622,"props":11783,"children":11784},{"class":624,"line":938},[11785,11789,11793,11797,11801,11805,11809,11814],{"type":254,"tag":622,"props":11786,"children":11787},{"style":644},[11788],{"type":259,"value":1970},{"type":254,"tag":622,"props":11790,"children":11791},{"style":644},[11792],{"type":259,"value":6987},{"type":254,"tag":622,"props":11794,"children":11795},{"style":714},[11796],{"type":259,"value":2842},{"type":254,"tag":622,"props":11798,"children":11799},{"style":629},[11800],{"type":259,"value":1743},{"type":254,"tag":622,"props":11802,"children":11803},{"style":714},[11804],{"type":259,"value":11233},{"type":254,"tag":622,"props":11806,"children":11807},{"style":629},[11808],{"type":259,"value":11280},{"type":254,"tag":622,"props":11810,"children":11811},{"style":1735},[11812],{"type":259,"value":11813}," GetWeatherForecasts",{"type":254,"tag":622,"props":11815,"children":11816},{"style":629},[11817],{"type":259,"value":1634},{"type":254,"tag":622,"props":11819,"children":11820},{"class":624,"line":976},[11821],{"type":254,"tag":622,"props":11822,"children":11823},{"style":629},[11824],{"type":259,"value":3574},{"type":254,"tag":622,"props":11826,"children":11827},{"class":624,"line":1014},[11828,11832,11837,11841,11845,11849,11853,11857,11861,11866,11870,11875,11879,11883,11887,11892],{"type":254,"tag":622,"props":11829,"children":11830},{"style":714},[11831],{"type":259,"value":3582},{"type":254,"tag":622,"props":11833,"children":11834},{"style":714},[11835],{"type":259,"value":11836}," weatherApiResponse",{"type":254,"tag":622,"props":11838,"children":11839},{"style":629},[11840],{"type":259,"value":1619},{"type":254,"tag":622,"props":11842,"children":11843},{"style":629},[11844],{"type":259,"value":1724},{"type":254,"tag":622,"props":11846,"children":11847},{"style":1202},[11848],{"type":259,"value":11621},{"type":254,"tag":622,"props":11850,"children":11851},{"style":629},[11852],{"type":259,"value":594},{"type":254,"tag":622,"props":11854,"children":11855},{"style":1735},[11856],{"type":259,"value":11203},{"type":254,"tag":622,"props":11858,"children":11859},{"style":629},[11860],{"type":259,"value":1668},{"type":254,"tag":622,"props":11862,"children":11863},{"style":1202},[11864],{"type":259,"value":11865},"BordeauxCoordinates",{"type":254,"tag":622,"props":11867,"children":11868},{"style":629},[11869],{"type":259,"value":594},{"type":254,"tag":622,"props":11871,"children":11872},{"style":1202},[11873],{"type":259,"value":11874},"Latitude",{"type":254,"tag":622,"props":11876,"children":11877},{"style":629},[11878],{"type":259,"value":3149},{"type":254,"tag":622,"props":11880,"children":11881},{"style":1202},[11882],{"type":259,"value":11674},{"type":254,"tag":622,"props":11884,"children":11885},{"style":629},[11886],{"type":259,"value":594},{"type":254,"tag":622,"props":11888,"children":11889},{"style":1202},[11890],{"type":259,"value":11891},"Longitude",{"type":254,"tag":622,"props":11893,"children":11894},{"style":629},[11895],{"type":259,"value":1554},{"type":254,"tag":622,"props":11897,"children":11898},{"class":624,"line":1052},[11899],{"type":254,"tag":622,"props":11900,"children":11901},{"style":1202},[11902],{"type":259,"value":11903},"        \n",{"type":254,"tag":622,"props":11905,"children":11906},{"class":624,"line":1078},[11907,11911,11916,11920,11924,11929,11933],{"type":254,"tag":622,"props":11908,"children":11909},{"style":714},[11910],{"type":259,"value":3582},{"type":254,"tag":622,"props":11912,"children":11913},{"style":714},[11914],{"type":259,"value":11915}," computeWeatherSummary",{"type":254,"tag":622,"props":11917,"children":11918},{"style":629},[11919],{"type":259,"value":1619},{"type":254,"tag":622,"props":11921,"children":11922},{"style":629},[11923],{"type":259,"value":11646},{"type":254,"tag":622,"props":11925,"children":11926},{"style":714},[11927],{"type":259,"value":11928}," temperature",{"type":254,"tag":622,"props":11930,"children":11931},{"style":629},[11932],{"type":259,"value":2309},{"type":254,"tag":622,"props":11934,"children":11935},{"style":629},[11936],{"type":259,"value":6569},{"type":254,"tag":622,"props":11938,"children":11939},{"class":624,"line":1087},[11940,11945],{"type":254,"tag":622,"props":11941,"children":11942},{"style":1202},[11943],{"type":259,"value":11944},"            temperature ",{"type":254,"tag":622,"props":11946,"children":11947},{"style":1196},[11948],{"type":259,"value":11949},"switch\n",{"type":254,"tag":622,"props":11951,"children":11952},{"class":624,"line":1096},[11953],{"type":254,"tag":622,"props":11954,"children":11955},{"style":629},[11956],{"type":259,"value":11957},"            {\n",{"type":254,"tag":622,"props":11959,"children":11960},{"class":624,"line":1105},[11961,11966,11970,11974,11978,11983,11987],{"type":254,"tag":622,"props":11962,"children":11963},{"style":629},[11964],{"type":259,"value":11965},"                \u003C",{"type":254,"tag":622,"props":11967,"children":11968},{"style":741},[11969],{"type":259,"value":6038},{"type":254,"tag":622,"props":11971,"children":11972},{"style":629},[11973],{"type":259,"value":2697},{"type":254,"tag":622,"props":11975,"children":11976},{"style":629},[11977],{"type":259,"value":661},{"type":254,"tag":622,"props":11979,"children":11980},{"style":664},[11981],{"type":259,"value":11982},"Freezing",{"type":254,"tag":622,"props":11984,"children":11985},{"style":629},[11986],{"type":259,"value":652},{"type":254,"tag":622,"props":11988,"children":11989},{"style":629},[11990],{"type":259,"value":676},{"type":254,"tag":622,"props":11992,"children":11993},{"class":624,"line":4131},[11994,11999,12003,12008,12013,12017,12021,12025,12030,12034],{"type":254,"tag":622,"props":11995,"children":11996},{"style":629},[11997],{"type":259,"value":11998},"                >=",{"type":254,"tag":622,"props":12000,"children":12001},{"style":741},[12002],{"type":259,"value":6038},{"type":254,"tag":622,"props":12004,"children":12005},{"style":629},[12006],{"type":259,"value":12007}," and",{"type":254,"tag":622,"props":12009,"children":12010},{"style":629},[12011],{"type":259,"value":12012}," \u003C",{"type":254,"tag":622,"props":12014,"children":12015},{"style":741},[12016],{"type":259,"value":6144},{"type":254,"tag":622,"props":12018,"children":12019},{"style":629},[12020],{"type":259,"value":2697},{"type":254,"tag":622,"props":12022,"children":12023},{"style":629},[12024],{"type":259,"value":661},{"type":254,"tag":622,"props":12026,"children":12027},{"style":664},[12028],{"type":259,"value":12029},"Bracing",{"type":254,"tag":622,"props":12031,"children":12032},{"style":629},[12033],{"type":259,"value":652},{"type":254,"tag":622,"props":12035,"children":12036},{"style":629},[12037],{"type":259,"value":676},{"type":254,"tag":622,"props":12039,"children":12040},{"class":624,"line":4183},[12041,12045,12049,12053,12057,12062,12066,12070,12075,12079],{"type":254,"tag":622,"props":12042,"children":12043},{"style":629},[12044],{"type":259,"value":11998},{"type":254,"tag":622,"props":12046,"children":12047},{"style":741},[12048],{"type":259,"value":6144},{"type":254,"tag":622,"props":12050,"children":12051},{"style":629},[12052],{"type":259,"value":12007},{"type":254,"tag":622,"props":12054,"children":12055},{"style":629},[12056],{"type":259,"value":12012},{"type":254,"tag":622,"props":12058,"children":12059},{"style":741},[12060],{"type":259,"value":12061}," 12",{"type":254,"tag":622,"props":12063,"children":12064},{"style":629},[12065],{"type":259,"value":2697},{"type":254,"tag":622,"props":12067,"children":12068},{"style":629},[12069],{"type":259,"value":661},{"type":254,"tag":622,"props":12071,"children":12072},{"style":664},[12073],{"type":259,"value":12074},"Chilly",{"type":254,"tag":622,"props":12076,"children":12077},{"style":629},[12078],{"type":259,"value":652},{"type":254,"tag":622,"props":12080,"children":12081},{"style":629},[12082],{"type":259,"value":676},{"type":254,"tag":622,"props":12084,"children":12085},{"class":624,"line":4208},[12086,12090,12094,12098,12102,12107,12111,12115,12120,12124],{"type":254,"tag":622,"props":12087,"children":12088},{"style":629},[12089],{"type":259,"value":11998},{"type":254,"tag":622,"props":12091,"children":12092},{"style":741},[12093],{"type":259,"value":12061},{"type":254,"tag":622,"props":12095,"children":12096},{"style":629},[12097],{"type":259,"value":12007},{"type":254,"tag":622,"props":12099,"children":12100},{"style":629},[12101],{"type":259,"value":12012},{"type":254,"tag":622,"props":12103,"children":12104},{"style":741},[12105],{"type":259,"value":12106}," 18",{"type":254,"tag":622,"props":12108,"children":12109},{"style":629},[12110],{"type":259,"value":2697},{"type":254,"tag":622,"props":12112,"children":12113},{"style":629},[12114],{"type":259,"value":661},{"type":254,"tag":622,"props":12116,"children":12117},{"style":664},[12118],{"type":259,"value":12119},"Cool",{"type":254,"tag":622,"props":12121,"children":12122},{"style":629},[12123],{"type":259,"value":652},{"type":254,"tag":622,"props":12125,"children":12126},{"style":629},[12127],{"type":259,"value":676},{"type":254,"tag":622,"props":12129,"children":12130},{"class":624,"line":4216},[12131,12135,12139,12143,12147,12152,12156,12160,12165,12169],{"type":254,"tag":622,"props":12132,"children":12133},{"style":629},[12134],{"type":259,"value":11998},{"type":254,"tag":622,"props":12136,"children":12137},{"style":741},[12138],{"type":259,"value":12106},{"type":254,"tag":622,"props":12140,"children":12141},{"style":629},[12142],{"type":259,"value":12007},{"type":254,"tag":622,"props":12144,"children":12145},{"style":629},[12146],{"type":259,"value":12012},{"type":254,"tag":622,"props":12148,"children":12149},{"style":741},[12150],{"type":259,"value":12151}," 24",{"type":254,"tag":622,"props":12153,"children":12154},{"style":629},[12155],{"type":259,"value":2697},{"type":254,"tag":622,"props":12157,"children":12158},{"style":629},[12159],{"type":259,"value":661},{"type":254,"tag":622,"props":12161,"children":12162},{"style":664},[12163],{"type":259,"value":12164},"Mild",{"type":254,"tag":622,"props":12166,"children":12167},{"style":629},[12168],{"type":259,"value":652},{"type":254,"tag":622,"props":12170,"children":12171},{"style":629},[12172],{"type":259,"value":676},{"type":254,"tag":622,"props":12174,"children":12175},{"class":624,"line":4224},[12176,12180,12184,12188,12192,12197,12201,12205,12210,12214],{"type":254,"tag":622,"props":12177,"children":12178},{"style":629},[12179],{"type":259,"value":11998},{"type":254,"tag":622,"props":12181,"children":12182},{"style":741},[12183],{"type":259,"value":12151},{"type":254,"tag":622,"props":12185,"children":12186},{"style":629},[12187],{"type":259,"value":12007},{"type":254,"tag":622,"props":12189,"children":12190},{"style":629},[12191],{"type":259,"value":12012},{"type":254,"tag":622,"props":12193,"children":12194},{"style":741},[12195],{"type":259,"value":12196}," 30",{"type":254,"tag":622,"props":12198,"children":12199},{"style":629},[12200],{"type":259,"value":2697},{"type":254,"tag":622,"props":12202,"children":12203},{"style":629},[12204],{"type":259,"value":661},{"type":254,"tag":622,"props":12206,"children":12207},{"style":664},[12208],{"type":259,"value":12209},"Warm",{"type":254,"tag":622,"props":12211,"children":12212},{"style":629},[12213],{"type":259,"value":652},{"type":254,"tag":622,"props":12215,"children":12216},{"style":629},[12217],{"type":259,"value":676},{"type":254,"tag":622,"props":12219,"children":12220},{"class":624,"line":4268},[12221,12225,12229,12233,12237,12242,12246,12250,12255,12259],{"type":254,"tag":622,"props":12222,"children":12223},{"style":629},[12224],{"type":259,"value":11998},{"type":254,"tag":622,"props":12226,"children":12227},{"style":741},[12228],{"type":259,"value":12196},{"type":254,"tag":622,"props":12230,"children":12231},{"style":629},[12232],{"type":259,"value":12007},{"type":254,"tag":622,"props":12234,"children":12235},{"style":629},[12236],{"type":259,"value":12012},{"type":254,"tag":622,"props":12238,"children":12239},{"style":741},[12240],{"type":259,"value":12241}," 35",{"type":254,"tag":622,"props":12243,"children":12244},{"style":629},[12245],{"type":259,"value":2697},{"type":254,"tag":622,"props":12247,"children":12248},{"style":629},[12249],{"type":259,"value":661},{"type":254,"tag":622,"props":12251,"children":12252},{"style":664},[12253],{"type":259,"value":12254},"Balmy",{"type":254,"tag":622,"props":12256,"children":12257},{"style":629},[12258],{"type":259,"value":652},{"type":254,"tag":622,"props":12260,"children":12261},{"style":629},[12262],{"type":259,"value":676},{"type":254,"tag":622,"props":12264,"children":12265},{"class":624,"line":4276},[12266,12270,12274,12278,12282,12287,12291,12295,12300,12304],{"type":254,"tag":622,"props":12267,"children":12268},{"style":629},[12269],{"type":259,"value":11998},{"type":254,"tag":622,"props":12271,"children":12272},{"style":741},[12273],{"type":259,"value":12241},{"type":254,"tag":622,"props":12275,"children":12276},{"style":629},[12277],{"type":259,"value":12007},{"type":254,"tag":622,"props":12279,"children":12280},{"style":629},[12281],{"type":259,"value":12012},{"type":254,"tag":622,"props":12283,"children":12284},{"style":741},[12285],{"type":259,"value":12286}," 40",{"type":254,"tag":622,"props":12288,"children":12289},{"style":629},[12290],{"type":259,"value":2697},{"type":254,"tag":622,"props":12292,"children":12293},{"style":629},[12294],{"type":259,"value":661},{"type":254,"tag":622,"props":12296,"children":12297},{"style":664},[12298],{"type":259,"value":12299},"Hot",{"type":254,"tag":622,"props":12301,"children":12302},{"style":629},[12303],{"type":259,"value":652},{"type":254,"tag":622,"props":12305,"children":12306},{"style":629},[12307],{"type":259,"value":676},{"type":254,"tag":622,"props":12309,"children":12310},{"class":624,"line":4332},[12311,12315,12319,12323,12327,12332,12336,12340,12345,12349],{"type":254,"tag":622,"props":12312,"children":12313},{"style":629},[12314],{"type":259,"value":11998},{"type":254,"tag":622,"props":12316,"children":12317},{"style":741},[12318],{"type":259,"value":12286},{"type":254,"tag":622,"props":12320,"children":12321},{"style":629},[12322],{"type":259,"value":12007},{"type":254,"tag":622,"props":12324,"children":12325},{"style":629},[12326],{"type":259,"value":12012},{"type":254,"tag":622,"props":12328,"children":12329},{"style":741},[12330],{"type":259,"value":12331}," 45",{"type":254,"tag":622,"props":12333,"children":12334},{"style":629},[12335],{"type":259,"value":2697},{"type":254,"tag":622,"props":12337,"children":12338},{"style":629},[12339],{"type":259,"value":661},{"type":254,"tag":622,"props":12341,"children":12342},{"style":664},[12343],{"type":259,"value":12344},"Sweltering",{"type":254,"tag":622,"props":12346,"children":12347},{"style":629},[12348],{"type":259,"value":652},{"type":254,"tag":622,"props":12350,"children":12351},{"style":629},[12352],{"type":259,"value":676},{"type":254,"tag":622,"props":12354,"children":12355},{"class":624,"line":4376},[12356,12360,12364,12368,12372,12377,12381],{"type":254,"tag":622,"props":12357,"children":12358},{"style":629},[12359],{"type":259,"value":11998},{"type":254,"tag":622,"props":12361,"children":12362},{"style":741},[12363],{"type":259,"value":12331},{"type":254,"tag":622,"props":12365,"children":12366},{"style":629},[12367],{"type":259,"value":2697},{"type":254,"tag":622,"props":12369,"children":12370},{"style":629},[12371],{"type":259,"value":661},{"type":254,"tag":622,"props":12373,"children":12374},{"style":664},[12375],{"type":259,"value":12376},"Scorching",{"type":254,"tag":622,"props":12378,"children":12379},{"style":629},[12380],{"type":259,"value":652},{"type":254,"tag":622,"props":12382,"children":12383},{"style":629},[12384],{"type":259,"value":676},{"type":254,"tag":622,"props":12386,"children":12387},{"class":624,"line":4384},[12388,12393,12398,12402,12406],{"type":254,"tag":622,"props":12389,"children":12390},{"style":1202},[12391],{"type":259,"value":12392},"                _ ",{"type":254,"tag":622,"props":12394,"children":12395},{"style":629},[12396],{"type":259,"value":12397},"=>",{"type":254,"tag":622,"props":12399,"children":12400},{"style":629},[12401],{"type":259,"value":661},{"type":254,"tag":622,"props":12403,"children":12404},{"style":664},[12405],{"type":259,"value":12209},{"type":254,"tag":622,"props":12407,"children":12408},{"style":629},[12409],{"type":259,"value":9909},{"type":254,"tag":622,"props":12411,"children":12412},{"class":624,"line":4416},[12413],{"type":254,"tag":622,"props":12414,"children":12415},{"style":629},[12416],{"type":259,"value":12417},"            };\n",{"type":254,"tag":622,"props":12419,"children":12420},{"class":624,"line":4456},[12421,12425,12429,12433],{"type":254,"tag":622,"props":12422,"children":12423},{"style":1196},[12424],{"type":259,"value":9046},{"type":254,"tag":622,"props":12426,"children":12427},{"style":1202},[12428],{"type":259,"value":11836},{"type":254,"tag":622,"props":12430,"children":12431},{"style":629},[12432],{"type":259,"value":594},{"type":254,"tag":622,"props":12434,"children":12435},{"style":1202},[12436],{"type":259,"value":12437},"List\n",{"type":254,"tag":622,"props":12439,"children":12440},{"class":624,"line":4488},[12441,12445,12449,12453,12458],{"type":254,"tag":622,"props":12442,"children":12443},{"style":629},[12444],{"type":259,"value":9102},{"type":254,"tag":622,"props":12446,"children":12447},{"style":1735},[12448],{"type":259,"value":2768},{"type":254,"tag":622,"props":12450,"children":12451},{"style":629},[12452],{"type":259,"value":1668},{"type":254,"tag":622,"props":12454,"children":12455},{"style":714},[12456],{"type":259,"value":12457},"x",{"type":254,"tag":622,"props":12459,"children":12460},{"style":629},[12461],{"type":259,"value":6569},{"type":254,"tag":622,"props":12463,"children":12464},{"class":624,"line":4496},[12465,12470],{"type":254,"tag":622,"props":12466,"children":12467},{"style":629},[12468],{"type":259,"value":12469},"                new",{"type":254,"tag":622,"props":12471,"children":12472},{"style":714},[12473],{"type":259,"value":12474}," WeatherForecast\n",{"type":254,"tag":622,"props":12476,"children":12477},{"class":624,"line":4564},[12478],{"type":254,"tag":622,"props":12479,"children":12480},{"style":629},[12481],{"type":259,"value":12482},"                {\n",{"type":254,"tag":622,"props":12484,"children":12485},{"class":624,"line":4572},[12486,12491,12495,12500,12504,12509,12513,12518,12522,12527,12531,12535,12539,12544,12548,12552],{"type":254,"tag":622,"props":12487,"children":12488},{"style":1202},[12489],{"type":259,"value":12490},"                    Date ",{"type":254,"tag":622,"props":12492,"children":12493},{"style":629},[12494],{"type":259,"value":1654},{"type":254,"tag":622,"props":12496,"children":12497},{"style":1202},[12498],{"type":259,"value":12499}," DateOnly",{"type":254,"tag":622,"props":12501,"children":12502},{"style":629},[12503],{"type":259,"value":594},{"type":254,"tag":622,"props":12505,"children":12506},{"style":1735},[12507],{"type":259,"value":12508},"FromDateTime",{"type":254,"tag":622,"props":12510,"children":12511},{"style":629},[12512],{"type":259,"value":1668},{"type":254,"tag":622,"props":12514,"children":12515},{"style":1202},[12516],{"type":259,"value":12517},"DateTimeOffset",{"type":254,"tag":622,"props":12519,"children":12520},{"style":629},[12521],{"type":259,"value":594},{"type":254,"tag":622,"props":12523,"children":12524},{"style":1735},[12525],{"type":259,"value":12526},"FromUnixTimeSeconds",{"type":254,"tag":622,"props":12528,"children":12529},{"style":629},[12530],{"type":259,"value":1668},{"type":254,"tag":622,"props":12532,"children":12533},{"style":1202},[12534],{"type":259,"value":12457},{"type":254,"tag":622,"props":12536,"children":12537},{"style":629},[12538],{"type":259,"value":594},{"type":254,"tag":622,"props":12540,"children":12541},{"style":1202},[12542],{"type":259,"value":12543},"Dt",{"type":254,"tag":622,"props":12545,"children":12546},{"style":629},[12547],{"type":259,"value":2280},{"type":254,"tag":622,"props":12549,"children":12550},{"style":1202},[12551],{"type":259,"value":2140},{"type":254,"tag":622,"props":12553,"children":12554},{"style":629},[12555],{"type":259,"value":12556},"),\n",{"type":254,"tag":622,"props":12558,"children":12559},{"class":624,"line":4644},[12560,12565,12569,12574,12578,12583,12587,12591,12595,12600,12604,12609],{"type":254,"tag":622,"props":12561,"children":12562},{"style":1202},[12563],{"type":259,"value":12564},"                    TemperatureC ",{"type":254,"tag":622,"props":12566,"children":12567},{"style":629},[12568],{"type":259,"value":1654},{"type":254,"tag":622,"props":12570,"children":12571},{"style":1202},[12572],{"type":259,"value":12573}," Convert",{"type":254,"tag":622,"props":12575,"children":12576},{"style":629},[12577],{"type":259,"value":594},{"type":254,"tag":622,"props":12579,"children":12580},{"style":1735},[12581],{"type":259,"value":12582},"ToInt32",{"type":254,"tag":622,"props":12584,"children":12585},{"style":629},[12586],{"type":259,"value":1668},{"type":254,"tag":622,"props":12588,"children":12589},{"style":1202},[12590],{"type":259,"value":12457},{"type":254,"tag":622,"props":12592,"children":12593},{"style":629},[12594],{"type":259,"value":594},{"type":254,"tag":622,"props":12596,"children":12597},{"style":1202},[12598],{"type":259,"value":12599},"Main",{"type":254,"tag":622,"props":12601,"children":12602},{"style":629},[12603],{"type":259,"value":594},{"type":254,"tag":622,"props":12605,"children":12606},{"style":1202},[12607],{"type":259,"value":12608},"Temp",{"type":254,"tag":622,"props":12610,"children":12611},{"style":629},[12612],{"type":259,"value":12556},{"type":254,"tag":622,"props":12614,"children":12615},{"class":624,"line":4652},[12616,12621,12625,12629,12633,12637,12641,12645,12649,12653],{"type":254,"tag":622,"props":12617,"children":12618},{"style":1202},[12619],{"type":259,"value":12620},"                    Summary ",{"type":254,"tag":622,"props":12622,"children":12623},{"style":629},[12624],{"type":259,"value":1654},{"type":254,"tag":622,"props":12626,"children":12627},{"style":1735},[12628],{"type":259,"value":11915},{"type":254,"tag":622,"props":12630,"children":12631},{"style":629},[12632],{"type":259,"value":1668},{"type":254,"tag":622,"props":12634,"children":12635},{"style":1202},[12636],{"type":259,"value":12457},{"type":254,"tag":622,"props":12638,"children":12639},{"style":629},[12640],{"type":259,"value":594},{"type":254,"tag":622,"props":12642,"children":12643},{"style":1202},[12644],{"type":259,"value":12599},{"type":254,"tag":622,"props":12646,"children":12647},{"style":629},[12648],{"type":259,"value":594},{"type":254,"tag":622,"props":12650,"children":12651},{"style":1202},[12652],{"type":259,"value":12608},{"type":254,"tag":622,"props":12654,"children":12655},{"style":629},[12656],{"type":259,"value":1685},{"type":254,"tag":622,"props":12658,"children":12659},{"class":624,"line":4660},[12660],{"type":254,"tag":622,"props":12661,"children":12662},{"style":629},[12663],{"type":259,"value":12664},"                })\n",{"type":254,"tag":622,"props":12666,"children":12667},{"class":624,"line":4692},[12668,12672,12677],{"type":254,"tag":622,"props":12669,"children":12670},{"style":629},[12671],{"type":259,"value":9102},{"type":254,"tag":622,"props":12673,"children":12674},{"style":1735},[12675],{"type":259,"value":12676},"ToArray",{"type":254,"tag":622,"props":12678,"children":12679},{"style":629},[12680],{"type":259,"value":2798},{"type":254,"tag":622,"props":12682,"children":12683},{"class":624,"line":4700},[12684],{"type":254,"tag":622,"props":12685,"children":12686},{"style":629},[12687],{"type":259,"value":1093},{"type":254,"tag":622,"props":12689,"children":12690},{"class":624,"line":4756},[12691],{"type":254,"tag":622,"props":12692,"children":12693},{"style":629},[12694],{"type":259,"value":1111},{"type":254,"tag":255,"props":12696,"children":12697},{},[12698],{"type":259,"value":12699},"The weather forecasts of a specific geolocation are retrieved. Indeed coordinates (corresponding to Bordeaux in France) are passed to the Open Weather Map API call. In C# 12, we can alias any type so we can introduce an alias \"Coordinates\" for the coordinates tuple:",{"type":254,"tag":612,"props":12701,"children":12703},{"className":1423,"code":12702,"language":1425,"meta":205,"style":205},"using Coordinates = (double Latitude, double Longitude);\n\npublic class OpenWeatherService : IWeatherService\n{\n    private readonly IOpenWeatherMapApi _openWeatherMapApi;\n    private static readonly Coordinates BordeauxCoordinates = (44.837789, -0.57918\n",[12704],{"type":254,"tag":389,"props":12705,"children":12706},{"__ignoreMap":205},[12707,12747,12754,12777,12784,12807],{"type":254,"tag":622,"props":12708,"children":12709},{"class":624,"line":625},[12710,12714,12719,12723,12727,12731,12735,12739,12743],{"type":254,"tag":622,"props":12711,"children":12712},{"style":741},[12713],{"type":259,"value":1604},{"type":254,"tag":622,"props":12715,"children":12716},{"style":714},[12717],{"type":259,"value":12718}," Coordinates",{"type":254,"tag":622,"props":12720,"children":12721},{"style":629},[12722],{"type":259,"value":1619},{"type":254,"tag":622,"props":12724,"children":12725},{"style":629},[12726],{"type":259,"value":11646},{"type":254,"tag":622,"props":12728,"children":12729},{"style":714},[12730],{"type":259,"value":11651},{"type":254,"tag":622,"props":12732,"children":12733},{"style":629},[12734],{"type":259,"value":3149},{"type":254,"tag":622,"props":12736,"children":12737},{"style":629},[12738],{"type":259,"value":11660},{"type":254,"tag":622,"props":12740,"children":12741},{"style":714},[12742],{"type":259,"value":11665},{"type":254,"tag":622,"props":12744,"children":12745},{"style":629},[12746],{"type":259,"value":1554},{"type":254,"tag":622,"props":12748,"children":12749},{"class":624,"line":635},[12750],{"type":254,"tag":622,"props":12751,"children":12752},{"emptyLinePlaceholder":1699},[12753],{"type":259,"value":1702},{"type":254,"tag":622,"props":12755,"children":12756},{"class":624,"line":679},[12757,12761,12765,12769,12773],{"type":254,"tag":622,"props":12758,"children":12759},{"style":644},[12760],{"type":259,"value":1437},{"type":254,"tag":622,"props":12762,"children":12763},{"style":714},[12764],{"type":259,"value":6761},{"type":254,"tag":622,"props":12766,"children":12767},{"style":714},[12768],{"type":259,"value":11584},{"type":254,"tag":622,"props":12770,"children":12771},{"style":629},[12772],{"type":259,"value":3274},{"type":254,"tag":622,"props":12774,"children":12775},{"style":714},[12776],{"type":259,"value":11593},{"type":254,"tag":622,"props":12778,"children":12779},{"class":624,"line":705},[12780],{"type":254,"tag":622,"props":12781,"children":12782},{"style":629},[12783],{"type":259,"value":632},{"type":254,"tag":622,"props":12785,"children":12786},{"class":624,"line":732},[12787,12791,12795,12799,12803],{"type":254,"tag":622,"props":12788,"children":12789},{"style":644},[12790],{"type":259,"value":6790},{"type":254,"tag":622,"props":12792,"children":12793},{"style":644},[12794],{"type":259,"value":6795},{"type":254,"tag":622,"props":12796,"children":12797},{"style":714},[12798],{"type":259,"value":11616},{"type":254,"tag":622,"props":12800,"children":12801},{"style":714},[12802],{"type":259,"value":11621},{"type":254,"tag":622,"props":12804,"children":12805},{"style":629},[12806],{"type":259,"value":2350},{"type":254,"tag":622,"props":12808,"children":12809},{"class":624,"line":759},[12810,12814,12818,12822,12826,12830,12834,12838,12842,12846,12850],{"type":254,"tag":622,"props":12811,"children":12812},{"style":644},[12813],{"type":259,"value":6790},{"type":254,"tag":622,"props":12815,"children":12816},{"style":644},[12817],{"type":259,"value":11637},{"type":254,"tag":622,"props":12819,"children":12820},{"style":644},[12821],{"type":259,"value":6795},{"type":254,"tag":622,"props":12823,"children":12824},{"style":714},[12825],{"type":259,"value":12718},{"type":254,"tag":622,"props":12827,"children":12828},{"style":714},[12829],{"type":259,"value":11674},{"type":254,"tag":622,"props":12831,"children":12832},{"style":629},[12833],{"type":259,"value":1619},{"type":254,"tag":622,"props":12835,"children":12836},{"style":629},[12837],{"type":259,"value":3547},{"type":254,"tag":622,"props":12839,"children":12840},{"style":741},[12841],{"type":259,"value":11687},{"type":254,"tag":622,"props":12843,"children":12844},{"style":629},[12845],{"type":259,"value":3149},{"type":254,"tag":622,"props":12847,"children":12848},{"style":629},[12849],{"type":259,"value":10733},{"type":254,"tag":622,"props":12851,"children":12852},{"style":741},[12853],{"type":259,"value":12854},"0.57918\n",{"type":254,"tag":255,"props":12856,"children":12857},{},[12858,12860,12865,12867,12874],{"type":259,"value":12859},"Once this call is done, results are mapped to the expected model ",{"type":254,"tag":389,"props":12861,"children":12863},{"className":12862},[],[12864],{"type":259,"value":11233},{"type":259,"value":12866},". A lambda expression is used to get the \"weather summary\" from a temperature. If we want to have a default summary, that's something we can do thanks to the support of ",{"type":254,"tag":262,"props":12868,"children":12871},{"href":12869,"rel":12870},"https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12#default-lambda-parameters",[266],[12872],{"type":259,"value":12873},"default lambda parameters",{"type":259,"value":12875}," in C#12.",{"type":254,"tag":612,"props":12877,"children":12879},{"className":1423,"code":12878,"language":1425,"meta":205,"style":205},"var computeWeatherSummary = (double temperature, string defaultSummary = \"Warm\") =>\n    temperature switch\n    {\n        \u003C 0 => \"Freezing\",\n        >= 0 and \u003C 5 => \"Bracing\",\n        >= 5 and \u003C 12 => \"Chilly\",\n        >= 12 and \u003C 18 => \"Cool\",\n        >= 18 and \u003C 24 => \"Mild\",\n        >= 24 and \u003C 30 => \"Warm\",\n        >= 30 and \u003C 35 => \"Balmy\",\n        >= 35 and \u003C 40 => \"Hot\",\n        >= 40 and \u003C 45 => \"Sweltering\",\n        >= 45 => \"Scorching\",\n        _ => defaultSummary\n    };\n",[12880],{"type":254,"tag":389,"props":12881,"children":12882},{"__ignoreMap":205},[12883,12943,12955,12962,12994,13038,13081,13124,13167,13210,13253,13296,13339,13370,13387],{"type":254,"tag":622,"props":12884,"children":12885},{"class":624,"line":625},[12886,12890,12894,12898,12902,12906,12910,12914,12919,12923,12927,12931,12935,12939],{"type":254,"tag":622,"props":12887,"children":12888},{"style":714},[12889],{"type":259,"value":1710},{"type":254,"tag":622,"props":12891,"children":12892},{"style":714},[12893],{"type":259,"value":11915},{"type":254,"tag":622,"props":12895,"children":12896},{"style":629},[12897],{"type":259,"value":1619},{"type":254,"tag":622,"props":12899,"children":12900},{"style":629},[12901],{"type":259,"value":11646},{"type":254,"tag":622,"props":12903,"children":12904},{"style":714},[12905],{"type":259,"value":11928},{"type":254,"tag":622,"props":12907,"children":12908},{"style":629},[12909],{"type":259,"value":3149},{"type":254,"tag":622,"props":12911,"children":12912},{"style":629},[12913],{"type":259,"value":1975},{"type":254,"tag":622,"props":12915,"children":12916},{"style":714},[12917],{"type":259,"value":12918}," defaultSummary",{"type":254,"tag":622,"props":12920,"children":12921},{"style":629},[12922],{"type":259,"value":1619},{"type":254,"tag":622,"props":12924,"children":12925},{"style":629},[12926],{"type":259,"value":661},{"type":254,"tag":622,"props":12928,"children":12929},{"style":664},[12930],{"type":259,"value":12209},{"type":254,"tag":622,"props":12932,"children":12933},{"style":629},[12934],{"type":259,"value":652},{"type":254,"tag":622,"props":12936,"children":12937},{"style":629},[12938],{"type":259,"value":2309},{"type":254,"tag":622,"props":12940,"children":12941},{"style":629},[12942],{"type":259,"value":6569},{"type":254,"tag":622,"props":12944,"children":12945},{"class":624,"line":635},[12946,12951],{"type":254,"tag":622,"props":12947,"children":12948},{"style":1202},[12949],{"type":259,"value":12950},"    temperature ",{"type":254,"tag":622,"props":12952,"children":12953},{"style":1196},[12954],{"type":259,"value":11949},{"type":254,"tag":622,"props":12956,"children":12957},{"class":624,"line":679},[12958],{"type":254,"tag":622,"props":12959,"children":12960},{"style":629},[12961],{"type":259,"value":3574},{"type":254,"tag":622,"props":12963,"children":12964},{"class":624,"line":705},[12965,12970,12974,12978,12982,12986,12990],{"type":254,"tag":622,"props":12966,"children":12967},{"style":629},[12968],{"type":259,"value":12969},"        \u003C",{"type":254,"tag":622,"props":12971,"children":12972},{"style":741},[12973],{"type":259,"value":6038},{"type":254,"tag":622,"props":12975,"children":12976},{"style":629},[12977],{"type":259,"value":2697},{"type":254,"tag":622,"props":12979,"children":12980},{"style":629},[12981],{"type":259,"value":661},{"type":254,"tag":622,"props":12983,"children":12984},{"style":664},[12985],{"type":259,"value":11982},{"type":254,"tag":622,"props":12987,"children":12988},{"style":629},[12989],{"type":259,"value":652},{"type":254,"tag":622,"props":12991,"children":12992},{"style":629},[12993],{"type":259,"value":676},{"type":254,"tag":622,"props":12995,"children":12996},{"class":624,"line":732},[12997,13002,13006,13010,13014,13018,13022,13026,13030,13034],{"type":254,"tag":622,"props":12998,"children":12999},{"style":629},[13000],{"type":259,"value":13001},"        >=",{"type":254,"tag":622,"props":13003,"children":13004},{"style":741},[13005],{"type":259,"value":6038},{"type":254,"tag":622,"props":13007,"children":13008},{"style":629},[13009],{"type":259,"value":12007},{"type":254,"tag":622,"props":13011,"children":13012},{"style":629},[13013],{"type":259,"value":12012},{"type":254,"tag":622,"props":13015,"children":13016},{"style":741},[13017],{"type":259,"value":6144},{"type":254,"tag":622,"props":13019,"children":13020},{"style":629},[13021],{"type":259,"value":2697},{"type":254,"tag":622,"props":13023,"children":13024},{"style":629},[13025],{"type":259,"value":661},{"type":254,"tag":622,"props":13027,"children":13028},{"style":664},[13029],{"type":259,"value":12029},{"type":254,"tag":622,"props":13031,"children":13032},{"style":629},[13033],{"type":259,"value":652},{"type":254,"tag":622,"props":13035,"children":13036},{"style":629},[13037],{"type":259,"value":676},{"type":254,"tag":622,"props":13039,"children":13040},{"class":624,"line":759},[13041,13045,13049,13053,13057,13061,13065,13069,13073,13077],{"type":254,"tag":622,"props":13042,"children":13043},{"style":629},[13044],{"type":259,"value":13001},{"type":254,"tag":622,"props":13046,"children":13047},{"style":741},[13048],{"type":259,"value":6144},{"type":254,"tag":622,"props":13050,"children":13051},{"style":629},[13052],{"type":259,"value":12007},{"type":254,"tag":622,"props":13054,"children":13055},{"style":629},[13056],{"type":259,"value":12012},{"type":254,"tag":622,"props":13058,"children":13059},{"style":741},[13060],{"type":259,"value":12061},{"type":254,"tag":622,"props":13062,"children":13063},{"style":629},[13064],{"type":259,"value":2697},{"type":254,"tag":622,"props":13066,"children":13067},{"style":629},[13068],{"type":259,"value":661},{"type":254,"tag":622,"props":13070,"children":13071},{"style":664},[13072],{"type":259,"value":12074},{"type":254,"tag":622,"props":13074,"children":13075},{"style":629},[13076],{"type":259,"value":652},{"type":254,"tag":622,"props":13078,"children":13079},{"style":629},[13080],{"type":259,"value":676},{"type":254,"tag":622,"props":13082,"children":13083},{"class":624,"line":798},[13084,13088,13092,13096,13100,13104,13108,13112,13116,13120],{"type":254,"tag":622,"props":13085,"children":13086},{"style":629},[13087],{"type":259,"value":13001},{"type":254,"tag":622,"props":13089,"children":13090},{"style":741},[13091],{"type":259,"value":12061},{"type":254,"tag":622,"props":13093,"children":13094},{"style":629},[13095],{"type":259,"value":12007},{"type":254,"tag":622,"props":13097,"children":13098},{"style":629},[13099],{"type":259,"value":12012},{"type":254,"tag":622,"props":13101,"children":13102},{"style":741},[13103],{"type":259,"value":12106},{"type":254,"tag":622,"props":13105,"children":13106},{"style":629},[13107],{"type":259,"value":2697},{"type":254,"tag":622,"props":13109,"children":13110},{"style":629},[13111],{"type":259,"value":661},{"type":254,"tag":622,"props":13113,"children":13114},{"style":664},[13115],{"type":259,"value":12119},{"type":254,"tag":622,"props":13117,"children":13118},{"style":629},[13119],{"type":259,"value":652},{"type":254,"tag":622,"props":13121,"children":13122},{"style":629},[13123],{"type":259,"value":676},{"type":254,"tag":622,"props":13125,"children":13126},{"class":624,"line":836},[13127,13131,13135,13139,13143,13147,13151,13155,13159,13163],{"type":254,"tag":622,"props":13128,"children":13129},{"style":629},[13130],{"type":259,"value":13001},{"type":254,"tag":622,"props":13132,"children":13133},{"style":741},[13134],{"type":259,"value":12106},{"type":254,"tag":622,"props":13136,"children":13137},{"style":629},[13138],{"type":259,"value":12007},{"type":254,"tag":622,"props":13140,"children":13141},{"style":629},[13142],{"type":259,"value":12012},{"type":254,"tag":622,"props":13144,"children":13145},{"style":741},[13146],{"type":259,"value":12151},{"type":254,"tag":622,"props":13148,"children":13149},{"style":629},[13150],{"type":259,"value":2697},{"type":254,"tag":622,"props":13152,"children":13153},{"style":629},[13154],{"type":259,"value":661},{"type":254,"tag":622,"props":13156,"children":13157},{"style":664},[13158],{"type":259,"value":12164},{"type":254,"tag":622,"props":13160,"children":13161},{"style":629},[13162],{"type":259,"value":652},{"type":254,"tag":622,"props":13164,"children":13165},{"style":629},[13166],{"type":259,"value":676},{"type":254,"tag":622,"props":13168,"children":13169},{"class":624,"line":862},[13170,13174,13178,13182,13186,13190,13194,13198,13202,13206],{"type":254,"tag":622,"props":13171,"children":13172},{"style":629},[13173],{"type":259,"value":13001},{"type":254,"tag":622,"props":13175,"children":13176},{"style":741},[13177],{"type":259,"value":12151},{"type":254,"tag":622,"props":13179,"children":13180},{"style":629},[13181],{"type":259,"value":12007},{"type":254,"tag":622,"props":13183,"children":13184},{"style":629},[13185],{"type":259,"value":12012},{"type":254,"tag":622,"props":13187,"children":13188},{"style":741},[13189],{"type":259,"value":12196},{"type":254,"tag":622,"props":13191,"children":13192},{"style":629},[13193],{"type":259,"value":2697},{"type":254,"tag":622,"props":13195,"children":13196},{"style":629},[13197],{"type":259,"value":661},{"type":254,"tag":622,"props":13199,"children":13200},{"style":664},[13201],{"type":259,"value":12209},{"type":254,"tag":622,"props":13203,"children":13204},{"style":629},[13205],{"type":259,"value":652},{"type":254,"tag":622,"props":13207,"children":13208},{"style":629},[13209],{"type":259,"value":676},{"type":254,"tag":622,"props":13211,"children":13212},{"class":624,"line":900},[13213,13217,13221,13225,13229,13233,13237,13241,13245,13249],{"type":254,"tag":622,"props":13214,"children":13215},{"style":629},[13216],{"type":259,"value":13001},{"type":254,"tag":622,"props":13218,"children":13219},{"style":741},[13220],{"type":259,"value":12196},{"type":254,"tag":622,"props":13222,"children":13223},{"style":629},[13224],{"type":259,"value":12007},{"type":254,"tag":622,"props":13226,"children":13227},{"style":629},[13228],{"type":259,"value":12012},{"type":254,"tag":622,"props":13230,"children":13231},{"style":741},[13232],{"type":259,"value":12241},{"type":254,"tag":622,"props":13234,"children":13235},{"style":629},[13236],{"type":259,"value":2697},{"type":254,"tag":622,"props":13238,"children":13239},{"style":629},[13240],{"type":259,"value":661},{"type":254,"tag":622,"props":13242,"children":13243},{"style":664},[13244],{"type":259,"value":12254},{"type":254,"tag":622,"props":13246,"children":13247},{"style":629},[13248],{"type":259,"value":652},{"type":254,"tag":622,"props":13250,"children":13251},{"style":629},[13252],{"type":259,"value":676},{"type":254,"tag":622,"props":13254,"children":13255},{"class":624,"line":938},[13256,13260,13264,13268,13272,13276,13280,13284,13288,13292],{"type":254,"tag":622,"props":13257,"children":13258},{"style":629},[13259],{"type":259,"value":13001},{"type":254,"tag":622,"props":13261,"children":13262},{"style":741},[13263],{"type":259,"value":12241},{"type":254,"tag":622,"props":13265,"children":13266},{"style":629},[13267],{"type":259,"value":12007},{"type":254,"tag":622,"props":13269,"children":13270},{"style":629},[13271],{"type":259,"value":12012},{"type":254,"tag":622,"props":13273,"children":13274},{"style":741},[13275],{"type":259,"value":12286},{"type":254,"tag":622,"props":13277,"children":13278},{"style":629},[13279],{"type":259,"value":2697},{"type":254,"tag":622,"props":13281,"children":13282},{"style":629},[13283],{"type":259,"value":661},{"type":254,"tag":622,"props":13285,"children":13286},{"style":664},[13287],{"type":259,"value":12299},{"type":254,"tag":622,"props":13289,"children":13290},{"style":629},[13291],{"type":259,"value":652},{"type":254,"tag":622,"props":13293,"children":13294},{"style":629},[13295],{"type":259,"value":676},{"type":254,"tag":622,"props":13297,"children":13298},{"class":624,"line":976},[13299,13303,13307,13311,13315,13319,13323,13327,13331,13335],{"type":254,"tag":622,"props":13300,"children":13301},{"style":629},[13302],{"type":259,"value":13001},{"type":254,"tag":622,"props":13304,"children":13305},{"style":741},[13306],{"type":259,"value":12286},{"type":254,"tag":622,"props":13308,"children":13309},{"style":629},[13310],{"type":259,"value":12007},{"type":254,"tag":622,"props":13312,"children":13313},{"style":629},[13314],{"type":259,"value":12012},{"type":254,"tag":622,"props":13316,"children":13317},{"style":741},[13318],{"type":259,"value":12331},{"type":254,"tag":622,"props":13320,"children":13321},{"style":629},[13322],{"type":259,"value":2697},{"type":254,"tag":622,"props":13324,"children":13325},{"style":629},[13326],{"type":259,"value":661},{"type":254,"tag":622,"props":13328,"children":13329},{"style":664},[13330],{"type":259,"value":12344},{"type":254,"tag":622,"props":13332,"children":13333},{"style":629},[13334],{"type":259,"value":652},{"type":254,"tag":622,"props":13336,"children":13337},{"style":629},[13338],{"type":259,"value":676},{"type":254,"tag":622,"props":13340,"children":13341},{"class":624,"line":1014},[13342,13346,13350,13354,13358,13362,13366],{"type":254,"tag":622,"props":13343,"children":13344},{"style":629},[13345],{"type":259,"value":13001},{"type":254,"tag":622,"props":13347,"children":13348},{"style":741},[13349],{"type":259,"value":12331},{"type":254,"tag":622,"props":13351,"children":13352},{"style":629},[13353],{"type":259,"value":2697},{"type":254,"tag":622,"props":13355,"children":13356},{"style":629},[13357],{"type":259,"value":661},{"type":254,"tag":622,"props":13359,"children":13360},{"style":664},[13361],{"type":259,"value":12376},{"type":254,"tag":622,"props":13363,"children":13364},{"style":629},[13365],{"type":259,"value":652},{"type":254,"tag":622,"props":13367,"children":13368},{"style":629},[13369],{"type":259,"value":676},{"type":254,"tag":622,"props":13371,"children":13372},{"class":624,"line":1052},[13373,13378,13382],{"type":254,"tag":622,"props":13374,"children":13375},{"style":1202},[13376],{"type":259,"value":13377},"        _ ",{"type":254,"tag":622,"props":13379,"children":13380},{"style":629},[13381],{"type":259,"value":12397},{"type":254,"tag":622,"props":13383,"children":13384},{"style":1202},[13385],{"type":259,"value":13386}," defaultSummary\n",{"type":254,"tag":622,"props":13388,"children":13389},{"class":624,"line":1078},[13390],{"type":254,"tag":622,"props":13391,"children":13392},{"style":629},[13393],{"type":259,"value":13394},"    };\n",{"type":254,"tag":255,"props":13396,"children":13397},{},[13398,13403],{"type":254,"tag":389,"props":13399,"children":13401},{"className":13400},[],[13402],{"type":259,"value":11544},{"type":259,"value":13404}," does not have this logic because Summaries are randomly selected from an array containing possible summaries.",{"type":254,"tag":612,"props":13406,"children":13408},{"className":1423,"code":13407,"language":1425,"meta":205,"style":205},"private static readonly string[] Summaries = new [] { \"Freezing\", \"Bracing\", \"Chilly\", \"Cool\", \"Mild\", \"Warm\", \"Balmy\", \"Hot\", \"Sweltering\", \"Scorching\" };\n",[13409],{"type":254,"tag":389,"props":13410,"children":13411},{"__ignoreMap":205},[13412],{"type":254,"tag":622,"props":13413,"children":13414},{"class":624,"line":625},[13415,13420,13424,13428,13432,13437,13442,13446,13450,13455,13459,13463,13467,13471,13475,13479,13483,13487,13491,13495,13499,13503,13507,13511,13515,13519,13523,13527,13531,13535,13539,13543,13547,13551,13555,13559,13563,13567,13571,13575,13579,13583,13587,13591,13595,13599,13603,13607,13611,13615],{"type":254,"tag":622,"props":13416,"children":13417},{"style":644},[13418],{"type":259,"value":13419},"private",{"type":254,"tag":622,"props":13421,"children":13422},{"style":644},[13423],{"type":259,"value":11637},{"type":254,"tag":622,"props":13425,"children":13426},{"style":644},[13427],{"type":259,"value":6795},{"type":254,"tag":622,"props":13429,"children":13430},{"style":629},[13431],{"type":259,"value":1975},{"type":254,"tag":622,"props":13433,"children":13434},{"style":629},[13435],{"type":259,"value":13436},"[]",{"type":254,"tag":622,"props":13438,"children":13439},{"style":714},[13440],{"type":259,"value":13441}," Summaries",{"type":254,"tag":622,"props":13443,"children":13444},{"style":629},[13445],{"type":259,"value":1619},{"type":254,"tag":622,"props":13447,"children":13448},{"style":629},[13449],{"type":259,"value":1624},{"type":254,"tag":622,"props":13451,"children":13452},{"style":629},[13453],{"type":259,"value":13454}," []",{"type":254,"tag":622,"props":13456,"children":13457},{"style":629},[13458],{"type":259,"value":1990},{"type":254,"tag":622,"props":13460,"children":13461},{"style":629},[13462],{"type":259,"value":661},{"type":254,"tag":622,"props":13464,"children":13465},{"style":664},[13466],{"type":259,"value":11982},{"type":254,"tag":622,"props":13468,"children":13469},{"style":629},[13470],{"type":259,"value":652},{"type":254,"tag":622,"props":13472,"children":13473},{"style":629},[13474],{"type":259,"value":3149},{"type":254,"tag":622,"props":13476,"children":13477},{"style":629},[13478],{"type":259,"value":661},{"type":254,"tag":622,"props":13480,"children":13481},{"style":664},[13482],{"type":259,"value":12029},{"type":254,"tag":622,"props":13484,"children":13485},{"style":629},[13486],{"type":259,"value":652},{"type":254,"tag":622,"props":13488,"children":13489},{"style":629},[13490],{"type":259,"value":3149},{"type":254,"tag":622,"props":13492,"children":13493},{"style":629},[13494],{"type":259,"value":661},{"type":254,"tag":622,"props":13496,"children":13497},{"style":664},[13498],{"type":259,"value":12074},{"type":254,"tag":622,"props":13500,"children":13501},{"style":629},[13502],{"type":259,"value":652},{"type":254,"tag":622,"props":13504,"children":13505},{"style":629},[13506],{"type":259,"value":3149},{"type":254,"tag":622,"props":13508,"children":13509},{"style":629},[13510],{"type":259,"value":661},{"type":254,"tag":622,"props":13512,"children":13513},{"style":664},[13514],{"type":259,"value":12119},{"type":254,"tag":622,"props":13516,"children":13517},{"style":629},[13518],{"type":259,"value":652},{"type":254,"tag":622,"props":13520,"children":13521},{"style":629},[13522],{"type":259,"value":3149},{"type":254,"tag":622,"props":13524,"children":13525},{"style":629},[13526],{"type":259,"value":661},{"type":254,"tag":622,"props":13528,"children":13529},{"style":664},[13530],{"type":259,"value":12164},{"type":254,"tag":622,"props":13532,"children":13533},{"style":629},[13534],{"type":259,"value":652},{"type":254,"tag":622,"props":13536,"children":13537},{"style":629},[13538],{"type":259,"value":3149},{"type":254,"tag":622,"props":13540,"children":13541},{"style":629},[13542],{"type":259,"value":661},{"type":254,"tag":622,"props":13544,"children":13545},{"style":664},[13546],{"type":259,"value":12209},{"type":254,"tag":622,"props":13548,"children":13549},{"style":629},[13550],{"type":259,"value":652},{"type":254,"tag":622,"props":13552,"children":13553},{"style":629},[13554],{"type":259,"value":3149},{"type":254,"tag":622,"props":13556,"children":13557},{"style":629},[13558],{"type":259,"value":661},{"type":254,"tag":622,"props":13560,"children":13561},{"style":664},[13562],{"type":259,"value":12254},{"type":254,"tag":622,"props":13564,"children":13565},{"style":629},[13566],{"type":259,"value":652},{"type":254,"tag":622,"props":13568,"children":13569},{"style":629},[13570],{"type":259,"value":3149},{"type":254,"tag":622,"props":13572,"children":13573},{"style":629},[13574],{"type":259,"value":661},{"type":254,"tag":622,"props":13576,"children":13577},{"style":664},[13578],{"type":259,"value":12299},{"type":254,"tag":622,"props":13580,"children":13581},{"style":629},[13582],{"type":259,"value":652},{"type":254,"tag":622,"props":13584,"children":13585},{"style":629},[13586],{"type":259,"value":3149},{"type":254,"tag":622,"props":13588,"children":13589},{"style":629},[13590],{"type":259,"value":661},{"type":254,"tag":622,"props":13592,"children":13593},{"style":664},[13594],{"type":259,"value":12344},{"type":254,"tag":622,"props":13596,"children":13597},{"style":629},[13598],{"type":259,"value":652},{"type":254,"tag":622,"props":13600,"children":13601},{"style":629},[13602],{"type":259,"value":3149},{"type":254,"tag":622,"props":13604,"children":13605},{"style":629},[13606],{"type":259,"value":661},{"type":254,"tag":622,"props":13608,"children":13609},{"style":664},[13610],{"type":259,"value":12376},{"type":254,"tag":622,"props":13612,"children":13613},{"style":629},[13614],{"type":259,"value":652},{"type":254,"tag":622,"props":13616,"children":13617},{"style":629},[13618],{"type":259,"value":13619}," };\n",{"type":254,"tag":255,"props":13621,"children":13622},{},[13623,13625,13632],{"type":259,"value":13624},"With ",{"type":254,"tag":262,"props":13626,"children":13629},{"href":13627,"rel":13628},"https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12#collection-expressions",[266],[13630],{"type":259,"value":13631},"collection expressions",{"type":259,"value":13633},", this array can be defined directly with square brackets.",{"type":254,"tag":612,"props":13635,"children":13637},{"className":1423,"code":13636,"language":1425,"meta":205,"style":205},"private static readonly string[] Summaries = [ \"Freezing\", \"Bracing\", \"Chilly\", \"Cool\", \"Mild\", \"Warm\", \"Balmy\", \"Hot\", \"Sweltering\", \"Scorching\"];\n",[13638],{"type":254,"tag":389,"props":13639,"children":13640},{"__ignoreMap":205},[13641],{"type":254,"tag":622,"props":13642,"children":13643},{"class":624,"line":625},[13644,13648,13652,13656,13660,13664,13668,13672,13677,13681,13685,13689,13693,13697,13701,13705,13709,13713,13717,13721,13725,13729,13733,13737,13741,13745,13749,13753,13757,13761,13765,13769,13773,13777,13781,13785,13789,13793,13797,13801,13805,13809,13813,13817,13821,13825,13829,13833],{"type":254,"tag":622,"props":13645,"children":13646},{"style":644},[13647],{"type":259,"value":13419},{"type":254,"tag":622,"props":13649,"children":13650},{"style":644},[13651],{"type":259,"value":11637},{"type":254,"tag":622,"props":13653,"children":13654},{"style":644},[13655],{"type":259,"value":6795},{"type":254,"tag":622,"props":13657,"children":13658},{"style":629},[13659],{"type":259,"value":1975},{"type":254,"tag":622,"props":13661,"children":13662},{"style":629},[13663],{"type":259,"value":13436},{"type":254,"tag":622,"props":13665,"children":13666},{"style":714},[13667],{"type":259,"value":13441},{"type":254,"tag":622,"props":13669,"children":13670},{"style":629},[13671],{"type":259,"value":1619},{"type":254,"tag":622,"props":13673,"children":13674},{"style":629},[13675],{"type":259,"value":13676}," [",{"type":254,"tag":622,"props":13678,"children":13679},{"style":629},[13680],{"type":259,"value":661},{"type":254,"tag":622,"props":13682,"children":13683},{"style":664},[13684],{"type":259,"value":11982},{"type":254,"tag":622,"props":13686,"children":13687},{"style":629},[13688],{"type":259,"value":652},{"type":254,"tag":622,"props":13690,"children":13691},{"style":629},[13692],{"type":259,"value":3149},{"type":254,"tag":622,"props":13694,"children":13695},{"style":629},[13696],{"type":259,"value":661},{"type":254,"tag":622,"props":13698,"children":13699},{"style":664},[13700],{"type":259,"value":12029},{"type":254,"tag":622,"props":13702,"children":13703},{"style":629},[13704],{"type":259,"value":652},{"type":254,"tag":622,"props":13706,"children":13707},{"style":629},[13708],{"type":259,"value":3149},{"type":254,"tag":622,"props":13710,"children":13711},{"style":629},[13712],{"type":259,"value":661},{"type":254,"tag":622,"props":13714,"children":13715},{"style":664},[13716],{"type":259,"value":12074},{"type":254,"tag":622,"props":13718,"children":13719},{"style":629},[13720],{"type":259,"value":652},{"type":254,"tag":622,"props":13722,"children":13723},{"style":629},[13724],{"type":259,"value":3149},{"type":254,"tag":622,"props":13726,"children":13727},{"style":629},[13728],{"type":259,"value":661},{"type":254,"tag":622,"props":13730,"children":13731},{"style":664},[13732],{"type":259,"value":12119},{"type":254,"tag":622,"props":13734,"children":13735},{"style":629},[13736],{"type":259,"value":652},{"type":254,"tag":622,"props":13738,"children":13739},{"style":629},[13740],{"type":259,"value":3149},{"type":254,"tag":622,"props":13742,"children":13743},{"style":629},[13744],{"type":259,"value":661},{"type":254,"tag":622,"props":13746,"children":13747},{"style":664},[13748],{"type":259,"value":12164},{"type":254,"tag":622,"props":13750,"children":13751},{"style":629},[13752],{"type":259,"value":652},{"type":254,"tag":622,"props":13754,"children":13755},{"style":629},[13756],{"type":259,"value":3149},{"type":254,"tag":622,"props":13758,"children":13759},{"style":629},[13760],{"type":259,"value":661},{"type":254,"tag":622,"props":13762,"children":13763},{"style":664},[13764],{"type":259,"value":12209},{"type":254,"tag":622,"props":13766,"children":13767},{"style":629},[13768],{"type":259,"value":652},{"type":254,"tag":622,"props":13770,"children":13771},{"style":629},[13772],{"type":259,"value":3149},{"type":254,"tag":622,"props":13774,"children":13775},{"style":629},[13776],{"type":259,"value":661},{"type":254,"tag":622,"props":13778,"children":13779},{"style":664},[13780],{"type":259,"value":12254},{"type":254,"tag":622,"props":13782,"children":13783},{"style":629},[13784],{"type":259,"value":652},{"type":254,"tag":622,"props":13786,"children":13787},{"style":629},[13788],{"type":259,"value":3149},{"type":254,"tag":622,"props":13790,"children":13791},{"style":629},[13792],{"type":259,"value":661},{"type":254,"tag":622,"props":13794,"children":13795},{"style":664},[13796],{"type":259,"value":12299},{"type":254,"tag":622,"props":13798,"children":13799},{"style":629},[13800],{"type":259,"value":652},{"type":254,"tag":622,"props":13802,"children":13803},{"style":629},[13804],{"type":259,"value":3149},{"type":254,"tag":622,"props":13806,"children":13807},{"style":629},[13808],{"type":259,"value":661},{"type":254,"tag":622,"props":13810,"children":13811},{"style":664},[13812],{"type":259,"value":12344},{"type":254,"tag":622,"props":13814,"children":13815},{"style":629},[13816],{"type":259,"value":652},{"type":254,"tag":622,"props":13818,"children":13819},{"style":629},[13820],{"type":259,"value":3149},{"type":254,"tag":622,"props":13822,"children":13823},{"style":629},[13824],{"type":259,"value":661},{"type":254,"tag":622,"props":13826,"children":13827},{"style":664},[13828],{"type":259,"value":12376},{"type":254,"tag":622,"props":13830,"children":13831},{"style":629},[13832],{"type":259,"value":652},{"type":254,"tag":622,"props":13834,"children":13835},{"style":629},[13836],{"type":259,"value":13837},"];\n",{"type":254,"tag":255,"props":13839,"children":13840},{},[13841],{"type":259,"value":13842},"It would work with other types of collections as well. If we needed to have another list containing only cold summaries and avoid duplication between the two lists, we could also define the two lists and use the spread operator.",{"type":254,"tag":612,"props":13844,"children":13846},{"className":1423,"code":13845,"language":1425,"meta":205,"style":205}," private static readonly IList\u003Cstring> ColdAdjectives = [\"Freezing\", \"Bracing\", \"Chilly\", \"Cool\"];\n private static readonly string[] Summaries = [ ..ColdAdjectives, \"Mild\", \"Warm\", \"Balmy\", \"Hot\", \"Sweltering\", \"Scorching\"];\n",[13847],{"type":254,"tag":389,"props":13848,"children":13849},{"__ignoreMap":205},[13850,13960],{"type":254,"tag":622,"props":13851,"children":13852},{"class":624,"line":625},[13853,13858,13862,13866,13871,13875,13879,13883,13888,13892,13896,13900,13904,13908,13912,13916,13920,13924,13928,13932,13936,13940,13944,13948,13952,13956],{"type":254,"tag":622,"props":13854,"children":13855},{"style":644},[13856],{"type":259,"value":13857}," private",{"type":254,"tag":622,"props":13859,"children":13860},{"style":644},[13861],{"type":259,"value":11637},{"type":254,"tag":622,"props":13863,"children":13864},{"style":644},[13865],{"type":259,"value":6795},{"type":254,"tag":622,"props":13867,"children":13868},{"style":714},[13869],{"type":259,"value":13870}," IList",{"type":254,"tag":622,"props":13872,"children":13873},{"style":629},[13874],{"type":259,"value":1743},{"type":254,"tag":622,"props":13876,"children":13877},{"style":629},[13878],{"type":259,"value":2851},{"type":254,"tag":622,"props":13880,"children":13881},{"style":629},[13882],{"type":259,"value":6813},{"type":254,"tag":622,"props":13884,"children":13885},{"style":714},[13886],{"type":259,"value":13887}," ColdAdjectives",{"type":254,"tag":622,"props":13889,"children":13890},{"style":629},[13891],{"type":259,"value":1619},{"type":254,"tag":622,"props":13893,"children":13894},{"style":629},[13895],{"type":259,"value":13676},{"type":254,"tag":622,"props":13897,"children":13898},{"style":629},[13899],{"type":259,"value":652},{"type":254,"tag":622,"props":13901,"children":13902},{"style":664},[13903],{"type":259,"value":11982},{"type":254,"tag":622,"props":13905,"children":13906},{"style":629},[13907],{"type":259,"value":652},{"type":254,"tag":622,"props":13909,"children":13910},{"style":629},[13911],{"type":259,"value":3149},{"type":254,"tag":622,"props":13913,"children":13914},{"style":629},[13915],{"type":259,"value":661},{"type":254,"tag":622,"props":13917,"children":13918},{"style":664},[13919],{"type":259,"value":12029},{"type":254,"tag":622,"props":13921,"children":13922},{"style":629},[13923],{"type":259,"value":652},{"type":254,"tag":622,"props":13925,"children":13926},{"style":629},[13927],{"type":259,"value":3149},{"type":254,"tag":622,"props":13929,"children":13930},{"style":629},[13931],{"type":259,"value":661},{"type":254,"tag":622,"props":13933,"children":13934},{"style":664},[13935],{"type":259,"value":12074},{"type":254,"tag":622,"props":13937,"children":13938},{"style":629},[13939],{"type":259,"value":652},{"type":254,"tag":622,"props":13941,"children":13942},{"style":629},[13943],{"type":259,"value":3149},{"type":254,"tag":622,"props":13945,"children":13946},{"style":629},[13947],{"type":259,"value":661},{"type":254,"tag":622,"props":13949,"children":13950},{"style":664},[13951],{"type":259,"value":12119},{"type":254,"tag":622,"props":13953,"children":13954},{"style":629},[13955],{"type":259,"value":652},{"type":254,"tag":622,"props":13957,"children":13958},{"style":629},[13959],{"type":259,"value":13837},{"type":254,"tag":622,"props":13961,"children":13962},{"class":624,"line":635},[13963,13967,13971,13975,13979,13983,13987,13991,13995,14000,14005,14009,14013,14017,14021,14025,14029,14033,14037,14041,14045,14049,14053,14057,14061,14065,14069,14073,14077,14081,14085,14089,14093,14097,14101],{"type":254,"tag":622,"props":13964,"children":13965},{"style":644},[13966],{"type":259,"value":13857},{"type":254,"tag":622,"props":13968,"children":13969},{"style":644},[13970],{"type":259,"value":11637},{"type":254,"tag":622,"props":13972,"children":13973},{"style":644},[13974],{"type":259,"value":6795},{"type":254,"tag":622,"props":13976,"children":13977},{"style":629},[13978],{"type":259,"value":1975},{"type":254,"tag":622,"props":13980,"children":13981},{"style":629},[13982],{"type":259,"value":13436},{"type":254,"tag":622,"props":13984,"children":13985},{"style":714},[13986],{"type":259,"value":13441},{"type":254,"tag":622,"props":13988,"children":13989},{"style":629},[13990],{"type":259,"value":1619},{"type":254,"tag":622,"props":13992,"children":13993},{"style":629},[13994],{"type":259,"value":13676},{"type":254,"tag":622,"props":13996,"children":13997},{"style":629},[13998],{"type":259,"value":13999}," ..",{"type":254,"tag":622,"props":14001,"children":14002},{"style":1202},[14003],{"type":259,"value":14004},"ColdAdjectives",{"type":254,"tag":622,"props":14006,"children":14007},{"style":629},[14008],{"type":259,"value":3149},{"type":254,"tag":622,"props":14010,"children":14011},{"style":629},[14012],{"type":259,"value":661},{"type":254,"tag":622,"props":14014,"children":14015},{"style":664},[14016],{"type":259,"value":12164},{"type":254,"tag":622,"props":14018,"children":14019},{"style":629},[14020],{"type":259,"value":652},{"type":254,"tag":622,"props":14022,"children":14023},{"style":629},[14024],{"type":259,"value":3149},{"type":254,"tag":622,"props":14026,"children":14027},{"style":629},[14028],{"type":259,"value":661},{"type":254,"tag":622,"props":14030,"children":14031},{"style":664},[14032],{"type":259,"value":12209},{"type":254,"tag":622,"props":14034,"children":14035},{"style":629},[14036],{"type":259,"value":652},{"type":254,"tag":622,"props":14038,"children":14039},{"style":629},[14040],{"type":259,"value":3149},{"type":254,"tag":622,"props":14042,"children":14043},{"style":629},[14044],{"type":259,"value":661},{"type":254,"tag":622,"props":14046,"children":14047},{"style":664},[14048],{"type":259,"value":12254},{"type":254,"tag":622,"props":14050,"children":14051},{"style":629},[14052],{"type":259,"value":652},{"type":254,"tag":622,"props":14054,"children":14055},{"style":629},[14056],{"type":259,"value":3149},{"type":254,"tag":622,"props":14058,"children":14059},{"style":629},[14060],{"type":259,"value":661},{"type":254,"tag":622,"props":14062,"children":14063},{"style":664},[14064],{"type":259,"value":12299},{"type":254,"tag":622,"props":14066,"children":14067},{"style":629},[14068],{"type":259,"value":652},{"type":254,"tag":622,"props":14070,"children":14071},{"style":629},[14072],{"type":259,"value":3149},{"type":254,"tag":622,"props":14074,"children":14075},{"style":629},[14076],{"type":259,"value":661},{"type":254,"tag":622,"props":14078,"children":14079},{"style":664},[14080],{"type":259,"value":12344},{"type":254,"tag":622,"props":14082,"children":14083},{"style":629},[14084],{"type":259,"value":652},{"type":254,"tag":622,"props":14086,"children":14087},{"style":629},[14088],{"type":259,"value":3149},{"type":254,"tag":622,"props":14090,"children":14091},{"style":629},[14092],{"type":259,"value":661},{"type":254,"tag":622,"props":14094,"children":14095},{"style":664},[14096],{"type":259,"value":12376},{"type":254,"tag":622,"props":14098,"children":14099},{"style":629},[14100],{"type":259,"value":652},{"type":254,"tag":622,"props":14102,"children":14103},{"style":629},[14104],{"type":259,"value":13837},{"type":254,"tag":255,"props":14106,"children":14107},{},[14108],{"type":259,"value":14109},"The last C# 12 thing we could do in this example is to take advantage of the new class (and structs) primary constructors that were previously limited to records.",{"type":254,"tag":255,"props":14111,"children":14112},{},[14113,14114,14119],{"type":259,"value":10970},{"type":254,"tag":389,"props":14115,"children":14117},{"className":14116},[],[14118],{"type":259,"value":11233},{"type":259,"value":14120}," class could become the following:",{"type":254,"tag":612,"props":14122,"children":14124},{"className":1423,"code":14123,"language":1425,"meta":205,"style":205},"namespace WeatherApi;\n\npublic class WeatherForecast(DateOnly date, int temperatureC, string? summary)\n{\n    public int TemperatureC { get; } = temperatureC;\n    \n    public DateOnly Date { get; } = date;\n    \n    public string? Summary { get; } = summary;\n    \n    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);\n}\n",[14125],{"type":254,"tag":389,"props":14126,"children":14127},{"__ignoreMap":205},[14128,14145,14152,14217,14224,14269,14276,14319,14326,14374,14381,14434],{"type":254,"tag":622,"props":14129,"children":14130},{"class":624,"line":625},[14131,14136,14141],{"type":254,"tag":622,"props":14132,"children":14133},{"style":714},[14134],{"type":259,"value":14135},"namespace",{"type":254,"tag":622,"props":14137,"children":14138},{"style":1202},[14139],{"type":259,"value":14140}," WeatherApi",{"type":254,"tag":622,"props":14142,"children":14143},{"style":629},[14144],{"type":259,"value":2350},{"type":254,"tag":622,"props":14146,"children":14147},{"class":624,"line":635},[14148],{"type":254,"tag":622,"props":14149,"children":14150},{"emptyLinePlaceholder":1699},[14151],{"type":259,"value":1702},{"type":254,"tag":622,"props":14153,"children":14154},{"class":624,"line":679},[14155,14159,14163,14168,14172,14177,14182,14186,14191,14196,14200,14204,14208,14213],{"type":254,"tag":622,"props":14156,"children":14157},{"style":644},[14158],{"type":259,"value":1437},{"type":254,"tag":622,"props":14160,"children":14161},{"style":714},[14162],{"type":259,"value":6761},{"type":254,"tag":622,"props":14164,"children":14165},{"style":714},[14166],{"type":259,"value":14167}," WeatherForecast",{"type":254,"tag":622,"props":14169,"children":14170},{"style":629},[14171],{"type":259,"value":1668},{"type":254,"tag":622,"props":14173,"children":14174},{"style":714},[14175],{"type":259,"value":14176},"DateOnly",{"type":254,"tag":622,"props":14178,"children":14179},{"style":714},[14180],{"type":259,"value":14181}," date",{"type":254,"tag":622,"props":14183,"children":14184},{"style":629},[14185],{"type":259,"value":3149},{"type":254,"tag":622,"props":14187,"children":14188},{"style":629},[14189],{"type":259,"value":14190}," int",{"type":254,"tag":622,"props":14192,"children":14193},{"style":714},[14194],{"type":259,"value":14195}," temperatureC",{"type":254,"tag":622,"props":14197,"children":14198},{"style":629},[14199],{"type":259,"value":3149},{"type":254,"tag":622,"props":14201,"children":14202},{"style":629},[14203],{"type":259,"value":1975},{"type":254,"tag":622,"props":14205,"children":14206},{"style":629},[14207],{"type":259,"value":1980},{"type":254,"tag":622,"props":14209,"children":14210},{"style":714},[14211],{"type":259,"value":14212}," summary",{"type":254,"tag":622,"props":14214,"children":14215},{"style":629},[14216],{"type":259,"value":1685},{"type":254,"tag":622,"props":14218,"children":14219},{"class":624,"line":705},[14220],{"type":254,"tag":622,"props":14221,"children":14222},{"style":629},[14223],{"type":259,"value":632},{"type":254,"tag":622,"props":14225,"children":14226},{"class":624,"line":732},[14227,14231,14235,14240,14244,14248,14252,14257,14261,14265],{"type":254,"tag":622,"props":14228,"children":14229},{"style":644},[14230],{"type":259,"value":1970},{"type":254,"tag":622,"props":14232,"children":14233},{"style":629},[14234],{"type":259,"value":14190},{"type":254,"tag":622,"props":14236,"children":14237},{"style":714},[14238],{"type":259,"value":14239}," TemperatureC",{"type":254,"tag":622,"props":14241,"children":14242},{"style":629},[14243],{"type":259,"value":1990},{"type":254,"tag":622,"props":14245,"children":14246},{"style":714},[14247],{"type":259,"value":1995},{"type":254,"tag":622,"props":14249,"children":14250},{"style":629},[14251],{"type":259,"value":2000},{"type":254,"tag":622,"props":14253,"children":14254},{"style":629},[14255],{"type":259,"value":14256}," }",{"type":254,"tag":622,"props":14258,"children":14259},{"style":629},[14260],{"type":259,"value":1619},{"type":254,"tag":622,"props":14262,"children":14263},{"style":1202},[14264],{"type":259,"value":14195},{"type":254,"tag":622,"props":14266,"children":14267},{"style":629},[14268],{"type":259,"value":2350},{"type":254,"tag":622,"props":14270,"children":14271},{"class":624,"line":759},[14272],{"type":254,"tag":622,"props":14273,"children":14274},{"style":1202},[14275],{"type":259,"value":7336},{"type":254,"tag":622,"props":14277,"children":14278},{"class":624,"line":798},[14279,14283,14287,14291,14295,14299,14303,14307,14311,14315],{"type":254,"tag":622,"props":14280,"children":14281},{"style":644},[14282],{"type":259,"value":1970},{"type":254,"tag":622,"props":14284,"children":14285},{"style":714},[14286],{"type":259,"value":12499},{"type":254,"tag":622,"props":14288,"children":14289},{"style":714},[14290],{"type":259,"value":1482},{"type":254,"tag":622,"props":14292,"children":14293},{"style":629},[14294],{"type":259,"value":1990},{"type":254,"tag":622,"props":14296,"children":14297},{"style":714},[14298],{"type":259,"value":1995},{"type":254,"tag":622,"props":14300,"children":14301},{"style":629},[14302],{"type":259,"value":2000},{"type":254,"tag":622,"props":14304,"children":14305},{"style":629},[14306],{"type":259,"value":14256},{"type":254,"tag":622,"props":14308,"children":14309},{"style":629},[14310],{"type":259,"value":1619},{"type":254,"tag":622,"props":14312,"children":14313},{"style":1202},[14314],{"type":259,"value":14181},{"type":254,"tag":622,"props":14316,"children":14317},{"style":629},[14318],{"type":259,"value":2350},{"type":254,"tag":622,"props":14320,"children":14321},{"class":624,"line":836},[14322],{"type":254,"tag":622,"props":14323,"children":14324},{"style":1202},[14325],{"type":259,"value":7336},{"type":254,"tag":622,"props":14327,"children":14328},{"class":624,"line":862},[14329,14333,14337,14341,14346,14350,14354,14358,14362,14366,14370],{"type":254,"tag":622,"props":14330,"children":14331},{"style":644},[14332],{"type":259,"value":1970},{"type":254,"tag":622,"props":14334,"children":14335},{"style":629},[14336],{"type":259,"value":1975},{"type":254,"tag":622,"props":14338,"children":14339},{"style":629},[14340],{"type":259,"value":1980},{"type":254,"tag":622,"props":14342,"children":14343},{"style":714},[14344],{"type":259,"value":14345}," Summary",{"type":254,"tag":622,"props":14347,"children":14348},{"style":629},[14349],{"type":259,"value":1990},{"type":254,"tag":622,"props":14351,"children":14352},{"style":714},[14353],{"type":259,"value":1995},{"type":254,"tag":622,"props":14355,"children":14356},{"style":629},[14357],{"type":259,"value":2000},{"type":254,"tag":622,"props":14359,"children":14360},{"style":629},[14361],{"type":259,"value":14256},{"type":254,"tag":622,"props":14363,"children":14364},{"style":629},[14365],{"type":259,"value":1619},{"type":254,"tag":622,"props":14367,"children":14368},{"style":1202},[14369],{"type":259,"value":14212},{"type":254,"tag":622,"props":14371,"children":14372},{"style":629},[14373],{"type":259,"value":2350},{"type":254,"tag":622,"props":14375,"children":14376},{"class":624,"line":900},[14377],{"type":254,"tag":622,"props":14378,"children":14379},{"style":1202},[14380],{"type":259,"value":7336},{"type":254,"tag":622,"props":14382,"children":14383},{"class":624,"line":938},[14384,14388,14392,14397,14401,14406,14411,14416,14421,14425,14430],{"type":254,"tag":622,"props":14385,"children":14386},{"style":644},[14387],{"type":259,"value":1970},{"type":254,"tag":622,"props":14389,"children":14390},{"style":629},[14391],{"type":259,"value":14190},{"type":254,"tag":622,"props":14393,"children":14394},{"style":714},[14395],{"type":259,"value":14396}," TemperatureF",{"type":254,"tag":622,"props":14398,"children":14399},{"style":629},[14400],{"type":259,"value":2697},{"type":254,"tag":622,"props":14402,"children":14403},{"style":741},[14404],{"type":259,"value":14405}," 32",{"type":254,"tag":622,"props":14407,"children":14408},{"style":629},[14409],{"type":259,"value":14410}," +",{"type":254,"tag":622,"props":14412,"children":14413},{"style":629},[14414],{"type":259,"value":14415}," (int)(",{"type":254,"tag":622,"props":14417,"children":14418},{"style":1202},[14419],{"type":259,"value":14420},"TemperatureC ",{"type":254,"tag":622,"props":14422,"children":14423},{"style":629},[14424],{"type":259,"value":9768},{"type":254,"tag":622,"props":14426,"children":14427},{"style":741},[14428],{"type":259,"value":14429}," 0.5556",{"type":254,"tag":622,"props":14431,"children":14432},{"style":629},[14433],{"type":259,"value":1554},{"type":254,"tag":622,"props":14435,"children":14436},{"class":624,"line":976},[14437],{"type":254,"tag":622,"props":14438,"children":14439},{"style":629},[14440],{"type":259,"value":1111},{"type":254,"tag":427,"props":14442,"children":14443},{"icon":429},[14444],{"type":254,"tag":255,"props":14445,"children":14446},{},[14447],{"type":259,"value":14448},"I'm not sure this is completely relevant here, a record would probably be better but you get the idea.",{"type":254,"tag":255,"props":14450,"children":14451},{},[14452,14454,14460],{"type":259,"value":14453},"You can use primary constructors in any class, it will work as well with dependency injection. However, be aware that the services you used to assign to a private read-only field of your class won't be read-only anymore like ",{"type":254,"tag":389,"props":14455,"children":14457},{"className":14456},[],[14458],{"type":259,"value":14459},"weatherService",{"type":259,"value":14461}," in this example:",{"type":254,"tag":612,"props":14463,"children":14465},{"className":1423,"code":14464,"language":1425,"meta":205,"style":205},"public class WeatherForecastController(IWeatherService weatherService, ILogger\u003CWeatherForecastController> logger) : ControllerBase\n{\n    [HttpGet(Name = \"GetWeatherForecast\")]\n    [ProducesResponseType(typeof(WeatherForecast), StatusCodes.Status200OK)]\n    public Task\u003CWeatherForecast[]> Get()\n    {\n        return weatherService.GetWeatherForecasts();\n    }\n}\n",[14466],{"type":254,"tag":389,"props":14467,"children":14468},{"__ignoreMap":205},[14469,14532,14539,14578,14617,14648,14655,14678,14685],{"type":254,"tag":622,"props":14470,"children":14471},{"class":624,"line":625},[14472,14476,14480,14484,14488,14492,14496,14500,14504,14508,14512,14516,14520,14524,14528],{"type":254,"tag":622,"props":14473,"children":14474},{"style":644},[14475],{"type":259,"value":1437},{"type":254,"tag":622,"props":14477,"children":14478},{"style":714},[14479],{"type":259,"value":6761},{"type":254,"tag":622,"props":14481,"children":14482},{"style":714},[14483],{"type":259,"value":11053},{"type":254,"tag":622,"props":14485,"children":14486},{"style":629},[14487],{"type":259,"value":1668},{"type":254,"tag":622,"props":14489,"children":14490},{"style":714},[14491],{"type":259,"value":10928},{"type":254,"tag":622,"props":14493,"children":14494},{"style":714},[14495],{"type":259,"value":11125},{"type":254,"tag":622,"props":14497,"children":14498},{"style":629},[14499],{"type":259,"value":3149},{"type":254,"tag":622,"props":14501,"children":14502},{"style":714},[14503],{"type":259,"value":6800},{"type":254,"tag":622,"props":14505,"children":14506},{"style":629},[14507],{"type":259,"value":1743},{"type":254,"tag":622,"props":14509,"children":14510},{"style":714},[14511],{"type":259,"value":10976},{"type":254,"tag":622,"props":14513,"children":14514},{"style":629},[14515],{"type":259,"value":6813},{"type":254,"tag":622,"props":14517,"children":14518},{"style":714},[14519],{"type":259,"value":6891},{"type":254,"tag":622,"props":14521,"children":14522},{"style":629},[14523],{"type":259,"value":2309},{"type":254,"tag":622,"props":14525,"children":14526},{"style":629},[14527],{"type":259,"value":3274},{"type":254,"tag":622,"props":14529,"children":14530},{"style":714},[14531],{"type":259,"value":11062},{"type":254,"tag":622,"props":14533,"children":14534},{"class":624,"line":635},[14535],{"type":254,"tag":622,"props":14536,"children":14537},{"style":629},[14538],{"type":259,"value":632},{"type":254,"tag":622,"props":14540,"children":14541},{"class":624,"line":679},[14542,14546,14550,14554,14558,14562,14566,14570,14574],{"type":254,"tag":622,"props":14543,"children":14544},{"style":629},[14545],{"type":259,"value":9494},{"type":254,"tag":622,"props":14547,"children":14548},{"style":714},[14549],{"type":259,"value":11182},{"type":254,"tag":622,"props":14551,"children":14552},{"style":629},[14553],{"type":259,"value":1668},{"type":254,"tag":622,"props":14555,"children":14556},{"style":714},[14557],{"type":259,"value":2254},{"type":254,"tag":622,"props":14559,"children":14560},{"style":629},[14561],{"type":259,"value":1619},{"type":254,"tag":622,"props":14563,"children":14564},{"style":629},[14565],{"type":259,"value":661},{"type":254,"tag":622,"props":14567,"children":14568},{"style":664},[14569],{"type":259,"value":11203},{"type":254,"tag":622,"props":14571,"children":14572},{"style":629},[14573],{"type":259,"value":652},{"type":254,"tag":622,"props":14575,"children":14576},{"style":629},[14577],{"type":259,"value":11037},{"type":254,"tag":622,"props":14579,"children":14580},{"class":624,"line":705},[14581,14585,14589,14593,14597,14601,14605,14609,14613],{"type":254,"tag":622,"props":14582,"children":14583},{"style":629},[14584],{"type":259,"value":9494},{"type":254,"tag":622,"props":14586,"children":14587},{"style":714},[14588],{"type":259,"value":11223},{"type":254,"tag":622,"props":14590,"children":14591},{"style":629},[14592],{"type":259,"value":11228},{"type":254,"tag":622,"props":14594,"children":14595},{"style":714},[14596],{"type":259,"value":11233},{"type":254,"tag":622,"props":14598,"children":14599},{"style":629},[14600],{"type":259,"value":11238},{"type":254,"tag":622,"props":14602,"children":14603},{"style":1202},[14604],{"type":259,"value":11243},{"type":254,"tag":622,"props":14606,"children":14607},{"style":629},[14608],{"type":259,"value":594},{"type":254,"tag":622,"props":14610,"children":14611},{"style":1202},[14612],{"type":259,"value":11252},{"type":254,"tag":622,"props":14614,"children":14615},{"style":629},[14616],{"type":259,"value":11037},{"type":254,"tag":622,"props":14618,"children":14619},{"class":624,"line":732},[14620,14624,14628,14632,14636,14640,14644],{"type":254,"tag":622,"props":14621,"children":14622},{"style":644},[14623],{"type":259,"value":1970},{"type":254,"tag":622,"props":14625,"children":14626},{"style":714},[14627],{"type":259,"value":2842},{"type":254,"tag":622,"props":14629,"children":14630},{"style":629},[14631],{"type":259,"value":1743},{"type":254,"tag":622,"props":14633,"children":14634},{"style":714},[14635],{"type":259,"value":11233},{"type":254,"tag":622,"props":14637,"children":14638},{"style":629},[14639],{"type":259,"value":11280},{"type":254,"tag":622,"props":14641,"children":14642},{"style":1735},[14643],{"type":259,"value":11285},{"type":254,"tag":622,"props":14645,"children":14646},{"style":629},[14647],{"type":259,"value":1634},{"type":254,"tag":622,"props":14649,"children":14650},{"class":624,"line":759},[14651],{"type":254,"tag":622,"props":14652,"children":14653},{"style":629},[14654],{"type":259,"value":3574},{"type":254,"tag":622,"props":14656,"children":14657},{"class":624,"line":798},[14658,14662,14666,14670,14674],{"type":254,"tag":622,"props":14659,"children":14660},{"style":1196},[14661],{"type":259,"value":9046},{"type":254,"tag":622,"props":14663,"children":14664},{"style":1202},[14665],{"type":259,"value":11125},{"type":254,"tag":622,"props":14667,"children":14668},{"style":629},[14669],{"type":259,"value":594},{"type":254,"tag":622,"props":14671,"children":14672},{"style":1735},[14673],{"type":259,"value":11316},{"type":254,"tag":622,"props":14675,"children":14676},{"style":629},[14677],{"type":259,"value":2798},{"type":254,"tag":622,"props":14679,"children":14680},{"class":624,"line":836},[14681],{"type":254,"tag":622,"props":14682,"children":14683},{"style":629},[14684],{"type":259,"value":1093},{"type":254,"tag":622,"props":14686,"children":14687},{"class":624,"line":862},[14688],{"type":254,"tag":622,"props":14689,"children":14690},{"style":629},[14691],{"type":259,"value":1111},{"type":254,"tag":255,"props":14693,"children":14694},{},[14695,14697,14702,14704,14710,14712,14719],{"type":259,"value":14696},"Having 2 different implementations of the ",{"type":254,"tag":389,"props":14698,"children":14700},{"className":14699},[],[14701],{"type":259,"value":10928},{"type":259,"value":14703}," is great, but what if you need one of them in some part of your code? The one you will have injected in your class is the last one registered in the DI container, but that may not be the one you want. You could get all of them by injecting ",{"type":254,"tag":389,"props":14705,"children":14707},{"className":14706},[],[14708],{"type":259,"value":14709},"IEnumerable\u003CIWeatherService>",{"type":259,"value":14711}," and selecting the one you need. You could also create a sort of factory to retrieve the correct instance. Yet in .NET 8, you don't need to worry about all that because you have the ",{"type":254,"tag":262,"props":14713,"children":14716},{"href":14714,"rel":14715},"https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#keyed-di-services",[266],[14717],{"type":259,"value":14718},"keyed DI Services",{"type":259,"value":594},{"type":254,"tag":255,"props":14721,"children":14722},{},[14723],{"type":259,"value":14724},"Specifying a key (that can be anything, not necessarily a string) is done when registering the services in the DI container.",{"type":254,"tag":612,"props":14726,"children":14728},{"className":1423,"code":14727,"language":1425,"meta":205,"style":205},"builder.Services.AddKeyedTransient\u003CIWeatherService, RandomWeatherService>(\"random\");\nbuilder.Services.AddKeyedTransient\u003CIWeatherService, OpenWeatherService>(\"api\");\n",[14729],{"type":254,"tag":389,"props":14730,"children":14731},{"__ignoreMap":205},[14732,14794],{"type":254,"tag":622,"props":14733,"children":14734},{"class":624,"line":625},[14735,14739,14743,14747,14751,14756,14760,14764,14768,14773,14777,14781,14786,14790],{"type":254,"tag":622,"props":14736,"children":14737},{"style":1202},[14738],{"type":259,"value":6499},{"type":254,"tag":622,"props":14740,"children":14741},{"style":629},[14742],{"type":259,"value":594},{"type":254,"tag":622,"props":14744,"children":14745},{"style":1202},[14746],{"type":259,"value":6508},{"type":254,"tag":622,"props":14748,"children":14749},{"style":629},[14750],{"type":259,"value":594},{"type":254,"tag":622,"props":14752,"children":14753},{"style":1735},[14754],{"type":259,"value":14755},"AddKeyedTransient",{"type":254,"tag":622,"props":14757,"children":14758},{"style":629},[14759],{"type":259,"value":1743},{"type":254,"tag":622,"props":14761,"children":14762},{"style":714},[14763],{"type":259,"value":10928},{"type":254,"tag":622,"props":14765,"children":14766},{"style":629},[14767],{"type":259,"value":3149},{"type":254,"tag":622,"props":14769,"children":14770},{"style":714},[14771],{"type":259,"value":14772}," RandomWeatherService",{"type":254,"tag":622,"props":14774,"children":14775},{"style":629},[14776],{"type":259,"value":3517},{"type":254,"tag":622,"props":14778,"children":14779},{"style":629},[14780],{"type":259,"value":652},{"type":254,"tag":622,"props":14782,"children":14783},{"style":664},[14784],{"type":259,"value":14785},"random",{"type":254,"tag":622,"props":14787,"children":14788},{"style":629},[14789],{"type":259,"value":652},{"type":254,"tag":622,"props":14791,"children":14792},{"style":629},[14793],{"type":259,"value":1554},{"type":254,"tag":622,"props":14795,"children":14796},{"class":624,"line":635},[14797,14801,14805,14809,14813,14817,14821,14825,14829,14833,14837,14841,14846,14850],{"type":254,"tag":622,"props":14798,"children":14799},{"style":1202},[14800],{"type":259,"value":6499},{"type":254,"tag":622,"props":14802,"children":14803},{"style":629},[14804],{"type":259,"value":594},{"type":254,"tag":622,"props":14806,"children":14807},{"style":1202},[14808],{"type":259,"value":6508},{"type":254,"tag":622,"props":14810,"children":14811},{"style":629},[14812],{"type":259,"value":594},{"type":254,"tag":622,"props":14814,"children":14815},{"style":1735},[14816],{"type":259,"value":14755},{"type":254,"tag":622,"props":14818,"children":14819},{"style":629},[14820],{"type":259,"value":1743},{"type":254,"tag":622,"props":14822,"children":14823},{"style":714},[14824],{"type":259,"value":10928},{"type":254,"tag":622,"props":14826,"children":14827},{"style":629},[14828],{"type":259,"value":3149},{"type":254,"tag":622,"props":14830,"children":14831},{"style":714},[14832],{"type":259,"value":11584},{"type":254,"tag":622,"props":14834,"children":14835},{"style":629},[14836],{"type":259,"value":3517},{"type":254,"tag":622,"props":14838,"children":14839},{"style":629},[14840],{"type":259,"value":652},{"type":254,"tag":622,"props":14842,"children":14843},{"style":664},[14844],{"type":259,"value":14845},"api",{"type":254,"tag":622,"props":14847,"children":14848},{"style":629},[14849],{"type":259,"value":652},{"type":254,"tag":622,"props":14851,"children":14852},{"style":629},[14853],{"type":259,"value":1554},{"type":254,"tag":255,"props":14855,"children":14856},{},[14857],{"type":259,"value":14858},"With this key, retrieving a specific implementation becomes easy.",{"type":254,"tag":612,"props":14860,"children":14862},{"className":1423,"code":14861,"language":1425,"meta":205,"style":205},"    public WeatherForecastController([FromKeyedServices(\"random\")] IWeatherService weatherService, ILogger\u003CWeatherForecastController> logger)\n    {\n        _logger = logger;\n        _weatherService = weatherService;\n    }\n",[14863],{"type":254,"tag":389,"props":14864,"children":14865},{"__ignoreMap":205},[14866,14939,14946,14965,14984],{"type":254,"tag":622,"props":14867,"children":14868},{"class":624,"line":625},[14869,14873,14877,14881,14886,14890,14894,14898,14902,14906,14911,14915,14919,14923,14927,14931,14935],{"type":254,"tag":622,"props":14870,"children":14871},{"style":644},[14872],{"type":259,"value":1970},{"type":254,"tag":622,"props":14874,"children":14875},{"style":1735},[14876],{"type":259,"value":11053},{"type":254,"tag":622,"props":14878,"children":14879},{"style":629},[14880],{"type":259,"value":9535},{"type":254,"tag":622,"props":14882,"children":14883},{"style":1735},[14884],{"type":259,"value":14885},"FromKeyedServices",{"type":254,"tag":622,"props":14887,"children":14888},{"style":629},[14889],{"type":259,"value":1668},{"type":254,"tag":622,"props":14891,"children":14892},{"style":629},[14893],{"type":259,"value":652},{"type":254,"tag":622,"props":14895,"children":14896},{"style":664},[14897],{"type":259,"value":14785},{"type":254,"tag":622,"props":14899,"children":14900},{"style":629},[14901],{"type":259,"value":652},{"type":254,"tag":622,"props":14903,"children":14904},{"style":629},[14905],{"type":259,"value":9562},{"type":254,"tag":622,"props":14907,"children":14908},{"style":1202},[14909],{"type":259,"value":14910}," IWeatherService weatherService",{"type":254,"tag":622,"props":14912,"children":14913},{"style":629},[14914],{"type":259,"value":3149},{"type":254,"tag":622,"props":14916,"children":14917},{"style":1202},[14918],{"type":259,"value":6800},{"type":254,"tag":622,"props":14920,"children":14921},{"style":629},[14922],{"type":259,"value":1743},{"type":254,"tag":622,"props":14924,"children":14925},{"style":1202},[14926],{"type":259,"value":10976},{"type":254,"tag":622,"props":14928,"children":14929},{"style":629},[14930],{"type":259,"value":6813},{"type":254,"tag":622,"props":14932,"children":14933},{"style":1202},[14934],{"type":259,"value":6891},{"type":254,"tag":622,"props":14936,"children":14937},{"style":629},[14938],{"type":259,"value":1685},{"type":254,"tag":622,"props":14940,"children":14941},{"class":624,"line":635},[14942],{"type":254,"tag":622,"props":14943,"children":14944},{"style":629},[14945],{"type":259,"value":3574},{"type":254,"tag":622,"props":14947,"children":14948},{"class":624,"line":679},[14949,14953,14957,14961],{"type":254,"tag":622,"props":14950,"children":14951},{"style":1202},[14952],{"type":259,"value":6923},{"type":254,"tag":622,"props":14954,"children":14955},{"style":629},[14956],{"type":259,"value":1654},{"type":254,"tag":622,"props":14958,"children":14959},{"style":1202},[14960],{"type":259,"value":6891},{"type":254,"tag":622,"props":14962,"children":14963},{"style":629},[14964],{"type":259,"value":2350},{"type":254,"tag":622,"props":14966,"children":14967},{"class":624,"line":705},[14968,14972,14976,14980],{"type":254,"tag":622,"props":14969,"children":14970},{"style":1202},[14971],{"type":259,"value":11144},{"type":254,"tag":622,"props":14973,"children":14974},{"style":629},[14975],{"type":259,"value":1654},{"type":254,"tag":622,"props":14977,"children":14978},{"style":1202},[14979],{"type":259,"value":11125},{"type":254,"tag":622,"props":14981,"children":14982},{"style":629},[14983],{"type":259,"value":2350},{"type":254,"tag":622,"props":14985,"children":14986},{"class":624,"line":732},[14987],{"type":254,"tag":622,"props":14988,"children":14989},{"style":629},[14990],{"type":259,"value":1093},{"type":254,"tag":255,"props":14992,"children":14993},{},[14994,14996,15003],{"type":259,"value":14995},"I did not discuss the code that requests the Open Weather Map API. It's quite simple thanks to the uses of ",{"type":254,"tag":262,"props":14997,"children":15000},{"href":14998,"rel":14999},"https://github.com/reactiveui/refit",[266],[15001],{"type":259,"value":15002},"Refit",{"type":259,"value":594},{"type":254,"tag":612,"props":15005,"children":15007},{"className":1423,"code":15006,"language":1425,"meta":205,"style":205},"using Refit;\n\nnamespace WeatherApi.Services.OpenWeatherMap;\n\npublic interface IOpenWeatherMapApi\n{\n    [Get(\"/forecast?lat={latitude}&lon={longitude}&units=metric\")]\n    Task\u003CWeatherMapResponse> GetWeatherForecast(double latitude, double longitude);\n}\n\npublic record WeatherMapResponse(IList\u003CWeatherMapForecast> List);\n\npublic record WeatherMapForecast(int Dt, WeatherMapMain Main);\n\npublic record WeatherMapMain(double Temp);\n",[15008],{"type":254,"tag":389,"props":15009,"children":15010},{"__ignoreMap":205},[15011,15027,15034,15066,15073,15090,15097,15130,15183,15190,15197,15244,15251,15295,15302],{"type":254,"tag":622,"props":15012,"children":15013},{"class":624,"line":625},[15014,15018,15023],{"type":254,"tag":622,"props":15015,"children":15016},{"style":741},[15017],{"type":259,"value":1604},{"type":254,"tag":622,"props":15019,"children":15020},{"style":1202},[15021],{"type":259,"value":15022}," Refit",{"type":254,"tag":622,"props":15024,"children":15025},{"style":629},[15026],{"type":259,"value":2350},{"type":254,"tag":622,"props":15028,"children":15029},{"class":624,"line":635},[15030],{"type":254,"tag":622,"props":15031,"children":15032},{"emptyLinePlaceholder":1699},[15033],{"type":259,"value":1702},{"type":254,"tag":622,"props":15035,"children":15036},{"class":624,"line":679},[15037,15041,15045,15049,15053,15057,15062],{"type":254,"tag":622,"props":15038,"children":15039},{"style":714},[15040],{"type":259,"value":14135},{"type":254,"tag":622,"props":15042,"children":15043},{"style":1202},[15044],{"type":259,"value":14140},{"type":254,"tag":622,"props":15046,"children":15047},{"style":629},[15048],{"type":259,"value":594},{"type":254,"tag":622,"props":15050,"children":15051},{"style":1202},[15052],{"type":259,"value":6508},{"type":254,"tag":622,"props":15054,"children":15055},{"style":629},[15056],{"type":259,"value":594},{"type":254,"tag":622,"props":15058,"children":15059},{"style":1202},[15060],{"type":259,"value":15061},"OpenWeatherMap",{"type":254,"tag":622,"props":15063,"children":15064},{"style":629},[15065],{"type":259,"value":2350},{"type":254,"tag":622,"props":15067,"children":15068},{"class":624,"line":705},[15069],{"type":254,"tag":622,"props":15070,"children":15071},{"emptyLinePlaceholder":1699},[15072],{"type":259,"value":1702},{"type":254,"tag":622,"props":15074,"children":15075},{"class":624,"line":732},[15076,15080,15085],{"type":254,"tag":622,"props":15077,"children":15078},{"style":644},[15079],{"type":259,"value":1437},{"type":254,"tag":622,"props":15081,"children":15082},{"style":714},[15083],{"type":259,"value":15084}," interface",{"type":254,"tag":622,"props":15086,"children":15087},{"style":714},[15088],{"type":259,"value":15089}," IOpenWeatherMapApi\n",{"type":254,"tag":622,"props":15091,"children":15092},{"class":624,"line":759},[15093],{"type":254,"tag":622,"props":15094,"children":15095},{"style":629},[15096],{"type":259,"value":632},{"type":254,"tag":622,"props":15098,"children":15099},{"class":624,"line":798},[15100,15104,15109,15113,15117,15122,15126],{"type":254,"tag":622,"props":15101,"children":15102},{"style":629},[15103],{"type":259,"value":9494},{"type":254,"tag":622,"props":15105,"children":15106},{"style":714},[15107],{"type":259,"value":15108},"Get",{"type":254,"tag":622,"props":15110,"children":15111},{"style":629},[15112],{"type":259,"value":1668},{"type":254,"tag":622,"props":15114,"children":15115},{"style":629},[15116],{"type":259,"value":652},{"type":254,"tag":622,"props":15118,"children":15119},{"style":664},[15120],{"type":259,"value":15121},"/forecast?lat={latitude}&lon={longitude}&units=metric",{"type":254,"tag":622,"props":15123,"children":15124},{"style":629},[15125],{"type":259,"value":652},{"type":254,"tag":622,"props":15127,"children":15128},{"style":629},[15129],{"type":259,"value":11037},{"type":254,"tag":622,"props":15131,"children":15132},{"class":624,"line":836},[15133,15138,15142,15147,15151,15156,15161,15166,15170,15174,15179],{"type":254,"tag":622,"props":15134,"children":15135},{"style":714},[15136],{"type":259,"value":15137},"    Task",{"type":254,"tag":622,"props":15139,"children":15140},{"style":629},[15141],{"type":259,"value":1743},{"type":254,"tag":622,"props":15143,"children":15144},{"style":714},[15145],{"type":259,"value":15146},"WeatherMapResponse",{"type":254,"tag":622,"props":15148,"children":15149},{"style":629},[15150],{"type":259,"value":6813},{"type":254,"tag":622,"props":15152,"children":15153},{"style":1735},[15154],{"type":259,"value":15155}," GetWeatherForecast",{"type":254,"tag":622,"props":15157,"children":15158},{"style":629},[15159],{"type":259,"value":15160},"(double",{"type":254,"tag":622,"props":15162,"children":15163},{"style":714},[15164],{"type":259,"value":15165}," latitude",{"type":254,"tag":622,"props":15167,"children":15168},{"style":629},[15169],{"type":259,"value":3149},{"type":254,"tag":622,"props":15171,"children":15172},{"style":629},[15173],{"type":259,"value":11660},{"type":254,"tag":622,"props":15175,"children":15176},{"style":714},[15177],{"type":259,"value":15178}," longitude",{"type":254,"tag":622,"props":15180,"children":15181},{"style":629},[15182],{"type":259,"value":1554},{"type":254,"tag":622,"props":15184,"children":15185},{"class":624,"line":862},[15186],{"type":254,"tag":622,"props":15187,"children":15188},{"style":629},[15189],{"type":259,"value":1111},{"type":254,"tag":622,"props":15191,"children":15192},{"class":624,"line":900},[15193],{"type":254,"tag":622,"props":15194,"children":15195},{"emptyLinePlaceholder":1699},[15196],{"type":259,"value":1702},{"type":254,"tag":622,"props":15198,"children":15199},{"class":624,"line":938},[15200,15204,15208,15213,15217,15222,15226,15231,15235,15240],{"type":254,"tag":622,"props":15201,"children":15202},{"style":644},[15203],{"type":259,"value":1437},{"type":254,"tag":622,"props":15205,"children":15206},{"style":714},[15207],{"type":259,"value":1442},{"type":254,"tag":622,"props":15209,"children":15210},{"style":714},[15211],{"type":259,"value":15212}," WeatherMapResponse",{"type":254,"tag":622,"props":15214,"children":15215},{"style":629},[15216],{"type":259,"value":1668},{"type":254,"tag":622,"props":15218,"children":15219},{"style":714},[15220],{"type":259,"value":15221},"IList",{"type":254,"tag":622,"props":15223,"children":15224},{"style":629},[15225],{"type":259,"value":1743},{"type":254,"tag":622,"props":15227,"children":15228},{"style":714},[15229],{"type":259,"value":15230},"WeatherMapForecast",{"type":254,"tag":622,"props":15232,"children":15233},{"style":629},[15234],{"type":259,"value":6813},{"type":254,"tag":622,"props":15236,"children":15237},{"style":714},[15238],{"type":259,"value":15239}," List",{"type":254,"tag":622,"props":15241,"children":15242},{"style":629},[15243],{"type":259,"value":1554},{"type":254,"tag":622,"props":15245,"children":15246},{"class":624,"line":976},[15247],{"type":254,"tag":622,"props":15248,"children":15249},{"emptyLinePlaceholder":1699},[15250],{"type":259,"value":1702},{"type":254,"tag":622,"props":15252,"children":15253},{"class":624,"line":1014},[15254,15258,15262,15267,15272,15277,15281,15286,15291],{"type":254,"tag":622,"props":15255,"children":15256},{"style":644},[15257],{"type":259,"value":1437},{"type":254,"tag":622,"props":15259,"children":15260},{"style":714},[15261],{"type":259,"value":1442},{"type":254,"tag":622,"props":15263,"children":15264},{"style":714},[15265],{"type":259,"value":15266}," WeatherMapForecast",{"type":254,"tag":622,"props":15268,"children":15269},{"style":629},[15270],{"type":259,"value":15271},"(int",{"type":254,"tag":622,"props":15273,"children":15274},{"style":714},[15275],{"type":259,"value":15276}," Dt",{"type":254,"tag":622,"props":15278,"children":15279},{"style":629},[15280],{"type":259,"value":3149},{"type":254,"tag":622,"props":15282,"children":15283},{"style":714},[15284],{"type":259,"value":15285}," WeatherMapMain",{"type":254,"tag":622,"props":15287,"children":15288},{"style":714},[15289],{"type":259,"value":15290}," Main",{"type":254,"tag":622,"props":15292,"children":15293},{"style":629},[15294],{"type":259,"value":1554},{"type":254,"tag":622,"props":15296,"children":15297},{"class":624,"line":1052},[15298],{"type":254,"tag":622,"props":15299,"children":15300},{"emptyLinePlaceholder":1699},[15301],{"type":259,"value":1702},{"type":254,"tag":622,"props":15303,"children":15304},{"class":624,"line":1078},[15305,15309,15313,15317,15321,15326],{"type":254,"tag":622,"props":15306,"children":15307},{"style":644},[15308],{"type":259,"value":1437},{"type":254,"tag":622,"props":15310,"children":15311},{"style":714},[15312],{"type":259,"value":1442},{"type":254,"tag":622,"props":15314,"children":15315},{"style":714},[15316],{"type":259,"value":15285},{"type":254,"tag":622,"props":15318,"children":15319},{"style":629},[15320],{"type":259,"value":15160},{"type":254,"tag":622,"props":15322,"children":15323},{"style":714},[15324],{"type":259,"value":15325}," Temp",{"type":254,"tag":622,"props":15327,"children":15328},{"style":629},[15329],{"type":259,"value":1554},{"type":254,"tag":255,"props":15331,"children":15332},{},[15333,15335,15341],{"type":259,"value":15334},"I created an HTTP Message Handler to take care of adding the Open Weather Map API key to the requests. This API key and the URL to the API come from the configuration and are mapped to a configuration object ",{"type":254,"tag":389,"props":15336,"children":15338},{"className":15337},[],[15339],{"type":259,"value":15340},"WeatherMapConfiguration",{"type":259,"value":594},{"type":254,"tag":255,"props":15343,"children":15344},{},[15345,15347,15354,15356,15363],{"type":259,"value":15346},"In .NET 8, we can use ",{"type":254,"tag":262,"props":15348,"children":15351},{"href":15349,"rel":15350},"https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#data-validation",[266],[15352],{"type":259,"value":15353},"data validation attributes",{"type":259,"value":15355}," for data like configuration options. There is also a ",{"type":254,"tag":262,"props":15357,"children":15360},{"href":15358,"rel":15359},"https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#options-validation",[266],[15361],{"type":259,"value":15362},"source code generator",{"type":259,"value":15364}," that can implement the validation logic:",{"type":254,"tag":612,"props":15366,"children":15368},{"className":1423,"code":15367,"language":1425,"meta":205,"style":205},"namespace WeatherApi.Services.OpenWeatherMap;\n\npublic class WeatherMapConfiguration\n{\n    [Required]\n    public required string ApiKey { get; init; }\n\n    [Required]\n    [Url]\n    public required string Uri { get; init; }\n    \n}\n\n[OptionsValidator]\npublic partial class WeatherMapConfigurationValidator : IValidateOptions\u003CWeatherMapConfiguration>\n{\n}\n",[15369],{"type":254,"tag":389,"props":15370,"children":15371},{"__ignoreMap":205},[15372,15403,15410,15426,15433,15449,15495,15502,15517,15532,15575,15582,15589,15596,15612,15655,15662],{"type":254,"tag":622,"props":15373,"children":15374},{"class":624,"line":625},[15375,15379,15383,15387,15391,15395,15399],{"type":254,"tag":622,"props":15376,"children":15377},{"style":714},[15378],{"type":259,"value":14135},{"type":254,"tag":622,"props":15380,"children":15381},{"style":1202},[15382],{"type":259,"value":14140},{"type":254,"tag":622,"props":15384,"children":15385},{"style":629},[15386],{"type":259,"value":594},{"type":254,"tag":622,"props":15388,"children":15389},{"style":1202},[15390],{"type":259,"value":6508},{"type":254,"tag":622,"props":15392,"children":15393},{"style":629},[15394],{"type":259,"value":594},{"type":254,"tag":622,"props":15396,"children":15397},{"style":1202},[15398],{"type":259,"value":15061},{"type":254,"tag":622,"props":15400,"children":15401},{"style":629},[15402],{"type":259,"value":2350},{"type":254,"tag":622,"props":15404,"children":15405},{"class":624,"line":635},[15406],{"type":254,"tag":622,"props":15407,"children":15408},{"emptyLinePlaceholder":1699},[15409],{"type":259,"value":1702},{"type":254,"tag":622,"props":15411,"children":15412},{"class":624,"line":679},[15413,15417,15421],{"type":254,"tag":622,"props":15414,"children":15415},{"style":644},[15416],{"type":259,"value":1437},{"type":254,"tag":622,"props":15418,"children":15419},{"style":714},[15420],{"type":259,"value":6761},{"type":254,"tag":622,"props":15422,"children":15423},{"style":714},[15424],{"type":259,"value":15425}," WeatherMapConfiguration\n",{"type":254,"tag":622,"props":15427,"children":15428},{"class":624,"line":705},[15429],{"type":254,"tag":622,"props":15430,"children":15431},{"style":629},[15432],{"type":259,"value":632},{"type":254,"tag":622,"props":15434,"children":15435},{"class":624,"line":732},[15436,15440,15445],{"type":254,"tag":622,"props":15437,"children":15438},{"style":629},[15439],{"type":259,"value":9494},{"type":254,"tag":622,"props":15441,"children":15442},{"style":714},[15443],{"type":259,"value":15444},"Required",{"type":254,"tag":622,"props":15446,"children":15447},{"style":629},[15448],{"type":259,"value":11003},{"type":254,"tag":622,"props":15450,"children":15451},{"class":624,"line":759},[15452,15456,15461,15465,15470,15474,15478,15482,15487,15491],{"type":254,"tag":622,"props":15453,"children":15454},{"style":644},[15455],{"type":259,"value":1970},{"type":254,"tag":622,"props":15457,"children":15458},{"style":644},[15459],{"type":259,"value":15460}," required",{"type":254,"tag":622,"props":15462,"children":15463},{"style":629},[15464],{"type":259,"value":1975},{"type":254,"tag":622,"props":15466,"children":15467},{"style":714},[15468],{"type":259,"value":15469}," ApiKey",{"type":254,"tag":622,"props":15471,"children":15472},{"style":629},[15473],{"type":259,"value":1990},{"type":254,"tag":622,"props":15475,"children":15476},{"style":714},[15477],{"type":259,"value":1995},{"type":254,"tag":622,"props":15479,"children":15480},{"style":629},[15481],{"type":259,"value":2000},{"type":254,"tag":622,"props":15483,"children":15484},{"style":714},[15485],{"type":259,"value":15486}," init",{"type":254,"tag":622,"props":15488,"children":15489},{"style":629},[15490],{"type":259,"value":2000},{"type":254,"tag":622,"props":15492,"children":15493},{"style":629},[15494],{"type":259,"value":2014},{"type":254,"tag":622,"props":15496,"children":15497},{"class":624,"line":798},[15498],{"type":254,"tag":622,"props":15499,"children":15500},{"emptyLinePlaceholder":1699},[15501],{"type":259,"value":1702},{"type":254,"tag":622,"props":15503,"children":15504},{"class":624,"line":836},[15505,15509,15513],{"type":254,"tag":622,"props":15506,"children":15507},{"style":629},[15508],{"type":259,"value":9494},{"type":254,"tag":622,"props":15510,"children":15511},{"style":714},[15512],{"type":259,"value":15444},{"type":254,"tag":622,"props":15514,"children":15515},{"style":629},[15516],{"type":259,"value":11003},{"type":254,"tag":622,"props":15518,"children":15519},{"class":624,"line":862},[15520,15524,15528],{"type":254,"tag":622,"props":15521,"children":15522},{"style":629},[15523],{"type":259,"value":9494},{"type":254,"tag":622,"props":15525,"children":15526},{"style":714},[15527],{"type":259,"value":4176},{"type":254,"tag":622,"props":15529,"children":15530},{"style":629},[15531],{"type":259,"value":11003},{"type":254,"tag":622,"props":15533,"children":15534},{"class":624,"line":900},[15535,15539,15543,15547,15551,15555,15559,15563,15567,15571],{"type":254,"tag":622,"props":15536,"children":15537},{"style":644},[15538],{"type":259,"value":1970},{"type":254,"tag":622,"props":15540,"children":15541},{"style":644},[15542],{"type":259,"value":15460},{"type":254,"tag":622,"props":15544,"children":15545},{"style":629},[15546],{"type":259,"value":1975},{"type":254,"tag":622,"props":15548,"children":15549},{"style":714},[15550],{"type":259,"value":1663},{"type":254,"tag":622,"props":15552,"children":15553},{"style":629},[15554],{"type":259,"value":1990},{"type":254,"tag":622,"props":15556,"children":15557},{"style":714},[15558],{"type":259,"value":1995},{"type":254,"tag":622,"props":15560,"children":15561},{"style":629},[15562],{"type":259,"value":2000},{"type":254,"tag":622,"props":15564,"children":15565},{"style":714},[15566],{"type":259,"value":15486},{"type":254,"tag":622,"props":15568,"children":15569},{"style":629},[15570],{"type":259,"value":2000},{"type":254,"tag":622,"props":15572,"children":15573},{"style":629},[15574],{"type":259,"value":2014},{"type":254,"tag":622,"props":15576,"children":15577},{"class":624,"line":938},[15578],{"type":254,"tag":622,"props":15579,"children":15580},{"style":1202},[15581],{"type":259,"value":7336},{"type":254,"tag":622,"props":15583,"children":15584},{"class":624,"line":976},[15585],{"type":254,"tag":622,"props":15586,"children":15587},{"style":629},[15588],{"type":259,"value":1111},{"type":254,"tag":622,"props":15590,"children":15591},{"class":624,"line":1014},[15592],{"type":254,"tag":622,"props":15593,"children":15594},{"emptyLinePlaceholder":1699},[15595],{"type":259,"value":1702},{"type":254,"tag":622,"props":15597,"children":15598},{"class":624,"line":1052},[15599,15603,15608],{"type":254,"tag":622,"props":15600,"children":15601},{"style":629},[15602],{"type":259,"value":10993},{"type":254,"tag":622,"props":15604,"children":15605},{"style":714},[15606],{"type":259,"value":15607},"OptionsValidator",{"type":254,"tag":622,"props":15609,"children":15610},{"style":629},[15611],{"type":259,"value":11003},{"type":254,"tag":622,"props":15613,"children":15614},{"class":624,"line":1078},[15615,15619,15624,15628,15633,15637,15642,15646,15650],{"type":254,"tag":622,"props":15616,"children":15617},{"style":644},[15618],{"type":259,"value":1437},{"type":254,"tag":622,"props":15620,"children":15621},{"style":644},[15622],{"type":259,"value":15623}," partial",{"type":254,"tag":622,"props":15625,"children":15626},{"style":714},[15627],{"type":259,"value":6761},{"type":254,"tag":622,"props":15629,"children":15630},{"style":714},[15631],{"type":259,"value":15632}," WeatherMapConfigurationValidator",{"type":254,"tag":622,"props":15634,"children":15635},{"style":629},[15636],{"type":259,"value":3274},{"type":254,"tag":622,"props":15638,"children":15639},{"style":714},[15640],{"type":259,"value":15641}," IValidateOptions",{"type":254,"tag":622,"props":15643,"children":15644},{"style":629},[15645],{"type":259,"value":1743},{"type":254,"tag":622,"props":15647,"children":15648},{"style":714},[15649],{"type":259,"value":15340},{"type":254,"tag":622,"props":15651,"children":15652},{"style":629},[15653],{"type":259,"value":15654},">\n",{"type":254,"tag":622,"props":15656,"children":15657},{"class":624,"line":1087},[15658],{"type":254,"tag":622,"props":15659,"children":15660},{"style":629},[15661],{"type":259,"value":632},{"type":254,"tag":622,"props":15663,"children":15664},{"class":624,"line":1096},[15665],{"type":254,"tag":622,"props":15666,"children":15667},{"style":629},[15668],{"type":259,"value":1111},{"type":254,"tag":255,"props":15670,"children":15671},{},[15672,15674,15679,15681,15686],{"type":259,"value":15673},"This way we can make sure that the configuration contains the API Key and the URI that has the ",{"type":254,"tag":389,"props":15675,"children":15677},{"className":15676},[],[15678],{"type":259,"value":4176},{"type":259,"value":15680}," format. The configuration in the ",{"type":254,"tag":389,"props":15682,"children":15684},{"className":15683},[],[15685],{"type":259,"value":3667},{"type":259,"value":15687}," looks like that:",{"type":254,"tag":612,"props":15689,"children":15691},{"className":1423,"code":15690,"language":1425,"meta":205,"style":205},"builder.Services.Configure\u003CWeatherMapConfiguration>(builder.Configuration.GetSection(\"WeatherMap\"));\nbuilder.Services.AddSingleton\u003CIValidateOptions\u003CWeatherMapConfiguration>, WeatherMapConfigurationValidator>();\n\nbuilder.Services.AddTransient\u003CApiKeyHandler>();\nbuilder.Services.AddRefitClient\u003CIOpenWeatherMapApi>()\n    .ConfigureHttpClient((provider, client) =>\n    {\n        var configuration = provider.GetRequiredService\u003CIOptions\u003CWeatherMapConfiguration>>().Value;\n        client.BaseAddress = new Uri(configuration.Uri);\n    })\n    .AddHttpMessageHandler\u003CApiKeyHandler>();\n",[15692],{"type":254,"tag":389,"props":15693,"children":15694},{"__ignoreMap":205},[15695,15772,15826,15833,15870,15907,15946,15953,16010,16061,16069],{"type":254,"tag":622,"props":15696,"children":15697},{"class":624,"line":625},[15698,15702,15706,15710,15714,15719,15723,15727,15731,15735,15739,15743,15747,15751,15755,15759,15764,15768],{"type":254,"tag":622,"props":15699,"children":15700},{"style":1202},[15701],{"type":259,"value":6499},{"type":254,"tag":622,"props":15703,"children":15704},{"style":629},[15705],{"type":259,"value":594},{"type":254,"tag":622,"props":15707,"children":15708},{"style":1202},[15709],{"type":259,"value":6508},{"type":254,"tag":622,"props":15711,"children":15712},{"style":629},[15713],{"type":259,"value":594},{"type":254,"tag":622,"props":15715,"children":15716},{"style":1735},[15717],{"type":259,"value":15718},"Configure",{"type":254,"tag":622,"props":15720,"children":15721},{"style":629},[15722],{"type":259,"value":1743},{"type":254,"tag":622,"props":15724,"children":15725},{"style":714},[15726],{"type":259,"value":15340},{"type":254,"tag":622,"props":15728,"children":15729},{"style":629},[15730],{"type":259,"value":3517},{"type":254,"tag":622,"props":15732,"children":15733},{"style":1202},[15734],{"type":259,"value":6499},{"type":254,"tag":622,"props":15736,"children":15737},{"style":629},[15738],{"type":259,"value":594},{"type":254,"tag":622,"props":15740,"children":15741},{"style":1202},[15742],{"type":259,"value":2924},{"type":254,"tag":622,"props":15744,"children":15745},{"style":629},[15746],{"type":259,"value":594},{"type":254,"tag":622,"props":15748,"children":15749},{"style":1735},[15750],{"type":259,"value":8389},{"type":254,"tag":622,"props":15752,"children":15753},{"style":629},[15754],{"type":259,"value":1668},{"type":254,"tag":622,"props":15756,"children":15757},{"style":629},[15758],{"type":259,"value":652},{"type":254,"tag":622,"props":15760,"children":15761},{"style":664},[15762],{"type":259,"value":15763},"WeatherMap",{"type":254,"tag":622,"props":15765,"children":15766},{"style":629},[15767],{"type":259,"value":652},{"type":254,"tag":622,"props":15769,"children":15770},{"style":629},[15771],{"type":259,"value":6624},{"type":254,"tag":622,"props":15773,"children":15774},{"class":624,"line":635},[15775,15779,15783,15787,15791,15796,15800,15805,15809,15813,15818,15822],{"type":254,"tag":622,"props":15776,"children":15777},{"style":1202},[15778],{"type":259,"value":6499},{"type":254,"tag":622,"props":15780,"children":15781},{"style":629},[15782],{"type":259,"value":594},{"type":254,"tag":622,"props":15784,"children":15785},{"style":1202},[15786],{"type":259,"value":6508},{"type":254,"tag":622,"props":15788,"children":15789},{"style":629},[15790],{"type":259,"value":594},{"type":254,"tag":622,"props":15792,"children":15793},{"style":1735},[15794],{"type":259,"value":15795},"AddSingleton",{"type":254,"tag":622,"props":15797,"children":15798},{"style":629},[15799],{"type":259,"value":1743},{"type":254,"tag":622,"props":15801,"children":15802},{"style":714},[15803],{"type":259,"value":15804},"IValidateOptions",{"type":254,"tag":622,"props":15806,"children":15807},{"style":629},[15808],{"type":259,"value":1743},{"type":254,"tag":622,"props":15810,"children":15811},{"style":714},[15812],{"type":259,"value":15340},{"type":254,"tag":622,"props":15814,"children":15815},{"style":629},[15816],{"type":259,"value":15817},">,",{"type":254,"tag":622,"props":15819,"children":15820},{"style":714},[15821],{"type":259,"value":15632},{"type":254,"tag":622,"props":15823,"children":15824},{"style":629},[15825],{"type":259,"value":6531},{"type":254,"tag":622,"props":15827,"children":15828},{"class":624,"line":679},[15829],{"type":254,"tag":622,"props":15830,"children":15831},{"emptyLinePlaceholder":1699},[15832],{"type":259,"value":1702},{"type":254,"tag":622,"props":15834,"children":15835},{"class":624,"line":705},[15836,15840,15844,15848,15852,15857,15861,15866],{"type":254,"tag":622,"props":15837,"children":15838},{"style":1202},[15839],{"type":259,"value":6499},{"type":254,"tag":622,"props":15841,"children":15842},{"style":629},[15843],{"type":259,"value":594},{"type":254,"tag":622,"props":15845,"children":15846},{"style":1202},[15847],{"type":259,"value":6508},{"type":254,"tag":622,"props":15849,"children":15850},{"style":629},[15851],{"type":259,"value":594},{"type":254,"tag":622,"props":15853,"children":15854},{"style":1735},[15855],{"type":259,"value":15856},"AddTransient",{"type":254,"tag":622,"props":15858,"children":15859},{"style":629},[15860],{"type":259,"value":1743},{"type":254,"tag":622,"props":15862,"children":15863},{"style":714},[15864],{"type":259,"value":15865},"ApiKeyHandler",{"type":254,"tag":622,"props":15867,"children":15868},{"style":629},[15869],{"type":259,"value":6531},{"type":254,"tag":622,"props":15871,"children":15872},{"class":624,"line":732},[15873,15877,15881,15885,15889,15894,15898,15902],{"type":254,"tag":622,"props":15874,"children":15875},{"style":1202},[15876],{"type":259,"value":6499},{"type":254,"tag":622,"props":15878,"children":15879},{"style":629},[15880],{"type":259,"value":594},{"type":254,"tag":622,"props":15882,"children":15883},{"style":1202},[15884],{"type":259,"value":6508},{"type":254,"tag":622,"props":15886,"children":15887},{"style":629},[15888],{"type":259,"value":594},{"type":254,"tag":622,"props":15890,"children":15891},{"style":1735},[15892],{"type":259,"value":15893},"AddRefitClient",{"type":254,"tag":622,"props":15895,"children":15896},{"style":629},[15897],{"type":259,"value":1743},{"type":254,"tag":622,"props":15899,"children":15900},{"style":714},[15901],{"type":259,"value":11731},{"type":254,"tag":622,"props":15903,"children":15904},{"style":629},[15905],{"type":259,"value":15906},">()\n",{"type":254,"tag":622,"props":15908,"children":15909},{"class":624,"line":759},[15910,15914,15919,15924,15929,15933,15938,15942],{"type":254,"tag":622,"props":15911,"children":15912},{"style":629},[15913],{"type":259,"value":2678},{"type":254,"tag":622,"props":15915,"children":15916},{"style":1735},[15917],{"type":259,"value":15918},"ConfigureHttpClient",{"type":254,"tag":622,"props":15920,"children":15921},{"style":629},[15922],{"type":259,"value":15923},"((",{"type":254,"tag":622,"props":15925,"children":15926},{"style":714},[15927],{"type":259,"value":15928},"provider",{"type":254,"tag":622,"props":15930,"children":15931},{"style":629},[15932],{"type":259,"value":3149},{"type":254,"tag":622,"props":15934,"children":15935},{"style":714},[15936],{"type":259,"value":15937}," client",{"type":254,"tag":622,"props":15939,"children":15940},{"style":629},[15941],{"type":259,"value":2309},{"type":254,"tag":622,"props":15943,"children":15944},{"style":629},[15945],{"type":259,"value":6569},{"type":254,"tag":622,"props":15947,"children":15948},{"class":624,"line":798},[15949],{"type":254,"tag":622,"props":15950,"children":15951},{"style":629},[15952],{"type":259,"value":3574},{"type":254,"tag":622,"props":15954,"children":15955},{"class":624,"line":836},[15956,15960,15965,15969,15974,15978,15982,15986,15990,15994,15998,16002,16006],{"type":254,"tag":622,"props":15957,"children":15958},{"style":714},[15959],{"type":259,"value":3582},{"type":254,"tag":622,"props":15961,"children":15962},{"style":714},[15963],{"type":259,"value":15964}," configuration",{"type":254,"tag":622,"props":15966,"children":15967},{"style":629},[15968],{"type":259,"value":1619},{"type":254,"tag":622,"props":15970,"children":15971},{"style":1202},[15972],{"type":259,"value":15973}," provider",{"type":254,"tag":622,"props":15975,"children":15976},{"style":629},[15977],{"type":259,"value":594},{"type":254,"tag":622,"props":15979,"children":15980},{"style":1735},[15981],{"type":259,"value":9007},{"type":254,"tag":622,"props":15983,"children":15984},{"style":629},[15985],{"type":259,"value":1743},{"type":254,"tag":622,"props":15987,"children":15988},{"style":714},[15989],{"type":259,"value":9016},{"type":254,"tag":622,"props":15991,"children":15992},{"style":629},[15993],{"type":259,"value":1743},{"type":254,"tag":622,"props":15995,"children":15996},{"style":714},[15997],{"type":259,"value":15340},{"type":254,"tag":622,"props":15999,"children":16000},{"style":629},[16001],{"type":259,"value":9029},{"type":254,"tag":622,"props":16003,"children":16004},{"style":1202},[16005],{"type":259,"value":9034},{"type":254,"tag":622,"props":16007,"children":16008},{"style":629},[16009],{"type":259,"value":2350},{"type":254,"tag":622,"props":16011,"children":16012},{"class":624,"line":862},[16013,16018,16022,16027,16031,16035,16039,16043,16048,16052,16057],{"type":254,"tag":622,"props":16014,"children":16015},{"style":1202},[16016],{"type":259,"value":16017},"        client",{"type":254,"tag":622,"props":16019,"children":16020},{"style":629},[16021],{"type":259,"value":594},{"type":254,"tag":622,"props":16023,"children":16024},{"style":1202},[16025],{"type":259,"value":16026},"BaseAddress ",{"type":254,"tag":622,"props":16028,"children":16029},{"style":629},[16030],{"type":259,"value":1654},{"type":254,"tag":622,"props":16032,"children":16033},{"style":629},[16034],{"type":259,"value":1624},{"type":254,"tag":622,"props":16036,"children":16037},{"style":714},[16038],{"type":259,"value":1663},{"type":254,"tag":622,"props":16040,"children":16041},{"style":629},[16042],{"type":259,"value":1668},{"type":254,"tag":622,"props":16044,"children":16045},{"style":1202},[16046],{"type":259,"value":16047},"configuration",{"type":254,"tag":622,"props":16049,"children":16050},{"style":629},[16051],{"type":259,"value":594},{"type":254,"tag":622,"props":16053,"children":16054},{"style":1202},[16055],{"type":259,"value":16056},"Uri",{"type":254,"tag":622,"props":16058,"children":16059},{"style":629},[16060],{"type":259,"value":1554},{"type":254,"tag":622,"props":16062,"children":16063},{"class":624,"line":900},[16064],{"type":254,"tag":622,"props":16065,"children":16066},{"style":629},[16067],{"type":259,"value":16068},"    })\n",{"type":254,"tag":622,"props":16070,"children":16071},{"class":624,"line":938},[16072,16076,16081,16085,16089],{"type":254,"tag":622,"props":16073,"children":16074},{"style":629},[16075],{"type":259,"value":2678},{"type":254,"tag":622,"props":16077,"children":16078},{"style":1735},[16079],{"type":259,"value":16080},"AddHttpMessageHandler",{"type":254,"tag":622,"props":16082,"children":16083},{"style":629},[16084],{"type":259,"value":1743},{"type":254,"tag":622,"props":16086,"children":16087},{"style":714},[16088],{"type":259,"value":15865},{"type":254,"tag":622,"props":16090,"children":16091},{"style":629},[16092],{"type":259,"value":6531},{"type":254,"tag":282,"props":16094,"children":16096},{"id":16095},"a-few-closing-words",[16097],{"type":259,"value":16098},"A few closing words",{"type":254,"tag":255,"props":16100,"children":16101},{},[16102],{"type":259,"value":16103},"Here is the recap of what we talked about:",{"type":259,"value":16105},"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",{"type":254,"tag":16107,"props":16108,"children":16109},"table",{},[16110,16129],{"type":254,"tag":16111,"props":16112,"children":16113},"thead",{},[16114],{"type":254,"tag":16115,"props":16116,"children":16117},"tr",{},[16118,16124],{"type":254,"tag":16119,"props":16120,"children":16121},"th",{},[16122],{"type":259,"value":16123},"Feature",{"type":254,"tag":16119,"props":16125,"children":16126},{},[16127],{"type":259,"value":16128},"Area",{"type":254,"tag":16130,"props":16131,"children":16132},"tbody",{},[16133,16151,16169,16185,16201,16218,16236,16252],{"type":254,"tag":16115,"props":16134,"children":16135},{},[16136,16146],{"type":254,"tag":16137,"props":16138,"children":16139},"td",{},[16140],{"type":254,"tag":262,"props":16141,"children":16143},{"href":11350,"rel":16142},[266],[16144],{"type":259,"value":16145},"Support for generic attributes",{"type":254,"tag":16137,"props":16147,"children":16148},{},[16149],{"type":259,"value":16150},".NET 8",{"type":254,"tag":16115,"props":16152,"children":16153},{},[16154,16164],{"type":254,"tag":16137,"props":16155,"children":16156},{},[16157],{"type":254,"tag":262,"props":16158,"children":16161},{"href":16159,"rel":16160},"https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12#primary-constructors",[266],[16162],{"type":259,"value":16163},"Primary constructors",{"type":254,"tag":16137,"props":16165,"children":16166},{},[16167],{"type":259,"value":16168},"C# 12",{"type":254,"tag":16115,"props":16170,"children":16171},{},[16172,16181],{"type":254,"tag":16137,"props":16173,"children":16174},{},[16175],{"type":254,"tag":262,"props":16176,"children":16178},{"href":13627,"rel":16177},[266],[16179],{"type":259,"value":16180},"Collection expressions",{"type":254,"tag":16137,"props":16182,"children":16183},{},[16184],{"type":259,"value":16168},{"type":254,"tag":16115,"props":16186,"children":16187},{},[16188,16197],{"type":254,"tag":16137,"props":16189,"children":16190},{},[16191],{"type":254,"tag":262,"props":16192,"children":16194},{"href":12869,"rel":16193},[266],[16195],{"type":259,"value":16196},"Optional parameters in lambda expressions",{"type":254,"tag":16137,"props":16198,"children":16199},{},[16200],{"type":259,"value":16168},{"type":254,"tag":16115,"props":16202,"children":16203},{},[16204,16214],{"type":254,"tag":16137,"props":16205,"children":16206},{},[16207],{"type":254,"tag":262,"props":16208,"children":16211},{"href":16209,"rel":16210},"https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12#alias-any-type",[266],[16212],{"type":259,"value":16213},"Alias any type",{"type":254,"tag":16137,"props":16215,"children":16216},{},[16217],{"type":259,"value":16168},{"type":254,"tag":16115,"props":16219,"children":16220},{},[16221,16231],{"type":254,"tag":16137,"props":16222,"children":16223},{},[16224],{"type":254,"tag":262,"props":16225,"children":16228},{"href":16226,"rel":16227},"https://learn.microsoft.com/en-us/visualstudio/debugger/using-the-debuggerdisplay-attribute",[266],[16229],{"type":259,"value":16230},"Debug customization attributes on ASP.NET Core types      ",{"type":254,"tag":16137,"props":16232,"children":16233},{},[16234],{"type":259,"value":16235},"ASP.NET Core 8",{"type":254,"tag":16115,"props":16237,"children":16238},{},[16239,16248],{"type":254,"tag":16137,"props":16240,"children":16241},{},[16242],{"type":254,"tag":262,"props":16243,"children":16245},{"href":15358,"rel":16244},[266],[16246],{"type":259,"value":16247},"Options validation",{"type":254,"tag":16137,"props":16249,"children":16250},{},[16251],{"type":259,"value":16150},{"type":254,"tag":16115,"props":16253,"children":16254},{},[16255,16264],{"type":254,"tag":16137,"props":16256,"children":16257},{},[16258],{"type":254,"tag":262,"props":16259,"children":16261},{"href":14714,"rel":16260},[266],[16262],{"type":259,"value":16263},"Keyed DI Services",{"type":254,"tag":16137,"props":16265,"children":16266},{},[16267],{"type":259,"value":16150},{"type":254,"tag":255,"props":16269,"children":16270},{},[16271,16273,16279,16281,16286,16288,16294],{"type":259,"value":16272},"There are many more interesting features in ",{"type":254,"tag":262,"props":16274,"children":16277},{"href":16275,"rel":16276},"https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12",[266],[16278],{"type":259,"value":16168},{"type":259,"value":16280},", ",{"type":254,"tag":262,"props":16282,"children":16284},{"href":14714,"rel":16283},[266],[16285],{"type":259,"value":16150},{"type":259,"value":16287},", or ",{"type":254,"tag":262,"props":16289,"children":16292},{"href":16290,"rel":16291},"https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-8.0?view=aspnetcore-8.0",[266],[16293],{"type":259,"value":16235},{"type":259,"value":16295},". Yet, the ones I introduced in this article are the ones I will probably use the most.",{"type":254,"tag":255,"props":16297,"children":16298},{},[16299,16301,16307,16309,16315],{"type":259,"value":16300},"You can find the complete code sample ",{"type":254,"tag":262,"props":16302,"children":16305},{"href":16303,"rel":16304},"https://github.com/TechWatching/CodeAppAndInfraInDotnet8",[266],[16306],{"type":259,"value":1414},{"type":259,"value":16308},". The repository also contains a folder ",{"type":254,"tag":389,"props":16310,"children":16312},{"className":16311},[],[16313],{"type":259,"value":16314},"infra",{"type":259,"value":16316}," to set up the Azure infrastructure to host this API. 2 IaC solutions that use .NET are shown: one using Azure SDK and one using Pulumi.",{"type":254,"tag":255,"props":16318,"children":16319},{},[16320,16322,16329],{"type":259,"value":16321},"This article was published as part of the ",{"type":254,"tag":262,"props":16323,"children":16326},{"href":16324,"rel":16325},"https://www.csadvent.christmas/",[266],[16327],{"type":259,"value":16328},"C# Advent 2023",{"type":259,"value":16330}," which is a nice initiative. Make sure to check the other blog articles on the advent calendar.",{"type":254,"tag":1260,"props":16332,"children":16333},{},[16334],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":16336},[16337,16338,16339],{"id":10637,"depth":635,"text":10640},{"id":10901,"depth":635,"text":10904},{"id":16095,"depth":635,"text":16098},"content:1.posts:56.playing-with-dotnet8.md","1.posts/56.playing-with-dotnet8.md",{"_path":172,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":171,"description":16343,"lead":16344,"date":16345,"image":16346,"badge":16348,"tags":16350,"body":16357,"_type":1275,"_id":19559,"_source":231,"_file":19560,"_extension":1278},"What if we could script the creation and configuration of a GitHub Repository so that it is ready to provision or deploy Azure resources from a GitHub Actions pipeline? We will do that in this article using the Azure CLI and GitHub CLI.","Scripting your Azure-Ready GitHub Repository using Azure and GitHub CLI","2023-10-23T00:00:00.000Z",{"src":16347},"/images/azureOIDC_2.webp",{"label":16349},"DevOps",[6260,16351,16352,16353,16354,16355,16356],"Azure CLI","GitHub","GitHub CLI","GitHub Actions","OpenID Connect","Microsoft Entra ID",{"type":251,"children":16358,"toc":19548},[16359,16363,16369,16374,16379,16715,16729,16734,16743,16760,16766,16772,16786,16798,16810,16816,16829,16834,16903,16908,16994,17018,17023,17088,17119,17125,17130,17245,17276,17281,17452,17457,17589,17594,17803,17826,17839,17955,17960,18017,18023,18028,18046,18128,18133,18238,18247,18253,19493,19497,19510,19515,19534,19539,19544],{"type":254,"tag":255,"props":16360,"children":16361},{},[16362],{"type":259,"value":16343},{"type":254,"tag":282,"props":16364,"children":16366},{"id":16365},"the-objective",[16367],{"type":259,"value":16368},"The Objective",{"type":254,"tag":255,"props":16370,"children":16371},{},[16372],{"type":259,"value":16373},"The goal is to go from nothing to running a GitHub Actions workflow that authenticates to Azure using Open ID Connect (so without secret credentials) in a newly created GitHub repository.",{"type":254,"tag":255,"props":16375,"children":16376},{},[16377],{"type":259,"value":16378},"The workflow we plan to run is as follows:",{"type":254,"tag":612,"props":16380,"children":16383},{"className":16381,"code":16382,"language":230,"meta":205,"style":205},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","name: Run Azure Login with OIDC\non:\n  workflow_dispatch:\n\npermissions:\n  id-token: write\n  contents: read\njobs:\n  build-and-deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - name: 'Az CLI login'\n        uses: azure/login@v1\n        with:\n          client-id: ${{ secrets.AZURE_CLIENT_ID }}\n          tenant-id: ${{ secrets.AZURE_TENANT_ID }}\n          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}\n\n      - name: 'Run az commands'\n        run: |\n          az account show\n          az group list\n",[16384],{"type":254,"tag":389,"props":16385,"children":16386},{"__ignoreMap":205},[16387,16404,16417,16429,16436,16448,16465,16482,16494,16506,16523,16535,16567,16584,16596,16613,16630,16647,16654,16682,16699,16707],{"type":254,"tag":622,"props":16388,"children":16389},{"class":624,"line":625},[16390,16395,16399],{"type":254,"tag":622,"props":16391,"children":16392},{"style":768},[16393],{"type":259,"value":16394},"name",{"type":254,"tag":622,"props":16396,"children":16397},{"style":629},[16398],{"type":259,"value":474},{"type":254,"tag":622,"props":16400,"children":16401},{"style":664},[16402],{"type":259,"value":16403}," Run Azure Login with OIDC\n",{"type":254,"tag":622,"props":16405,"children":16406},{"class":624,"line":635},[16407,16412],{"type":254,"tag":622,"props":16408,"children":16409},{"style":7715},[16410],{"type":259,"value":16411},"on",{"type":254,"tag":622,"props":16413,"children":16414},{"style":629},[16415],{"type":259,"value":16416},":\n",{"type":254,"tag":622,"props":16418,"children":16419},{"class":624,"line":679},[16420,16425],{"type":254,"tag":622,"props":16421,"children":16422},{"style":768},[16423],{"type":259,"value":16424},"  workflow_dispatch",{"type":254,"tag":622,"props":16426,"children":16427},{"style":629},[16428],{"type":259,"value":16416},{"type":254,"tag":622,"props":16430,"children":16431},{"class":624,"line":705},[16432],{"type":254,"tag":622,"props":16433,"children":16434},{"emptyLinePlaceholder":1699},[16435],{"type":259,"value":1702},{"type":254,"tag":622,"props":16437,"children":16438},{"class":624,"line":732},[16439,16444],{"type":254,"tag":622,"props":16440,"children":16441},{"style":768},[16442],{"type":259,"value":16443},"permissions",{"type":254,"tag":622,"props":16445,"children":16446},{"style":629},[16447],{"type":259,"value":16416},{"type":254,"tag":622,"props":16449,"children":16450},{"class":624,"line":759},[16451,16456,16460],{"type":254,"tag":622,"props":16452,"children":16453},{"style":768},[16454],{"type":259,"value":16455},"  id-token",{"type":254,"tag":622,"props":16457,"children":16458},{"style":629},[16459],{"type":259,"value":474},{"type":254,"tag":622,"props":16461,"children":16462},{"style":664},[16463],{"type":259,"value":16464}," write\n",{"type":254,"tag":622,"props":16466,"children":16467},{"class":624,"line":798},[16468,16473,16477],{"type":254,"tag":622,"props":16469,"children":16470},{"style":768},[16471],{"type":259,"value":16472},"  contents",{"type":254,"tag":622,"props":16474,"children":16475},{"style":629},[16476],{"type":259,"value":474},{"type":254,"tag":622,"props":16478,"children":16479},{"style":664},[16480],{"type":259,"value":16481}," read\n",{"type":254,"tag":622,"props":16483,"children":16484},{"class":624,"line":836},[16485,16490],{"type":254,"tag":622,"props":16486,"children":16487},{"style":768},[16488],{"type":259,"value":16489},"jobs",{"type":254,"tag":622,"props":16491,"children":16492},{"style":629},[16493],{"type":259,"value":16416},{"type":254,"tag":622,"props":16495,"children":16496},{"class":624,"line":862},[16497,16502],{"type":254,"tag":622,"props":16498,"children":16499},{"style":768},[16500],{"type":259,"value":16501},"  build-and-deploy",{"type":254,"tag":622,"props":16503,"children":16504},{"style":629},[16505],{"type":259,"value":16416},{"type":254,"tag":622,"props":16507,"children":16508},{"class":624,"line":900},[16509,16514,16518],{"type":254,"tag":622,"props":16510,"children":16511},{"style":768},[16512],{"type":259,"value":16513},"    runs-on",{"type":254,"tag":622,"props":16515,"children":16516},{"style":629},[16517],{"type":259,"value":474},{"type":254,"tag":622,"props":16519,"children":16520},{"style":664},[16521],{"type":259,"value":16522}," ubuntu-latest\n",{"type":254,"tag":622,"props":16524,"children":16525},{"class":624,"line":938},[16526,16531],{"type":254,"tag":622,"props":16527,"children":16528},{"style":768},[16529],{"type":259,"value":16530},"    steps",{"type":254,"tag":622,"props":16532,"children":16533},{"style":629},[16534],{"type":259,"value":16416},{"type":254,"tag":622,"props":16536,"children":16537},{"class":624,"line":976},[16538,16543,16548,16552,16557,16562],{"type":254,"tag":622,"props":16539,"children":16540},{"style":629},[16541],{"type":259,"value":16542},"      -",{"type":254,"tag":622,"props":16544,"children":16545},{"style":768},[16546],{"type":259,"value":16547}," name",{"type":254,"tag":622,"props":16549,"children":16550},{"style":629},[16551],{"type":259,"value":474},{"type":254,"tag":622,"props":16553,"children":16554},{"style":629},[16555],{"type":259,"value":16556}," '",{"type":254,"tag":622,"props":16558,"children":16559},{"style":664},[16560],{"type":259,"value":16561},"Az CLI login",{"type":254,"tag":622,"props":16563,"children":16564},{"style":629},[16565],{"type":259,"value":16566},"'\n",{"type":254,"tag":622,"props":16568,"children":16569},{"class":624,"line":1014},[16570,16575,16579],{"type":254,"tag":622,"props":16571,"children":16572},{"style":768},[16573],{"type":259,"value":16574},"        uses",{"type":254,"tag":622,"props":16576,"children":16577},{"style":629},[16578],{"type":259,"value":474},{"type":254,"tag":622,"props":16580,"children":16581},{"style":664},[16582],{"type":259,"value":16583}," azure/login@v1\n",{"type":254,"tag":622,"props":16585,"children":16586},{"class":624,"line":1052},[16587,16592],{"type":254,"tag":622,"props":16588,"children":16589},{"style":768},[16590],{"type":259,"value":16591},"        with",{"type":254,"tag":622,"props":16593,"children":16594},{"style":629},[16595],{"type":259,"value":16416},{"type":254,"tag":622,"props":16597,"children":16598},{"class":624,"line":1078},[16599,16604,16608],{"type":254,"tag":622,"props":16600,"children":16601},{"style":768},[16602],{"type":259,"value":16603},"          client-id",{"type":254,"tag":622,"props":16605,"children":16606},{"style":629},[16607],{"type":259,"value":474},{"type":254,"tag":622,"props":16609,"children":16610},{"style":664},[16611],{"type":259,"value":16612}," ${{ secrets.AZURE_CLIENT_ID }}\n",{"type":254,"tag":622,"props":16614,"children":16615},{"class":624,"line":1087},[16616,16621,16625],{"type":254,"tag":622,"props":16617,"children":16618},{"style":768},[16619],{"type":259,"value":16620},"          tenant-id",{"type":254,"tag":622,"props":16622,"children":16623},{"style":629},[16624],{"type":259,"value":474},{"type":254,"tag":622,"props":16626,"children":16627},{"style":664},[16628],{"type":259,"value":16629}," ${{ secrets.AZURE_TENANT_ID }}\n",{"type":254,"tag":622,"props":16631,"children":16632},{"class":624,"line":1096},[16633,16638,16642],{"type":254,"tag":622,"props":16634,"children":16635},{"style":768},[16636],{"type":259,"value":16637},"          subscription-id",{"type":254,"tag":622,"props":16639,"children":16640},{"style":629},[16641],{"type":259,"value":474},{"type":254,"tag":622,"props":16643,"children":16644},{"style":664},[16645],{"type":259,"value":16646}," ${{ secrets.AZURE_SUBSCRIPTION_ID }}\n",{"type":254,"tag":622,"props":16648,"children":16649},{"class":624,"line":1105},[16650],{"type":254,"tag":622,"props":16651,"children":16652},{"emptyLinePlaceholder":1699},[16653],{"type":259,"value":1702},{"type":254,"tag":622,"props":16655,"children":16656},{"class":624,"line":4131},[16657,16661,16665,16669,16673,16678],{"type":254,"tag":622,"props":16658,"children":16659},{"style":629},[16660],{"type":259,"value":16542},{"type":254,"tag":622,"props":16662,"children":16663},{"style":768},[16664],{"type":259,"value":16547},{"type":254,"tag":622,"props":16666,"children":16667},{"style":629},[16668],{"type":259,"value":474},{"type":254,"tag":622,"props":16670,"children":16671},{"style":629},[16672],{"type":259,"value":16556},{"type":254,"tag":622,"props":16674,"children":16675},{"style":664},[16676],{"type":259,"value":16677},"Run az commands",{"type":254,"tag":622,"props":16679,"children":16680},{"style":629},[16681],{"type":259,"value":16566},{"type":254,"tag":622,"props":16683,"children":16684},{"class":624,"line":4183},[16685,16690,16694],{"type":254,"tag":622,"props":16686,"children":16687},{"style":768},[16688],{"type":259,"value":16689},"        run",{"type":254,"tag":622,"props":16691,"children":16692},{"style":629},[16693],{"type":259,"value":474},{"type":254,"tag":622,"props":16695,"children":16696},{"style":1196},[16697],{"type":259,"value":16698}," |\n",{"type":254,"tag":622,"props":16700,"children":16701},{"class":624,"line":4208},[16702],{"type":254,"tag":622,"props":16703,"children":16704},{"style":664},[16705],{"type":259,"value":16706},"          az account show\n",{"type":254,"tag":622,"props":16708,"children":16709},{"class":624,"line":4216},[16710],{"type":254,"tag":622,"props":16711,"children":16712},{"style":664},[16713],{"type":259,"value":16714},"          az group list\n",{"type":254,"tag":255,"props":16716,"children":16717},{},[16718,16720,16727],{"type":259,"value":16719},"This workflow is an example coming from ",{"type":254,"tag":262,"props":16721,"children":16724},{"href":16722,"rel":16723},"https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure",[266],[16725],{"type":259,"value":16726},"the GitHub documentation",{"type":259,"value":16728}," showing how to configure GitHub Actions workflow to access Azure resources protected by Microsoft Entra.",{"type":254,"tag":255,"props":16730,"children":16731},{},[16732],{"type":259,"value":16733},"To run this workflow we will need to automate the configuration of these resources:",{"type":254,"tag":255,"props":16735,"children":16736},{},[16737],{"type":254,"tag":1161,"props":16738,"children":16742},{"alt":16739,"className":16740,"src":16741},"A diagram showing the interactions between Azure and GitHub.",[1165,1166],"/posts/images/scripting_azurereadygithub_azure_1.webp",[],{"type":254,"tag":427,"props":16744,"children":16745},{"icon":429},[16746],{"type":254,"tag":255,"props":16747,"children":16748},{},[16749,16751,16758],{"type":259,"value":16750},"Looks familiar? That's the same diagram from my article about ",{"type":254,"tag":262,"props":16752,"children":16755},{"href":16753,"rel":16754},"https://www.techwatching.dev/posts/azure-ready-github-repository",[266],[16756],{"type":259,"value":16757},"creating an Azure-Ready GitHub Repository using Pulumi",{"type":259,"value":16759},". The purpose was the same but using Pulumi instead of CLI tools. If you prefer a declarative Infrastructure as Code approach using programming languages over CLI tools, you should definitively read it 😉",{"type":254,"tag":282,"props":16761,"children":16763},{"id":16762},"the-script",[16764],{"type":259,"value":16765},"The Script",{"type":254,"tag":455,"props":16767,"children":16769},{"id":16768},"a-word-about-the-tools-used",[16770],{"type":259,"value":16771},"A word about the tools used",{"type":254,"tag":255,"props":16773,"children":16774},{},[16775,16777,16784],{"type":259,"value":16776},"I will be using ",{"type":254,"tag":262,"props":16778,"children":16781},{"href":16779,"rel":16780},"https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.3",[266],[16782],{"type":259,"value":16783},"PowerShell which is cross-platform",{"type":259,"value":16785},". However, if you prefer using a different shell, you will simply need to adjust some syntax (such as the environment variable declarations) to ensure compatibility.",{"type":254,"tag":255,"props":16787,"children":16788},{},[16789,16791,16797],{"type":259,"value":16790},"To create and configure the Microsoft Entra ID resources, we will need the ",{"type":254,"tag":262,"props":16792,"children":16795},{"href":16793,"rel":16794},"https://learn.microsoft.com/en-us/cli/azure/install-azure-cli",[266],[16796],{"type":259,"value":16351},{"type":259,"value":594},{"type":254,"tag":255,"props":16799,"children":16800},{},[16801,16803,16809],{"type":259,"value":16802},"To create and configure the GitHub repository, we will need the ",{"type":254,"tag":262,"props":16804,"children":16807},{"href":16805,"rel":16806},"https://cli.github.com/",[266],[16808],{"type":259,"value":16353},{"type":259,"value":594},{"type":254,"tag":455,"props":16811,"children":16813},{"id":16812},"create-the-repository-on-github",[16814],{"type":259,"value":16815},"Create the repository on GitHub",{"type":254,"tag":255,"props":16817,"children":16818},{},[16819,16821,16827],{"type":259,"value":16820},"Let's assume we are already in a new directory with the YAML workflow file ",{"type":254,"tag":389,"props":16822,"children":16824},{"className":16823},[],[16825],{"type":259,"value":16826},".github\\workflows\\main.yml",{"type":259,"value":16828}," in it.",{"type":254,"tag":255,"props":16830,"children":16831},{},[16832],{"type":259,"value":16833},"First, we can initialize the git repository.",{"type":254,"tag":612,"props":16835,"children":16839},{"className":16836,"code":16837,"language":16838,"meta":205,"style":205},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","git init\ngit add .\ngit commit -m \"Intialize repository with the GitHub Actions workflow file\"\n","bash",[16840],{"type":254,"tag":389,"props":16841,"children":16842},{"__ignoreMap":205},[16843,16856,16873],{"type":254,"tag":622,"props":16844,"children":16845},{"class":624,"line":625},[16846,16851],{"type":254,"tag":622,"props":16847,"children":16848},{"style":714},[16849],{"type":259,"value":16850},"git",{"type":254,"tag":622,"props":16852,"children":16853},{"style":664},[16854],{"type":259,"value":16855}," init\n",{"type":254,"tag":622,"props":16857,"children":16858},{"class":624,"line":635},[16859,16863,16868],{"type":254,"tag":622,"props":16860,"children":16861},{"style":714},[16862],{"type":259,"value":16850},{"type":254,"tag":622,"props":16864,"children":16865},{"style":664},[16866],{"type":259,"value":16867}," add",{"type":254,"tag":622,"props":16869,"children":16870},{"style":664},[16871],{"type":259,"value":16872}," .\n",{"type":254,"tag":622,"props":16874,"children":16875},{"class":624,"line":679},[16876,16880,16885,16890,16894,16899],{"type":254,"tag":622,"props":16877,"children":16878},{"style":714},[16879],{"type":259,"value":16850},{"type":254,"tag":622,"props":16881,"children":16882},{"style":664},[16883],{"type":259,"value":16884}," commit",{"type":254,"tag":622,"props":16886,"children":16887},{"style":664},[16888],{"type":259,"value":16889}," -m",{"type":254,"tag":622,"props":16891,"children":16892},{"style":629},[16893],{"type":259,"value":661},{"type":254,"tag":622,"props":16895,"children":16896},{"style":664},[16897],{"type":259,"value":16898},"Intialize repository with the GitHub Actions workflow file",{"type":254,"tag":622,"props":16900,"children":16901},{"style":629},[16902],{"type":259,"value":9909},{"type":254,"tag":255,"props":16904,"children":16905},{},[16906],{"type":259,"value":16907},"Second, we can create the GitHub repository and push the git repository we just initialized in it.",{"type":254,"tag":612,"props":16909,"children":16911},{"className":10656,"code":16910,"language":10658,"meta":205,"style":205},"$repositoryName = \"MyAzureReadyRepository\"\ngh repo create $repositoryName --private --source=. --push\n",[16912],{"type":254,"tag":389,"props":16913,"children":16914},{"__ignoreMap":205},[16915,16945],{"type":254,"tag":622,"props":16916,"children":16917},{"class":624,"line":625},[16918,16923,16928,16932,16936,16941],{"type":254,"tag":622,"props":16919,"children":16920},{"style":629},[16921],{"type":259,"value":16922},"$",{"type":254,"tag":622,"props":16924,"children":16925},{"style":1202},[16926],{"type":259,"value":16927},"repositoryName ",{"type":254,"tag":622,"props":16929,"children":16930},{"style":629},[16931],{"type":259,"value":1654},{"type":254,"tag":622,"props":16933,"children":16934},{"style":629},[16935],{"type":259,"value":661},{"type":254,"tag":622,"props":16937,"children":16938},{"style":664},[16939],{"type":259,"value":16940},"MyAzureReadyRepository",{"type":254,"tag":622,"props":16942,"children":16943},{"style":629},[16944],{"type":259,"value":9909},{"type":254,"tag":622,"props":16946,"children":16947},{"class":624,"line":635},[16948,16953,16957,16961,16965,16970,16974,16979,16984,16989],{"type":254,"tag":622,"props":16949,"children":16950},{"style":1202},[16951],{"type":259,"value":16952},"gh repo create ",{"type":254,"tag":622,"props":16954,"children":16955},{"style":629},[16956],{"type":259,"value":16922},{"type":254,"tag":622,"props":16958,"children":16959},{"style":1202},[16960],{"type":259,"value":16927},{"type":254,"tag":622,"props":16962,"children":16963},{"style":629},[16964],{"type":259,"value":10675},{"type":254,"tag":622,"props":16966,"children":16967},{"style":1202},[16968],{"type":259,"value":16969},"private ",{"type":254,"tag":622,"props":16971,"children":16972},{"style":629},[16973],{"type":259,"value":10675},{"type":254,"tag":622,"props":16975,"children":16976},{"style":1202},[16977],{"type":259,"value":16978},"source",{"type":254,"tag":622,"props":16980,"children":16981},{"style":629},[16982],{"type":259,"value":16983},"=.",{"type":254,"tag":622,"props":16985,"children":16986},{"style":629},[16987],{"type":259,"value":16988}," --",{"type":254,"tag":622,"props":16990,"children":16991},{"style":1202},[16992],{"type":259,"value":16993},"push\n",{"type":254,"tag":427,"props":16995,"children":16996},{"icon":567},[16997],{"type":254,"tag":255,"props":16998,"children":16999},{},[17000,17002,17008,17010,17016],{"type":259,"value":17001},"You can use the ",{"type":254,"tag":389,"props":17003,"children":17005},{"className":17004},[],[17006],{"type":259,"value":17007},"--public",{"type":259,"value":17009}," flag instead of the ",{"type":254,"tag":389,"props":17011,"children":17013},{"className":17012},[],[17014],{"type":259,"value":17015},"--private",{"type":259,"value":17017}," one if you want your GitHub repository to be public.",{"type":254,"tag":255,"props":17019,"children":17020},{},[17021],{"type":259,"value":17022},"The repository's full name (containing the organization name) can be retrieved like this:",{"type":254,"tag":612,"props":17024,"children":17026},{"className":10656,"code":17025,"language":10658,"meta":205,"style":205},"$repositoryFullName=$(gh repo view --json nameWithOwner -q \".nameWithOwner\")\n",[17027],{"type":254,"tag":389,"props":17028,"children":17029},{"__ignoreMap":205},[17030],{"type":254,"tag":622,"props":17031,"children":17032},{"class":624,"line":625},[17033,17037,17042,17047,17052,17056,17061,17066,17071,17075,17080,17084],{"type":254,"tag":622,"props":17034,"children":17035},{"style":629},[17036],{"type":259,"value":16922},{"type":254,"tag":622,"props":17038,"children":17039},{"style":1202},[17040],{"type":259,"value":17041},"repositoryFullName",{"type":254,"tag":622,"props":17043,"children":17044},{"style":629},[17045],{"type":259,"value":17046},"=$(",{"type":254,"tag":622,"props":17048,"children":17049},{"style":1202},[17050],{"type":259,"value":17051},"gh repo view ",{"type":254,"tag":622,"props":17053,"children":17054},{"style":629},[17055],{"type":259,"value":10675},{"type":254,"tag":622,"props":17057,"children":17058},{"style":1202},[17059],{"type":259,"value":17060},"json nameWithOwner ",{"type":254,"tag":622,"props":17062,"children":17063},{"style":629},[17064],{"type":259,"value":17065},"-",{"type":254,"tag":622,"props":17067,"children":17068},{"style":1202},[17069],{"type":259,"value":17070},"q ",{"type":254,"tag":622,"props":17072,"children":17073},{"style":629},[17074],{"type":259,"value":652},{"type":254,"tag":622,"props":17076,"children":17077},{"style":664},[17078],{"type":259,"value":17079},".nameWithOwner",{"type":254,"tag":622,"props":17081,"children":17082},{"style":629},[17083],{"type":259,"value":652},{"type":254,"tag":622,"props":17085,"children":17086},{"style":629},[17087],{"type":259,"value":1685},{"type":254,"tag":427,"props":17089,"children":17090},{"icon":567},[17091],{"type":254,"tag":255,"props":17092,"children":17093},{},[17094,17096,17102,17104,17110,17112],{"type":259,"value":17095},"Passing the ",{"type":254,"tag":389,"props":17097,"children":17099},{"className":17098},[],[17100],{"type":259,"value":17101},"--json",{"type":259,"value":17103}," flag converts the output format to JSON which, combined with the ",{"type":254,"tag":389,"props":17105,"children":17107},{"className":17106},[],[17108],{"type":259,"value":17109},"--q",{"type":259,"value":17111}," flag can be handy for filtering or formatting a command output. More on that ",{"type":254,"tag":262,"props":17113,"children":17116},{"href":17114,"rel":17115},"https://cli.github.com/manual/gh_help_formatting",[266],[17117],{"type":259,"value":17118},"in the documentation",{"type":254,"tag":455,"props":17120,"children":17122},{"id":17121},"create-the-microsoft-entra-id-resources",[17123],{"type":259,"value":17124},"Create the Microsoft Entra ID resources",{"type":254,"tag":255,"props":17126,"children":17127},{},[17128],{"type":259,"value":17129},"Later, we will need the subscription and the tenant identifiers. Let's retrieve them now and take this opportunity to check that we are logged in on the correct tenant with the correct subscription selected.",{"type":254,"tag":612,"props":17131,"children":17133},{"className":10656,"code":17132,"language":10658,"meta":205,"style":205},"$subscriptionId=$(az account show --query \"id\" -o tsv)\n$tenantId=$(az account show --query \"tenantId\" -o tsv)\n",[17134],{"type":254,"tag":389,"props":17135,"children":17136},{"__ignoreMap":205},[17137,17193],{"type":254,"tag":622,"props":17138,"children":17139},{"class":624,"line":625},[17140,17144,17149,17153,17158,17162,17167,17171,17176,17180,17184,17189],{"type":254,"tag":622,"props":17141,"children":17142},{"style":629},[17143],{"type":259,"value":16922},{"type":254,"tag":622,"props":17145,"children":17146},{"style":1202},[17147],{"type":259,"value":17148},"subscriptionId",{"type":254,"tag":622,"props":17150,"children":17151},{"style":629},[17152],{"type":259,"value":17046},{"type":254,"tag":622,"props":17154,"children":17155},{"style":1202},[17156],{"type":259,"value":17157},"az account show ",{"type":254,"tag":622,"props":17159,"children":17160},{"style":629},[17161],{"type":259,"value":10675},{"type":254,"tag":622,"props":17163,"children":17164},{"style":1202},[17165],{"type":259,"value":17166},"query ",{"type":254,"tag":622,"props":17168,"children":17169},{"style":629},[17170],{"type":259,"value":652},{"type":254,"tag":622,"props":17172,"children":17173},{"style":664},[17174],{"type":259,"value":17175},"id",{"type":254,"tag":622,"props":17177,"children":17178},{"style":629},[17179],{"type":259,"value":652},{"type":254,"tag":622,"props":17181,"children":17182},{"style":629},[17183],{"type":259,"value":10733},{"type":254,"tag":622,"props":17185,"children":17186},{"style":1202},[17187],{"type":259,"value":17188},"o tsv",{"type":254,"tag":622,"props":17190,"children":17191},{"style":629},[17192],{"type":259,"value":1685},{"type":254,"tag":622,"props":17194,"children":17195},{"class":624,"line":635},[17196,17200,17205,17209,17213,17217,17221,17225,17229,17233,17237,17241],{"type":254,"tag":622,"props":17197,"children":17198},{"style":629},[17199],{"type":259,"value":16922},{"type":254,"tag":622,"props":17201,"children":17202},{"style":1202},[17203],{"type":259,"value":17204},"tenantId",{"type":254,"tag":622,"props":17206,"children":17207},{"style":629},[17208],{"type":259,"value":17046},{"type":254,"tag":622,"props":17210,"children":17211},{"style":1202},[17212],{"type":259,"value":17157},{"type":254,"tag":622,"props":17214,"children":17215},{"style":629},[17216],{"type":259,"value":10675},{"type":254,"tag":622,"props":17218,"children":17219},{"style":1202},[17220],{"type":259,"value":17166},{"type":254,"tag":622,"props":17222,"children":17223},{"style":629},[17224],{"type":259,"value":652},{"type":254,"tag":622,"props":17226,"children":17227},{"style":664},[17228],{"type":259,"value":17204},{"type":254,"tag":622,"props":17230,"children":17231},{"style":629},[17232],{"type":259,"value":652},{"type":254,"tag":622,"props":17234,"children":17235},{"style":629},[17236],{"type":259,"value":10733},{"type":254,"tag":622,"props":17238,"children":17239},{"style":1202},[17240],{"type":259,"value":17188},{"type":254,"tag":622,"props":17242,"children":17243},{"style":629},[17244],{"type":259,"value":1685},{"type":254,"tag":427,"props":17246,"children":17247},{"icon":429},[17248],{"type":254,"tag":255,"props":17249,"children":17250},{},[17251,17253,17259,17261,17267,17269,17275],{"type":259,"value":17252},"Similar to the GitHub CLI, the Azure CLI has a ",{"type":254,"tag":389,"props":17254,"children":17256},{"className":17255},[],[17257],{"type":259,"value":17258},"--query",{"type":259,"value":17260}," flag to filter a command output. There are also different output formats. The ",{"type":254,"tag":389,"props":17262,"children":17264},{"className":17263},[],[17265],{"type":259,"value":17266},"tsv",{"type":259,"value":17268}," (tab-separated values) one is useful for capturing a value in an environment variable. If you are not very familiar with the Azure CLI, you can check my article on the topic ",{"type":254,"tag":262,"props":17270,"children":17273},{"href":17271,"rel":17272},"https://www.techwatching.dev/posts/welcome-azure-cli",[266],[17274],{"type":259,"value":1414},{"type":259,"value":594},{"type":254,"tag":255,"props":17277,"children":17278},{},[17279],{"type":259,"value":17280},"To create the app registration and its associated service principal, we can execute the following commands:",{"type":254,"tag":612,"props":17282,"children":17284},{"className":10656,"code":17283,"language":10658,"meta":205,"style":205},"$appId=$(az ad app create --display-name \"GitHub Action OIDC for ${repositoryFullName}\" --query \"appId\" -o tsv)\n$servicePrincipalId=$(az ad sp create --id $appId --query \"id\" -o tsv)\n",[17285],{"type":254,"tag":389,"props":17286,"children":17287},{"__ignoreMap":205},[17288,17381],{"type":254,"tag":622,"props":17289,"children":17290},{"class":624,"line":625},[17291,17295,17300,17304,17309,17313,17318,17322,17327,17331,17336,17341,17345,17349,17353,17357,17361,17365,17369,17373,17377],{"type":254,"tag":622,"props":17292,"children":17293},{"style":629},[17294],{"type":259,"value":16922},{"type":254,"tag":622,"props":17296,"children":17297},{"style":1202},[17298],{"type":259,"value":17299},"appId",{"type":254,"tag":622,"props":17301,"children":17302},{"style":629},[17303],{"type":259,"value":17046},{"type":254,"tag":622,"props":17305,"children":17306},{"style":1202},[17307],{"type":259,"value":17308},"az ad app create ",{"type":254,"tag":622,"props":17310,"children":17311},{"style":629},[17312],{"type":259,"value":10675},{"type":254,"tag":622,"props":17314,"children":17315},{"style":1202},[17316],{"type":259,"value":17317},"display",{"type":254,"tag":622,"props":17319,"children":17320},{"style":629},[17321],{"type":259,"value":17065},{"type":254,"tag":622,"props":17323,"children":17324},{"style":1202},[17325],{"type":259,"value":17326},"name ",{"type":254,"tag":622,"props":17328,"children":17329},{"style":629},[17330],{"type":259,"value":652},{"type":254,"tag":622,"props":17332,"children":17333},{"style":664},[17334],{"type":259,"value":17335},"GitHub Action OIDC for ",{"type":254,"tag":622,"props":17337,"children":17338},{"style":629},[17339],{"type":259,"value":17340},"${",{"type":254,"tag":622,"props":17342,"children":17343},{"style":1202},[17344],{"type":259,"value":17041},{"type":254,"tag":622,"props":17346,"children":17347},{"style":629},[17348],{"type":259,"value":2443},{"type":254,"tag":622,"props":17350,"children":17351},{"style":629},[17352],{"type":259,"value":16988},{"type":254,"tag":622,"props":17354,"children":17355},{"style":1202},[17356],{"type":259,"value":17166},{"type":254,"tag":622,"props":17358,"children":17359},{"style":629},[17360],{"type":259,"value":652},{"type":254,"tag":622,"props":17362,"children":17363},{"style":664},[17364],{"type":259,"value":17299},{"type":254,"tag":622,"props":17366,"children":17367},{"style":629},[17368],{"type":259,"value":652},{"type":254,"tag":622,"props":17370,"children":17371},{"style":629},[17372],{"type":259,"value":10733},{"type":254,"tag":622,"props":17374,"children":17375},{"style":1202},[17376],{"type":259,"value":17188},{"type":254,"tag":622,"props":17378,"children":17379},{"style":629},[17380],{"type":259,"value":1685},{"type":254,"tag":622,"props":17382,"children":17383},{"class":624,"line":635},[17384,17388,17393,17397,17402,17406,17411,17415,17420,17424,17428,17432,17436,17440,17444,17448],{"type":254,"tag":622,"props":17385,"children":17386},{"style":629},[17387],{"type":259,"value":16922},{"type":254,"tag":622,"props":17389,"children":17390},{"style":1202},[17391],{"type":259,"value":17392},"servicePrincipalId",{"type":254,"tag":622,"props":17394,"children":17395},{"style":629},[17396],{"type":259,"value":17046},{"type":254,"tag":622,"props":17398,"children":17399},{"style":1202},[17400],{"type":259,"value":17401},"az ad sp create ",{"type":254,"tag":622,"props":17403,"children":17404},{"style":629},[17405],{"type":259,"value":10675},{"type":254,"tag":622,"props":17407,"children":17408},{"style":1202},[17409],{"type":259,"value":17410},"id ",{"type":254,"tag":622,"props":17412,"children":17413},{"style":629},[17414],{"type":259,"value":16922},{"type":254,"tag":622,"props":17416,"children":17417},{"style":1202},[17418],{"type":259,"value":17419},"appId ",{"type":254,"tag":622,"props":17421,"children":17422},{"style":629},[17423],{"type":259,"value":10675},{"type":254,"tag":622,"props":17425,"children":17426},{"style":1202},[17427],{"type":259,"value":17166},{"type":254,"tag":622,"props":17429,"children":17430},{"style":629},[17431],{"type":259,"value":652},{"type":254,"tag":622,"props":17433,"children":17434},{"style":664},[17435],{"type":259,"value":17175},{"type":254,"tag":622,"props":17437,"children":17438},{"style":629},[17439],{"type":259,"value":652},{"type":254,"tag":622,"props":17441,"children":17442},{"style":629},[17443],{"type":259,"value":10733},{"type":254,"tag":622,"props":17445,"children":17446},{"style":1202},[17447],{"type":259,"value":17188},{"type":254,"tag":622,"props":17449,"children":17450},{"style":629},[17451],{"type":259,"value":1685},{"type":254,"tag":255,"props":17453,"children":17454},{},[17455],{"type":259,"value":17456},"We can now assign the contributor role to the service principal on the subscription.",{"type":254,"tag":612,"props":17458,"children":17460},{"className":10656,"code":17459,"language":10658,"meta":205,"style":205},"az role assignment create --role contributor --subscription $subscriptionId --assignee-object-id  $servicePrincipalId --assignee-principal-type ServicePrincipal --scope /subscriptions/$subscriptionId\n",[17461],{"type":254,"tag":389,"props":17462,"children":17463},{"__ignoreMap":205},[17464],{"type":254,"tag":622,"props":17465,"children":17466},{"class":624,"line":625},[17467,17472,17476,17481,17485,17490,17494,17499,17503,17508,17512,17517,17521,17526,17530,17535,17539,17543,17547,17552,17556,17561,17565,17570,17574,17579,17584],{"type":254,"tag":622,"props":17468,"children":17469},{"style":1202},[17470],{"type":259,"value":17471},"az role assignment create ",{"type":254,"tag":622,"props":17473,"children":17474},{"style":629},[17475],{"type":259,"value":10675},{"type":254,"tag":622,"props":17477,"children":17478},{"style":1202},[17479],{"type":259,"value":17480},"role contributor ",{"type":254,"tag":622,"props":17482,"children":17483},{"style":629},[17484],{"type":259,"value":10675},{"type":254,"tag":622,"props":17486,"children":17487},{"style":1202},[17488],{"type":259,"value":17489},"subscription ",{"type":254,"tag":622,"props":17491,"children":17492},{"style":629},[17493],{"type":259,"value":16922},{"type":254,"tag":622,"props":17495,"children":17496},{"style":1202},[17497],{"type":259,"value":17498},"subscriptionId ",{"type":254,"tag":622,"props":17500,"children":17501},{"style":629},[17502],{"type":259,"value":10675},{"type":254,"tag":622,"props":17504,"children":17505},{"style":1202},[17506],{"type":259,"value":17507},"assignee",{"type":254,"tag":622,"props":17509,"children":17510},{"style":629},[17511],{"type":259,"value":17065},{"type":254,"tag":622,"props":17513,"children":17514},{"style":1202},[17515],{"type":259,"value":17516},"object",{"type":254,"tag":622,"props":17518,"children":17519},{"style":629},[17520],{"type":259,"value":17065},{"type":254,"tag":622,"props":17522,"children":17523},{"style":1202},[17524],{"type":259,"value":17525},"id  ",{"type":254,"tag":622,"props":17527,"children":17528},{"style":629},[17529],{"type":259,"value":16922},{"type":254,"tag":622,"props":17531,"children":17532},{"style":1202},[17533],{"type":259,"value":17534},"servicePrincipalId ",{"type":254,"tag":622,"props":17536,"children":17537},{"style":629},[17538],{"type":259,"value":10675},{"type":254,"tag":622,"props":17540,"children":17541},{"style":1202},[17542],{"type":259,"value":17507},{"type":254,"tag":622,"props":17544,"children":17545},{"style":629},[17546],{"type":259,"value":17065},{"type":254,"tag":622,"props":17548,"children":17549},{"style":1202},[17550],{"type":259,"value":17551},"principal",{"type":254,"tag":622,"props":17553,"children":17554},{"style":629},[17555],{"type":259,"value":17065},{"type":254,"tag":622,"props":17557,"children":17558},{"style":1202},[17559],{"type":259,"value":17560},"type ServicePrincipal ",{"type":254,"tag":622,"props":17562,"children":17563},{"style":629},[17564],{"type":259,"value":10675},{"type":254,"tag":622,"props":17566,"children":17567},{"style":1202},[17568],{"type":259,"value":17569},"scope ",{"type":254,"tag":622,"props":17571,"children":17572},{"style":629},[17573],{"type":259,"value":9768},{"type":254,"tag":622,"props":17575,"children":17576},{"style":1202},[17577],{"type":259,"value":17578},"subscriptions",{"type":254,"tag":622,"props":17580,"children":17581},{"style":629},[17582],{"type":259,"value":17583},"/$",{"type":254,"tag":622,"props":17585,"children":17586},{"style":1202},[17587],{"type":259,"value":17588},"subscriptionId\n",{"type":254,"tag":255,"props":17590,"children":17591},{},[17592],{"type":259,"value":17593},"Creating federated credentials is a bit more complex as one of the arguments needs to be an in-line JSON string.",{"type":254,"tag":612,"props":17595,"children":17597},{"className":10656,"code":17596,"language":10658,"meta":205,"style":205},"$parametersJson = @{\n    name = \"FederatedIdentityForWorkshop\"\n    issuer = \"https://token.actions.githubusercontent.com\"\n    subject = \"repo:${repositoryFullName}:ref:refs/heads/main\"\n    description = \"Deployments for ${repositoryFullName}\"\n    audiences = @(\n        \"api://AzureADTokenExchange\"\n    )\n}\n",[17598],{"type":254,"tag":389,"props":17599,"children":17600},{"__ignoreMap":205},[17601,17626,17651,17676,17718,17752,17772,17788,17796],{"type":254,"tag":622,"props":17602,"children":17603},{"class":624,"line":625},[17604,17608,17613,17617,17622],{"type":254,"tag":622,"props":17605,"children":17606},{"style":629},[17607],{"type":259,"value":16922},{"type":254,"tag":622,"props":17609,"children":17610},{"style":1202},[17611],{"type":259,"value":17612},"parametersJson ",{"type":254,"tag":622,"props":17614,"children":17615},{"style":629},[17616],{"type":259,"value":1654},{"type":254,"tag":622,"props":17618,"children":17619},{"style":741},[17620],{"type":259,"value":17621}," @",{"type":254,"tag":622,"props":17623,"children":17624},{"style":629},[17625],{"type":259,"value":632},{"type":254,"tag":622,"props":17627,"children":17628},{"class":624,"line":635},[17629,17634,17638,17642,17647],{"type":254,"tag":622,"props":17630,"children":17631},{"style":1202},[17632],{"type":259,"value":17633},"    name ",{"type":254,"tag":622,"props":17635,"children":17636},{"style":629},[17637],{"type":259,"value":1654},{"type":254,"tag":622,"props":17639,"children":17640},{"style":629},[17641],{"type":259,"value":661},{"type":254,"tag":622,"props":17643,"children":17644},{"style":664},[17645],{"type":259,"value":17646},"FederatedIdentityForWorkshop",{"type":254,"tag":622,"props":17648,"children":17649},{"style":629},[17650],{"type":259,"value":9909},{"type":254,"tag":622,"props":17652,"children":17653},{"class":624,"line":679},[17654,17659,17663,17667,17672],{"type":254,"tag":622,"props":17655,"children":17656},{"style":1202},[17657],{"type":259,"value":17658},"    issuer ",{"type":254,"tag":622,"props":17660,"children":17661},{"style":629},[17662],{"type":259,"value":1654},{"type":254,"tag":622,"props":17664,"children":17665},{"style":629},[17666],{"type":259,"value":661},{"type":254,"tag":622,"props":17668,"children":17669},{"style":664},[17670],{"type":259,"value":17671},"https://token.actions.githubusercontent.com",{"type":254,"tag":622,"props":17673,"children":17674},{"style":629},[17675],{"type":259,"value":9909},{"type":254,"tag":622,"props":17677,"children":17678},{"class":624,"line":705},[17679,17684,17688,17692,17697,17701,17705,17709,17714],{"type":254,"tag":622,"props":17680,"children":17681},{"style":1202},[17682],{"type":259,"value":17683},"    subject ",{"type":254,"tag":622,"props":17685,"children":17686},{"style":629},[17687],{"type":259,"value":1654},{"type":254,"tag":622,"props":17689,"children":17690},{"style":629},[17691],{"type":259,"value":661},{"type":254,"tag":622,"props":17693,"children":17694},{"style":664},[17695],{"type":259,"value":17696},"repo:",{"type":254,"tag":622,"props":17698,"children":17699},{"style":629},[17700],{"type":259,"value":17340},{"type":254,"tag":622,"props":17702,"children":17703},{"style":1202},[17704],{"type":259,"value":17041},{"type":254,"tag":622,"props":17706,"children":17707},{"style":629},[17708],{"type":259,"value":2421},{"type":254,"tag":622,"props":17710,"children":17711},{"style":664},[17712],{"type":259,"value":17713},":ref:refs/heads/main",{"type":254,"tag":622,"props":17715,"children":17716},{"style":629},[17717],{"type":259,"value":9909},{"type":254,"tag":622,"props":17719,"children":17720},{"class":624,"line":732},[17721,17726,17730,17734,17739,17743,17747],{"type":254,"tag":622,"props":17722,"children":17723},{"style":1202},[17724],{"type":259,"value":17725},"    description ",{"type":254,"tag":622,"props":17727,"children":17728},{"style":629},[17729],{"type":259,"value":1654},{"type":254,"tag":622,"props":17731,"children":17732},{"style":629},[17733],{"type":259,"value":661},{"type":254,"tag":622,"props":17735,"children":17736},{"style":664},[17737],{"type":259,"value":17738},"Deployments for ",{"type":254,"tag":622,"props":17740,"children":17741},{"style":629},[17742],{"type":259,"value":17340},{"type":254,"tag":622,"props":17744,"children":17745},{"style":1202},[17746],{"type":259,"value":17041},{"type":254,"tag":622,"props":17748,"children":17749},{"style":629},[17750],{"type":259,"value":17751},"}\"\n",{"type":254,"tag":622,"props":17753,"children":17754},{"class":624,"line":759},[17755,17760,17764,17768],{"type":254,"tag":622,"props":17756,"children":17757},{"style":1202},[17758],{"type":259,"value":17759},"    audiences ",{"type":254,"tag":622,"props":17761,"children":17762},{"style":629},[17763],{"type":259,"value":1654},{"type":254,"tag":622,"props":17765,"children":17766},{"style":741},[17767],{"type":259,"value":17621},{"type":254,"tag":622,"props":17769,"children":17770},{"style":629},[17771],{"type":259,"value":1452},{"type":254,"tag":622,"props":17773,"children":17774},{"class":624,"line":798},[17775,17779,17784],{"type":254,"tag":622,"props":17776,"children":17777},{"style":629},[17778],{"type":259,"value":765},{"type":254,"tag":622,"props":17780,"children":17781},{"style":664},[17782],{"type":259,"value":17783},"api://AzureADTokenExchange",{"type":254,"tag":622,"props":17785,"children":17786},{"style":629},[17787],{"type":259,"value":9909},{"type":254,"tag":622,"props":17789,"children":17790},{"class":624,"line":836},[17791],{"type":254,"tag":622,"props":17792,"children":17793},{"style":629},[17794],{"type":259,"value":17795},"    )\n",{"type":254,"tag":622,"props":17797,"children":17798},{"class":624,"line":862},[17799],{"type":254,"tag":622,"props":17800,"children":17801},{"style":629},[17802],{"type":259,"value":1111},{"type":254,"tag":427,"props":17804,"children":17805},{"icon":567},[17806],{"type":254,"tag":255,"props":17807,"children":17808},{},[17809,17810,17816,17818,17824],{"type":259,"value":10970},{"type":254,"tag":389,"props":17811,"children":17813},{"className":17812},[],[17814],{"type":259,"value":17815},"subject",{"type":259,"value":17817}," property here specifies that the GitHub Actions workflow from the created repository is only authorized to authenticate to Azure when it runs on the main branch. Of course, there are other possible configurations, such as those involving pull requests or environments. Consult the ",{"type":254,"tag":262,"props":17819,"children":17822},{"href":17820,"rel":17821},"https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#example-subject-claims",[266],[17823],{"type":259,"value":6340},{"type":259,"value":17825}," to learn more about these options.",{"type":254,"tag":255,"props":17827,"children":17828},{},[17829,17831,17838],{"type":259,"value":17830},"To make this JSON string an inline string with escaped quotes that works for the Azure CLI, we have to transform the string using a command I found in this ",{"type":254,"tag":262,"props":17832,"children":17835},{"href":17833,"rel":17834},"https://medium.com/medialesson/use-dynamic-json-strings-with-azure-cli-commands-in-powershell-b191eccc8e9b",[266],[17836],{"type":259,"value":17837},"blog article",{"type":259,"value":594},{"type":254,"tag":612,"props":17840,"children":17842},{"className":10656,"code":17841,"language":10658,"meta":205,"style":205},"$parameters = $($parametersJson | ConvertTo-Json -Depth 100 -Compress).Replace(\"`\"\", \"\\`\"\")\n",[17843],{"type":254,"tag":389,"props":17844,"children":17845},{"__ignoreMap":205},[17846],{"type":254,"tag":622,"props":17847,"children":17848},{"class":624,"line":625},[17849,17853,17858,17862,17867,17871,17876,17881,17885,17890,17895,17899,17904,17908,17913,17917,17921,17926,17930,17934,17938,17943,17947,17951],{"type":254,"tag":622,"props":17850,"children":17851},{"style":629},[17852],{"type":259,"value":16922},{"type":254,"tag":622,"props":17854,"children":17855},{"style":1202},[17856],{"type":259,"value":17857},"parameters ",{"type":254,"tag":622,"props":17859,"children":17860},{"style":629},[17861],{"type":259,"value":1654},{"type":254,"tag":622,"props":17863,"children":17864},{"style":629},[17865],{"type":259,"value":17866}," $($",{"type":254,"tag":622,"props":17868,"children":17869},{"style":1202},[17870],{"type":259,"value":17612},{"type":254,"tag":622,"props":17872,"children":17873},{"style":629},[17874],{"type":259,"value":17875},"|",{"type":254,"tag":622,"props":17877,"children":17878},{"style":1735},[17879],{"type":259,"value":17880}," ConvertTo-Json",{"type":254,"tag":622,"props":17882,"children":17883},{"style":629},[17884],{"type":259,"value":10733},{"type":254,"tag":622,"props":17886,"children":17887},{"style":1202},[17888],{"type":259,"value":17889},"Depth ",{"type":254,"tag":622,"props":17891,"children":17892},{"style":741},[17893],{"type":259,"value":17894},"100",{"type":254,"tag":622,"props":17896,"children":17897},{"style":629},[17898],{"type":259,"value":10733},{"type":254,"tag":622,"props":17900,"children":17901},{"style":1202},[17902],{"type":259,"value":17903},"Compress",{"type":254,"tag":622,"props":17905,"children":17906},{"style":629},[17907],{"type":259,"value":2309},{"type":254,"tag":622,"props":17909,"children":17910},{"style":1202},[17911],{"type":259,"value":17912},".Replace",{"type":254,"tag":622,"props":17914,"children":17915},{"style":629},[17916],{"type":259,"value":1668},{"type":254,"tag":622,"props":17918,"children":17919},{"style":629},[17920],{"type":259,"value":652},{"type":254,"tag":622,"props":17922,"children":17923},{"style":1202},[17924],{"type":259,"value":17925},"`\"",{"type":254,"tag":622,"props":17927,"children":17928},{"style":629},[17929],{"type":259,"value":652},{"type":254,"tag":622,"props":17931,"children":17932},{"style":629},[17933],{"type":259,"value":3149},{"type":254,"tag":622,"props":17935,"children":17936},{"style":629},[17937],{"type":259,"value":661},{"type":254,"tag":622,"props":17939,"children":17940},{"style":664},[17941],{"type":259,"value":17942},"\\",{"type":254,"tag":622,"props":17944,"children":17945},{"style":1202},[17946],{"type":259,"value":17925},{"type":254,"tag":622,"props":17948,"children":17949},{"style":629},[17950],{"type":259,"value":652},{"type":254,"tag":622,"props":17952,"children":17953},{"style":629},[17954],{"type":259,"value":1685},{"type":254,"tag":255,"props":17956,"children":17957},{},[17958],{"type":259,"value":17959},"And finally, we can create the federated credentials.",{"type":254,"tag":612,"props":17961,"children":17963},{"className":10656,"code":17962,"language":10658,"meta":205,"style":205},"az ad app federated-credential create --id $appId --parameters $parameters\n",[17964],{"type":254,"tag":389,"props":17965,"children":17966},{"__ignoreMap":205},[17967],{"type":254,"tag":622,"props":17968,"children":17969},{"class":624,"line":625},[17970,17975,17979,17984,17988,17992,17996,18000,18004,18008,18012],{"type":254,"tag":622,"props":17971,"children":17972},{"style":1202},[17973],{"type":259,"value":17974},"az ad app federated",{"type":254,"tag":622,"props":17976,"children":17977},{"style":629},[17978],{"type":259,"value":17065},{"type":254,"tag":622,"props":17980,"children":17981},{"style":1202},[17982],{"type":259,"value":17983},"credential create ",{"type":254,"tag":622,"props":17985,"children":17986},{"style":629},[17987],{"type":259,"value":10675},{"type":254,"tag":622,"props":17989,"children":17990},{"style":1202},[17991],{"type":259,"value":17410},{"type":254,"tag":622,"props":17993,"children":17994},{"style":629},[17995],{"type":259,"value":16922},{"type":254,"tag":622,"props":17997,"children":17998},{"style":1202},[17999],{"type":259,"value":17419},{"type":254,"tag":622,"props":18001,"children":18002},{"style":629},[18003],{"type":259,"value":10675},{"type":254,"tag":622,"props":18005,"children":18006},{"style":1202},[18007],{"type":259,"value":17857},{"type":254,"tag":622,"props":18009,"children":18010},{"style":629},[18011],{"type":259,"value":16922},{"type":254,"tag":622,"props":18013,"children":18014},{"style":1202},[18015],{"type":259,"value":18016},"parameters\n",{"type":254,"tag":455,"props":18018,"children":18020},{"id":18019},"configure-the-github-actions-and-run-the-workflow",[18021],{"type":259,"value":18022},"Configure the GitHub Actions and run the workflow",{"type":254,"tag":255,"props":18024,"children":18025},{},[18026],{"type":259,"value":18027},"For the OIDC authentication to function properly, we need to set 3 GitHub Actions Secrets (could also be GitHub Actions variables as there are not really secrets):",{"type":254,"tag":476,"props":18029,"children":18030},{},[18031,18036,18041],{"type":254,"tag":336,"props":18032,"children":18033},{},[18034],{"type":259,"value":18035},"The identifier of the Azure tenant",{"type":254,"tag":336,"props":18037,"children":18038},{},[18039],{"type":259,"value":18040},"The identifier of the Azure subscription",{"type":254,"tag":336,"props":18042,"children":18043},{},[18044],{"type":259,"value":18045},"The application identifier of the app registration",{"type":254,"tag":612,"props":18047,"children":18049},{"className":10656,"code":18048,"language":10658,"meta":205,"style":205},"gh secret set AZURE_TENANT_ID --body $tenantId\ngh secret set AZURE_SUBSCRIPTION_ID --body $subscriptionId\ngh secret set AZURE_CLIENT_ID --body $appId\n",[18050],{"type":254,"tag":389,"props":18051,"children":18052},{"__ignoreMap":205},[18053,18079,18103],{"type":254,"tag":622,"props":18054,"children":18055},{"class":624,"line":625},[18056,18061,18065,18070,18074],{"type":254,"tag":622,"props":18057,"children":18058},{"style":1202},[18059],{"type":259,"value":18060},"gh secret set AZURE_TENANT_ID ",{"type":254,"tag":622,"props":18062,"children":18063},{"style":629},[18064],{"type":259,"value":10675},{"type":254,"tag":622,"props":18066,"children":18067},{"style":1202},[18068],{"type":259,"value":18069},"body ",{"type":254,"tag":622,"props":18071,"children":18072},{"style":629},[18073],{"type":259,"value":16922},{"type":254,"tag":622,"props":18075,"children":18076},{"style":1202},[18077],{"type":259,"value":18078},"tenantId\n",{"type":254,"tag":622,"props":18080,"children":18081},{"class":624,"line":635},[18082,18087,18091,18095,18099],{"type":254,"tag":622,"props":18083,"children":18084},{"style":1202},[18085],{"type":259,"value":18086},"gh secret set AZURE_SUBSCRIPTION_ID ",{"type":254,"tag":622,"props":18088,"children":18089},{"style":629},[18090],{"type":259,"value":10675},{"type":254,"tag":622,"props":18092,"children":18093},{"style":1202},[18094],{"type":259,"value":18069},{"type":254,"tag":622,"props":18096,"children":18097},{"style":629},[18098],{"type":259,"value":16922},{"type":254,"tag":622,"props":18100,"children":18101},{"style":1202},[18102],{"type":259,"value":17588},{"type":254,"tag":622,"props":18104,"children":18105},{"class":624,"line":679},[18106,18111,18115,18119,18123],{"type":254,"tag":622,"props":18107,"children":18108},{"style":1202},[18109],{"type":259,"value":18110},"gh secret set AZURE_CLIENT_ID ",{"type":254,"tag":622,"props":18112,"children":18113},{"style":629},[18114],{"type":259,"value":10675},{"type":254,"tag":622,"props":18116,"children":18117},{"style":1202},[18118],{"type":259,"value":18069},{"type":254,"tag":622,"props":18120,"children":18121},{"style":629},[18122],{"type":259,"value":16922},{"type":254,"tag":622,"props":18124,"children":18125},{"style":1202},[18126],{"type":259,"value":18127},"appId\n",{"type":254,"tag":255,"props":18129,"children":18130},{},[18131],{"type":259,"value":18132},"We can directly run the workflow from the GitHub CLI, and watch the run until it is completed.",{"type":254,"tag":612,"props":18134,"children":18136},{"className":10656,"code":18135,"language":10658,"meta":205,"style":205},"gh workflow run main.yml\n$runId=$(gh run list --workflow=main.yml --json databaseId -q \".[0].databaseId\")\ngh run watch $runId\n",[18137],{"type":254,"tag":389,"props":18138,"children":18139},{"__ignoreMap":205},[18140,18148,18221],{"type":254,"tag":622,"props":18141,"children":18142},{"class":624,"line":625},[18143],{"type":254,"tag":622,"props":18144,"children":18145},{"style":1202},[18146],{"type":259,"value":18147},"gh workflow run main.yml\n",{"type":254,"tag":622,"props":18149,"children":18150},{"class":624,"line":635},[18151,18155,18160,18164,18169,18173,18178,18182,18187,18191,18196,18200,18204,18208,18213,18217],{"type":254,"tag":622,"props":18152,"children":18153},{"style":629},[18154],{"type":259,"value":16922},{"type":254,"tag":622,"props":18156,"children":18157},{"style":1202},[18158],{"type":259,"value":18159},"runId",{"type":254,"tag":622,"props":18161,"children":18162},{"style":629},[18163],{"type":259,"value":17046},{"type":254,"tag":622,"props":18165,"children":18166},{"style":1202},[18167],{"type":259,"value":18168},"gh run list ",{"type":254,"tag":622,"props":18170,"children":18171},{"style":629},[18172],{"type":259,"value":10675},{"type":254,"tag":622,"props":18174,"children":18175},{"style":1202},[18176],{"type":259,"value":18177},"workflow",{"type":254,"tag":622,"props":18179,"children":18180},{"style":629},[18181],{"type":259,"value":1654},{"type":254,"tag":622,"props":18183,"children":18184},{"style":1202},[18185],{"type":259,"value":18186},"main.yml ",{"type":254,"tag":622,"props":18188,"children":18189},{"style":629},[18190],{"type":259,"value":10675},{"type":254,"tag":622,"props":18192,"children":18193},{"style":1202},[18194],{"type":259,"value":18195},"json databaseId ",{"type":254,"tag":622,"props":18197,"children":18198},{"style":629},[18199],{"type":259,"value":17065},{"type":254,"tag":622,"props":18201,"children":18202},{"style":1202},[18203],{"type":259,"value":17070},{"type":254,"tag":622,"props":18205,"children":18206},{"style":629},[18207],{"type":259,"value":652},{"type":254,"tag":622,"props":18209,"children":18210},{"style":664},[18211],{"type":259,"value":18212},".[0].databaseId",{"type":254,"tag":622,"props":18214,"children":18215},{"style":629},[18216],{"type":259,"value":652},{"type":254,"tag":622,"props":18218,"children":18219},{"style":629},[18220],{"type":259,"value":1685},{"type":254,"tag":622,"props":18222,"children":18223},{"class":624,"line":679},[18224,18229,18233],{"type":254,"tag":622,"props":18225,"children":18226},{"style":1202},[18227],{"type":259,"value":18228},"gh run watch ",{"type":254,"tag":622,"props":18230,"children":18231},{"style":629},[18232],{"type":259,"value":16922},{"type":254,"tag":622,"props":18234,"children":18235},{"style":1202},[18236],{"type":259,"value":18237},"runId\n",{"type":254,"tag":255,"props":18239,"children":18240},{},[18241],{"type":254,"tag":1161,"props":18242,"children":18246},{"alt":18243,"className":18244,"src":18245},"Screenshot of the GitHub Actions workfow run",[1165,1166],"/posts/images/scripting_azurereadygithub_github_1.webp",[],{"type":254,"tag":282,"props":18248,"children":18250},{"id":18249},"full-script",[18251],{"type":259,"value":18252},"Full script",{"type":254,"tag":612,"props":18254,"children":18256},{"className":10656,"code":18255,"language":10658,"meta":205,"style":205},"# Initialize git repository with current code\n# You should have added the main.yml workflow file in the `.github\\workflows` directory \ngit init\ngit add .\ngit commit -m \"Intialize repository with the GitHub Actions workflow file\"\n\n# Create a new remote private GitHub repository\n$repositoryName = \"MyAzureReadyRepository\"\ngh repo create $repositoryName --private --source=. --push\n\n# Retrieve the repository full name (org/repo)\n$repositoryFullName=$(gh repo view --json nameWithOwner -q \".nameWithOwner\") \n\n# Retrieve the current subscription and current tenant identifiers \n$subscriptionId=$(az account show --query \"id\" -o tsv)\n$tenantId=$(az account show --query \"tenantId\" -o tsv)\n\n# Create an App Registration and its associated service principal\n$appId=$(az ad app create --display-name \"GitHub Action OIDC for ${repositoryFullName}\" --query \"appId\" -o tsv)\n$servicePrincipalId=$(az ad sp create --id $appId --query \"id\" -o tsv)\n\n# Assign the contributor role to the service principal on the subscription\naz role assignment create --role contributor --subscription $subscriptionId --assignee-object-id  $servicePrincipalId --assignee-principal-type ServicePrincipal --scope /subscriptions/$subscriptionId\n\n# Prepare parameters for federated credentials\n$parametersJson = @{\n    name = \"FederatedIdentityForWorkshop\"\n    issuer = \"https://token.actions.githubusercontent.com\"\n    subject = \"repo:${repositoryFullName}:ref:refs/heads/main\"\n    description = \"Deployments for ${repositoryFullName}\"\n    audiences = @(\n        \"api://AzureADTokenExchange\"\n    )\n}\n\n# Change parameters to single line string with escaped quotes to make it work with Azure CLI\n# https://medium.com/medialesson/use-dynamic-json-strings-with-azure-cli-commands-in-powershell-b191eccc8e9b\n$parameters = $($parametersJson | ConvertTo-Json -Depth 100 -Compress).Replace(\"`\"\", \"\\`\"\")\n\n# Create federated credentials\naz ad app federated-credential create --id $appId --parameters $parameters\n\n# Create GitHub secrets needed for the GitHub Actions\ngh secret set AZURE_TENANT_ID --body $tenantId\ngh secret set AZURE_SUBSCRIPTION_ID --body $subscriptionId\ngh secret set AZURE_CLIENT_ID --body $appId\n\n# Run workflow\ngh workflow run main.yml\n$runId=$(gh run list --workflow=main.yml --json databaseId -q \".[0].databaseId\")\ngh run watch $runId\n\n# Open the repostory in the browser\ngh repo view -w\n",[18257],{"type":254,"tag":389,"props":18258,"children":18259},{"__ignoreMap":205},[18260,18268,18276,18284,18292,18321,18328,18336,18363,18406,18413,18421,18476,18483,18491,18542,18593,18600,18608,18695,18762,18769,18777,18888,18895,18903,18926,18949,18972,19011,19042,19061,19076,19083,19090,19097,19105,19113,19212,19219,19227,19274,19281,19289,19312,19335,19358,19365,19373,19380,19447,19462,19469,19477],{"type":254,"tag":622,"props":18261,"children":18262},{"class":624,"line":625},[18263],{"type":254,"tag":622,"props":18264,"children":18265},{"style":3195},[18266],{"type":259,"value":18267},"# Initialize git repository with current code\n",{"type":254,"tag":622,"props":18269,"children":18270},{"class":624,"line":635},[18271],{"type":254,"tag":622,"props":18272,"children":18273},{"style":3195},[18274],{"type":259,"value":18275},"# You should have added the main.yml workflow file in the `.github\\workflows` directory \n",{"type":254,"tag":622,"props":18277,"children":18278},{"class":624,"line":679},[18279],{"type":254,"tag":622,"props":18280,"children":18281},{"style":1202},[18282],{"type":259,"value":18283},"git init\n",{"type":254,"tag":622,"props":18285,"children":18286},{"class":624,"line":705},[18287],{"type":254,"tag":622,"props":18288,"children":18289},{"style":1202},[18290],{"type":259,"value":18291},"git add .\n",{"type":254,"tag":622,"props":18293,"children":18294},{"class":624,"line":732},[18295,18300,18304,18309,18313,18317],{"type":254,"tag":622,"props":18296,"children":18297},{"style":1202},[18298],{"type":259,"value":18299},"git commit ",{"type":254,"tag":622,"props":18301,"children":18302},{"style":629},[18303],{"type":259,"value":17065},{"type":254,"tag":622,"props":18305,"children":18306},{"style":1202},[18307],{"type":259,"value":18308},"m ",{"type":254,"tag":622,"props":18310,"children":18311},{"style":629},[18312],{"type":259,"value":652},{"type":254,"tag":622,"props":18314,"children":18315},{"style":664},[18316],{"type":259,"value":16898},{"type":254,"tag":622,"props":18318,"children":18319},{"style":629},[18320],{"type":259,"value":9909},{"type":254,"tag":622,"props":18322,"children":18323},{"class":624,"line":759},[18324],{"type":254,"tag":622,"props":18325,"children":18326},{"emptyLinePlaceholder":1699},[18327],{"type":259,"value":1702},{"type":254,"tag":622,"props":18329,"children":18330},{"class":624,"line":798},[18331],{"type":254,"tag":622,"props":18332,"children":18333},{"style":3195},[18334],{"type":259,"value":18335},"# Create a new remote private GitHub repository\n",{"type":254,"tag":622,"props":18337,"children":18338},{"class":624,"line":836},[18339,18343,18347,18351,18355,18359],{"type":254,"tag":622,"props":18340,"children":18341},{"style":629},[18342],{"type":259,"value":16922},{"type":254,"tag":622,"props":18344,"children":18345},{"style":1202},[18346],{"type":259,"value":16927},{"type":254,"tag":622,"props":18348,"children":18349},{"style":629},[18350],{"type":259,"value":1654},{"type":254,"tag":622,"props":18352,"children":18353},{"style":629},[18354],{"type":259,"value":661},{"type":254,"tag":622,"props":18356,"children":18357},{"style":664},[18358],{"type":259,"value":16940},{"type":254,"tag":622,"props":18360,"children":18361},{"style":629},[18362],{"type":259,"value":9909},{"type":254,"tag":622,"props":18364,"children":18365},{"class":624,"line":862},[18366,18370,18374,18378,18382,18386,18390,18394,18398,18402],{"type":254,"tag":622,"props":18367,"children":18368},{"style":1202},[18369],{"type":259,"value":16952},{"type":254,"tag":622,"props":18371,"children":18372},{"style":629},[18373],{"type":259,"value":16922},{"type":254,"tag":622,"props":18375,"children":18376},{"style":1202},[18377],{"type":259,"value":16927},{"type":254,"tag":622,"props":18379,"children":18380},{"style":629},[18381],{"type":259,"value":10675},{"type":254,"tag":622,"props":18383,"children":18384},{"style":1202},[18385],{"type":259,"value":16969},{"type":254,"tag":622,"props":18387,"children":18388},{"style":629},[18389],{"type":259,"value":10675},{"type":254,"tag":622,"props":18391,"children":18392},{"style":1202},[18393],{"type":259,"value":16978},{"type":254,"tag":622,"props":18395,"children":18396},{"style":629},[18397],{"type":259,"value":16983},{"type":254,"tag":622,"props":18399,"children":18400},{"style":629},[18401],{"type":259,"value":16988},{"type":254,"tag":622,"props":18403,"children":18404},{"style":1202},[18405],{"type":259,"value":16993},{"type":254,"tag":622,"props":18407,"children":18408},{"class":624,"line":900},[18409],{"type":254,"tag":622,"props":18410,"children":18411},{"emptyLinePlaceholder":1699},[18412],{"type":259,"value":1702},{"type":254,"tag":622,"props":18414,"children":18415},{"class":624,"line":938},[18416],{"type":254,"tag":622,"props":18417,"children":18418},{"style":3195},[18419],{"type":259,"value":18420},"# Retrieve the repository full name (org/repo)\n",{"type":254,"tag":622,"props":18422,"children":18423},{"class":624,"line":976},[18424,18428,18432,18436,18440,18444,18448,18452,18456,18460,18464,18468,18472],{"type":254,"tag":622,"props":18425,"children":18426},{"style":629},[18427],{"type":259,"value":16922},{"type":254,"tag":622,"props":18429,"children":18430},{"style":1202},[18431],{"type":259,"value":17041},{"type":254,"tag":622,"props":18433,"children":18434},{"style":629},[18435],{"type":259,"value":17046},{"type":254,"tag":622,"props":18437,"children":18438},{"style":1202},[18439],{"type":259,"value":17051},{"type":254,"tag":622,"props":18441,"children":18442},{"style":629},[18443],{"type":259,"value":10675},{"type":254,"tag":622,"props":18445,"children":18446},{"style":1202},[18447],{"type":259,"value":17060},{"type":254,"tag":622,"props":18449,"children":18450},{"style":629},[18451],{"type":259,"value":17065},{"type":254,"tag":622,"props":18453,"children":18454},{"style":1202},[18455],{"type":259,"value":17070},{"type":254,"tag":622,"props":18457,"children":18458},{"style":629},[18459],{"type":259,"value":652},{"type":254,"tag":622,"props":18461,"children":18462},{"style":664},[18463],{"type":259,"value":17079},{"type":254,"tag":622,"props":18465,"children":18466},{"style":629},[18467],{"type":259,"value":652},{"type":254,"tag":622,"props":18469,"children":18470},{"style":629},[18471],{"type":259,"value":2309},{"type":254,"tag":622,"props":18473,"children":18474},{"style":1202},[18475],{"type":259,"value":2314},{"type":254,"tag":622,"props":18477,"children":18478},{"class":624,"line":1014},[18479],{"type":254,"tag":622,"props":18480,"children":18481},{"emptyLinePlaceholder":1699},[18482],{"type":259,"value":1702},{"type":254,"tag":622,"props":18484,"children":18485},{"class":624,"line":1052},[18486],{"type":254,"tag":622,"props":18487,"children":18488},{"style":3195},[18489],{"type":259,"value":18490},"# Retrieve the current subscription and current tenant identifiers \n",{"type":254,"tag":622,"props":18492,"children":18493},{"class":624,"line":1078},[18494,18498,18502,18506,18510,18514,18518,18522,18526,18530,18534,18538],{"type":254,"tag":622,"props":18495,"children":18496},{"style":629},[18497],{"type":259,"value":16922},{"type":254,"tag":622,"props":18499,"children":18500},{"style":1202},[18501],{"type":259,"value":17148},{"type":254,"tag":622,"props":18503,"children":18504},{"style":629},[18505],{"type":259,"value":17046},{"type":254,"tag":622,"props":18507,"children":18508},{"style":1202},[18509],{"type":259,"value":17157},{"type":254,"tag":622,"props":18511,"children":18512},{"style":629},[18513],{"type":259,"value":10675},{"type":254,"tag":622,"props":18515,"children":18516},{"style":1202},[18517],{"type":259,"value":17166},{"type":254,"tag":622,"props":18519,"children":18520},{"style":629},[18521],{"type":259,"value":652},{"type":254,"tag":622,"props":18523,"children":18524},{"style":664},[18525],{"type":259,"value":17175},{"type":254,"tag":622,"props":18527,"children":18528},{"style":629},[18529],{"type":259,"value":652},{"type":254,"tag":622,"props":18531,"children":18532},{"style":629},[18533],{"type":259,"value":10733},{"type":254,"tag":622,"props":18535,"children":18536},{"style":1202},[18537],{"type":259,"value":17188},{"type":254,"tag":622,"props":18539,"children":18540},{"style":629},[18541],{"type":259,"value":1685},{"type":254,"tag":622,"props":18543,"children":18544},{"class":624,"line":1087},[18545,18549,18553,18557,18561,18565,18569,18573,18577,18581,18585,18589],{"type":254,"tag":622,"props":18546,"children":18547},{"style":629},[18548],{"type":259,"value":16922},{"type":254,"tag":622,"props":18550,"children":18551},{"style":1202},[18552],{"type":259,"value":17204},{"type":254,"tag":622,"props":18554,"children":18555},{"style":629},[18556],{"type":259,"value":17046},{"type":254,"tag":622,"props":18558,"children":18559},{"style":1202},[18560],{"type":259,"value":17157},{"type":254,"tag":622,"props":18562,"children":18563},{"style":629},[18564],{"type":259,"value":10675},{"type":254,"tag":622,"props":18566,"children":18567},{"style":1202},[18568],{"type":259,"value":17166},{"type":254,"tag":622,"props":18570,"children":18571},{"style":629},[18572],{"type":259,"value":652},{"type":254,"tag":622,"props":18574,"children":18575},{"style":664},[18576],{"type":259,"value":17204},{"type":254,"tag":622,"props":18578,"children":18579},{"style":629},[18580],{"type":259,"value":652},{"type":254,"tag":622,"props":18582,"children":18583},{"style":629},[18584],{"type":259,"value":10733},{"type":254,"tag":622,"props":18586,"children":18587},{"style":1202},[18588],{"type":259,"value":17188},{"type":254,"tag":622,"props":18590,"children":18591},{"style":629},[18592],{"type":259,"value":1685},{"type":254,"tag":622,"props":18594,"children":18595},{"class":624,"line":1096},[18596],{"type":254,"tag":622,"props":18597,"children":18598},{"emptyLinePlaceholder":1699},[18599],{"type":259,"value":1702},{"type":254,"tag":622,"props":18601,"children":18602},{"class":624,"line":1105},[18603],{"type":254,"tag":622,"props":18604,"children":18605},{"style":3195},[18606],{"type":259,"value":18607},"# Create an App Registration and its associated service principal\n",{"type":254,"tag":622,"props":18609,"children":18610},{"class":624,"line":4131},[18611,18615,18619,18623,18627,18631,18635,18639,18643,18647,18651,18655,18659,18663,18667,18671,18675,18679,18683,18687,18691],{"type":254,"tag":622,"props":18612,"children":18613},{"style":629},[18614],{"type":259,"value":16922},{"type":254,"tag":622,"props":18616,"children":18617},{"style":1202},[18618],{"type":259,"value":17299},{"type":254,"tag":622,"props":18620,"children":18621},{"style":629},[18622],{"type":259,"value":17046},{"type":254,"tag":622,"props":18624,"children":18625},{"style":1202},[18626],{"type":259,"value":17308},{"type":254,"tag":622,"props":18628,"children":18629},{"style":629},[18630],{"type":259,"value":10675},{"type":254,"tag":622,"props":18632,"children":18633},{"style":1202},[18634],{"type":259,"value":17317},{"type":254,"tag":622,"props":18636,"children":18637},{"style":629},[18638],{"type":259,"value":17065},{"type":254,"tag":622,"props":18640,"children":18641},{"style":1202},[18642],{"type":259,"value":17326},{"type":254,"tag":622,"props":18644,"children":18645},{"style":629},[18646],{"type":259,"value":652},{"type":254,"tag":622,"props":18648,"children":18649},{"style":664},[18650],{"type":259,"value":17335},{"type":254,"tag":622,"props":18652,"children":18653},{"style":629},[18654],{"type":259,"value":17340},{"type":254,"tag":622,"props":18656,"children":18657},{"style":1202},[18658],{"type":259,"value":17041},{"type":254,"tag":622,"props":18660,"children":18661},{"style":629},[18662],{"type":259,"value":2443},{"type":254,"tag":622,"props":18664,"children":18665},{"style":629},[18666],{"type":259,"value":16988},{"type":254,"tag":622,"props":18668,"children":18669},{"style":1202},[18670],{"type":259,"value":17166},{"type":254,"tag":622,"props":18672,"children":18673},{"style":629},[18674],{"type":259,"value":652},{"type":254,"tag":622,"props":18676,"children":18677},{"style":664},[18678],{"type":259,"value":17299},{"type":254,"tag":622,"props":18680,"children":18681},{"style":629},[18682],{"type":259,"value":652},{"type":254,"tag":622,"props":18684,"children":18685},{"style":629},[18686],{"type":259,"value":10733},{"type":254,"tag":622,"props":18688,"children":18689},{"style":1202},[18690],{"type":259,"value":17188},{"type":254,"tag":622,"props":18692,"children":18693},{"style":629},[18694],{"type":259,"value":1685},{"type":254,"tag":622,"props":18696,"children":18697},{"class":624,"line":4183},[18698,18702,18706,18710,18714,18718,18722,18726,18730,18734,18738,18742,18746,18750,18754,18758],{"type":254,"tag":622,"props":18699,"children":18700},{"style":629},[18701],{"type":259,"value":16922},{"type":254,"tag":622,"props":18703,"children":18704},{"style":1202},[18705],{"type":259,"value":17392},{"type":254,"tag":622,"props":18707,"children":18708},{"style":629},[18709],{"type":259,"value":17046},{"type":254,"tag":622,"props":18711,"children":18712},{"style":1202},[18713],{"type":259,"value":17401},{"type":254,"tag":622,"props":18715,"children":18716},{"style":629},[18717],{"type":259,"value":10675},{"type":254,"tag":622,"props":18719,"children":18720},{"style":1202},[18721],{"type":259,"value":17410},{"type":254,"tag":622,"props":18723,"children":18724},{"style":629},[18725],{"type":259,"value":16922},{"type":254,"tag":622,"props":18727,"children":18728},{"style":1202},[18729],{"type":259,"value":17419},{"type":254,"tag":622,"props":18731,"children":18732},{"style":629},[18733],{"type":259,"value":10675},{"type":254,"tag":622,"props":18735,"children":18736},{"style":1202},[18737],{"type":259,"value":17166},{"type":254,"tag":622,"props":18739,"children":18740},{"style":629},[18741],{"type":259,"value":652},{"type":254,"tag":622,"props":18743,"children":18744},{"style":664},[18745],{"type":259,"value":17175},{"type":254,"tag":622,"props":18747,"children":18748},{"style":629},[18749],{"type":259,"value":652},{"type":254,"tag":622,"props":18751,"children":18752},{"style":629},[18753],{"type":259,"value":10733},{"type":254,"tag":622,"props":18755,"children":18756},{"style":1202},[18757],{"type":259,"value":17188},{"type":254,"tag":622,"props":18759,"children":18760},{"style":629},[18761],{"type":259,"value":1685},{"type":254,"tag":622,"props":18763,"children":18764},{"class":624,"line":4208},[18765],{"type":254,"tag":622,"props":18766,"children":18767},{"emptyLinePlaceholder":1699},[18768],{"type":259,"value":1702},{"type":254,"tag":622,"props":18770,"children":18771},{"class":624,"line":4216},[18772],{"type":254,"tag":622,"props":18773,"children":18774},{"style":3195},[18775],{"type":259,"value":18776},"# Assign the contributor role to the service principal on the subscription\n",{"type":254,"tag":622,"props":18778,"children":18779},{"class":624,"line":4224},[18780,18784,18788,18792,18796,18800,18804,18808,18812,18816,18820,18824,18828,18832,18836,18840,18844,18848,18852,18856,18860,18864,18868,18872,18876,18880,18884],{"type":254,"tag":622,"props":18781,"children":18782},{"style":1202},[18783],{"type":259,"value":17471},{"type":254,"tag":622,"props":18785,"children":18786},{"style":629},[18787],{"type":259,"value":10675},{"type":254,"tag":622,"props":18789,"children":18790},{"style":1202},[18791],{"type":259,"value":17480},{"type":254,"tag":622,"props":18793,"children":18794},{"style":629},[18795],{"type":259,"value":10675},{"type":254,"tag":622,"props":18797,"children":18798},{"style":1202},[18799],{"type":259,"value":17489},{"type":254,"tag":622,"props":18801,"children":18802},{"style":629},[18803],{"type":259,"value":16922},{"type":254,"tag":622,"props":18805,"children":18806},{"style":1202},[18807],{"type":259,"value":17498},{"type":254,"tag":622,"props":18809,"children":18810},{"style":629},[18811],{"type":259,"value":10675},{"type":254,"tag":622,"props":18813,"children":18814},{"style":1202},[18815],{"type":259,"value":17507},{"type":254,"tag":622,"props":18817,"children":18818},{"style":629},[18819],{"type":259,"value":17065},{"type":254,"tag":622,"props":18821,"children":18822},{"style":1202},[18823],{"type":259,"value":17516},{"type":254,"tag":622,"props":18825,"children":18826},{"style":629},[18827],{"type":259,"value":17065},{"type":254,"tag":622,"props":18829,"children":18830},{"style":1202},[18831],{"type":259,"value":17525},{"type":254,"tag":622,"props":18833,"children":18834},{"style":629},[18835],{"type":259,"value":16922},{"type":254,"tag":622,"props":18837,"children":18838},{"style":1202},[18839],{"type":259,"value":17534},{"type":254,"tag":622,"props":18841,"children":18842},{"style":629},[18843],{"type":259,"value":10675},{"type":254,"tag":622,"props":18845,"children":18846},{"style":1202},[18847],{"type":259,"value":17507},{"type":254,"tag":622,"props":18849,"children":18850},{"style":629},[18851],{"type":259,"value":17065},{"type":254,"tag":622,"props":18853,"children":18854},{"style":1202},[18855],{"type":259,"value":17551},{"type":254,"tag":622,"props":18857,"children":18858},{"style":629},[18859],{"type":259,"value":17065},{"type":254,"tag":622,"props":18861,"children":18862},{"style":1202},[18863],{"type":259,"value":17560},{"type":254,"tag":622,"props":18865,"children":18866},{"style":629},[18867],{"type":259,"value":10675},{"type":254,"tag":622,"props":18869,"children":18870},{"style":1202},[18871],{"type":259,"value":17569},{"type":254,"tag":622,"props":18873,"children":18874},{"style":629},[18875],{"type":259,"value":9768},{"type":254,"tag":622,"props":18877,"children":18878},{"style":1202},[18879],{"type":259,"value":17578},{"type":254,"tag":622,"props":18881,"children":18882},{"style":629},[18883],{"type":259,"value":17583},{"type":254,"tag":622,"props":18885,"children":18886},{"style":1202},[18887],{"type":259,"value":17588},{"type":254,"tag":622,"props":18889,"children":18890},{"class":624,"line":4268},[18891],{"type":254,"tag":622,"props":18892,"children":18893},{"emptyLinePlaceholder":1699},[18894],{"type":259,"value":1702},{"type":254,"tag":622,"props":18896,"children":18897},{"class":624,"line":4276},[18898],{"type":254,"tag":622,"props":18899,"children":18900},{"style":3195},[18901],{"type":259,"value":18902},"# Prepare parameters for federated credentials\n",{"type":254,"tag":622,"props":18904,"children":18905},{"class":624,"line":4332},[18906,18910,18914,18918,18922],{"type":254,"tag":622,"props":18907,"children":18908},{"style":629},[18909],{"type":259,"value":16922},{"type":254,"tag":622,"props":18911,"children":18912},{"style":1202},[18913],{"type":259,"value":17612},{"type":254,"tag":622,"props":18915,"children":18916},{"style":629},[18917],{"type":259,"value":1654},{"type":254,"tag":622,"props":18919,"children":18920},{"style":741},[18921],{"type":259,"value":17621},{"type":254,"tag":622,"props":18923,"children":18924},{"style":629},[18925],{"type":259,"value":632},{"type":254,"tag":622,"props":18927,"children":18928},{"class":624,"line":4376},[18929,18933,18937,18941,18945],{"type":254,"tag":622,"props":18930,"children":18931},{"style":1202},[18932],{"type":259,"value":17633},{"type":254,"tag":622,"props":18934,"children":18935},{"style":629},[18936],{"type":259,"value":1654},{"type":254,"tag":622,"props":18938,"children":18939},{"style":629},[18940],{"type":259,"value":661},{"type":254,"tag":622,"props":18942,"children":18943},{"style":664},[18944],{"type":259,"value":17646},{"type":254,"tag":622,"props":18946,"children":18947},{"style":629},[18948],{"type":259,"value":9909},{"type":254,"tag":622,"props":18950,"children":18951},{"class":624,"line":4384},[18952,18956,18960,18964,18968],{"type":254,"tag":622,"props":18953,"children":18954},{"style":1202},[18955],{"type":259,"value":17658},{"type":254,"tag":622,"props":18957,"children":18958},{"style":629},[18959],{"type":259,"value":1654},{"type":254,"tag":622,"props":18961,"children":18962},{"style":629},[18963],{"type":259,"value":661},{"type":254,"tag":622,"props":18965,"children":18966},{"style":664},[18967],{"type":259,"value":17671},{"type":254,"tag":622,"props":18969,"children":18970},{"style":629},[18971],{"type":259,"value":9909},{"type":254,"tag":622,"props":18973,"children":18974},{"class":624,"line":4416},[18975,18979,18983,18987,18991,18995,18999,19003,19007],{"type":254,"tag":622,"props":18976,"children":18977},{"style":1202},[18978],{"type":259,"value":17683},{"type":254,"tag":622,"props":18980,"children":18981},{"style":629},[18982],{"type":259,"value":1654},{"type":254,"tag":622,"props":18984,"children":18985},{"style":629},[18986],{"type":259,"value":661},{"type":254,"tag":622,"props":18988,"children":18989},{"style":664},[18990],{"type":259,"value":17696},{"type":254,"tag":622,"props":18992,"children":18993},{"style":629},[18994],{"type":259,"value":17340},{"type":254,"tag":622,"props":18996,"children":18997},{"style":1202},[18998],{"type":259,"value":17041},{"type":254,"tag":622,"props":19000,"children":19001},{"style":629},[19002],{"type":259,"value":2421},{"type":254,"tag":622,"props":19004,"children":19005},{"style":664},[19006],{"type":259,"value":17713},{"type":254,"tag":622,"props":19008,"children":19009},{"style":629},[19010],{"type":259,"value":9909},{"type":254,"tag":622,"props":19012,"children":19013},{"class":624,"line":4456},[19014,19018,19022,19026,19030,19034,19038],{"type":254,"tag":622,"props":19015,"children":19016},{"style":1202},[19017],{"type":259,"value":17725},{"type":254,"tag":622,"props":19019,"children":19020},{"style":629},[19021],{"type":259,"value":1654},{"type":254,"tag":622,"props":19023,"children":19024},{"style":629},[19025],{"type":259,"value":661},{"type":254,"tag":622,"props":19027,"children":19028},{"style":664},[19029],{"type":259,"value":17738},{"type":254,"tag":622,"props":19031,"children":19032},{"style":629},[19033],{"type":259,"value":17340},{"type":254,"tag":622,"props":19035,"children":19036},{"style":1202},[19037],{"type":259,"value":17041},{"type":254,"tag":622,"props":19039,"children":19040},{"style":629},[19041],{"type":259,"value":17751},{"type":254,"tag":622,"props":19043,"children":19044},{"class":624,"line":4488},[19045,19049,19053,19057],{"type":254,"tag":622,"props":19046,"children":19047},{"style":1202},[19048],{"type":259,"value":17759},{"type":254,"tag":622,"props":19050,"children":19051},{"style":629},[19052],{"type":259,"value":1654},{"type":254,"tag":622,"props":19054,"children":19055},{"style":741},[19056],{"type":259,"value":17621},{"type":254,"tag":622,"props":19058,"children":19059},{"style":629},[19060],{"type":259,"value":1452},{"type":254,"tag":622,"props":19062,"children":19063},{"class":624,"line":4496},[19064,19068,19072],{"type":254,"tag":622,"props":19065,"children":19066},{"style":629},[19067],{"type":259,"value":765},{"type":254,"tag":622,"props":19069,"children":19070},{"style":664},[19071],{"type":259,"value":17783},{"type":254,"tag":622,"props":19073,"children":19074},{"style":629},[19075],{"type":259,"value":9909},{"type":254,"tag":622,"props":19077,"children":19078},{"class":624,"line":4564},[19079],{"type":254,"tag":622,"props":19080,"children":19081},{"style":629},[19082],{"type":259,"value":17795},{"type":254,"tag":622,"props":19084,"children":19085},{"class":624,"line":4572},[19086],{"type":254,"tag":622,"props":19087,"children":19088},{"style":629},[19089],{"type":259,"value":1111},{"type":254,"tag":622,"props":19091,"children":19092},{"class":624,"line":4644},[19093],{"type":254,"tag":622,"props":19094,"children":19095},{"emptyLinePlaceholder":1699},[19096],{"type":259,"value":1702},{"type":254,"tag":622,"props":19098,"children":19099},{"class":624,"line":4652},[19100],{"type":254,"tag":622,"props":19101,"children":19102},{"style":3195},[19103],{"type":259,"value":19104},"# Change parameters to single line string with escaped quotes to make it work with Azure CLI\n",{"type":254,"tag":622,"props":19106,"children":19107},{"class":624,"line":4660},[19108],{"type":254,"tag":622,"props":19109,"children":19110},{"style":3195},[19111],{"type":259,"value":19112},"# https://medium.com/medialesson/use-dynamic-json-strings-with-azure-cli-commands-in-powershell-b191eccc8e9b\n",{"type":254,"tag":622,"props":19114,"children":19115},{"class":624,"line":4692},[19116,19120,19124,19128,19132,19136,19140,19144,19148,19152,19156,19160,19164,19168,19172,19176,19180,19184,19188,19192,19196,19200,19204,19208],{"type":254,"tag":622,"props":19117,"children":19118},{"style":629},[19119],{"type":259,"value":16922},{"type":254,"tag":622,"props":19121,"children":19122},{"style":1202},[19123],{"type":259,"value":17857},{"type":254,"tag":622,"props":19125,"children":19126},{"style":629},[19127],{"type":259,"value":1654},{"type":254,"tag":622,"props":19129,"children":19130},{"style":629},[19131],{"type":259,"value":17866},{"type":254,"tag":622,"props":19133,"children":19134},{"style":1202},[19135],{"type":259,"value":17612},{"type":254,"tag":622,"props":19137,"children":19138},{"style":629},[19139],{"type":259,"value":17875},{"type":254,"tag":622,"props":19141,"children":19142},{"style":1735},[19143],{"type":259,"value":17880},{"type":254,"tag":622,"props":19145,"children":19146},{"style":629},[19147],{"type":259,"value":10733},{"type":254,"tag":622,"props":19149,"children":19150},{"style":1202},[19151],{"type":259,"value":17889},{"type":254,"tag":622,"props":19153,"children":19154},{"style":741},[19155],{"type":259,"value":17894},{"type":254,"tag":622,"props":19157,"children":19158},{"style":629},[19159],{"type":259,"value":10733},{"type":254,"tag":622,"props":19161,"children":19162},{"style":1202},[19163],{"type":259,"value":17903},{"type":254,"tag":622,"props":19165,"children":19166},{"style":629},[19167],{"type":259,"value":2309},{"type":254,"tag":622,"props":19169,"children":19170},{"style":1202},[19171],{"type":259,"value":17912},{"type":254,"tag":622,"props":19173,"children":19174},{"style":629},[19175],{"type":259,"value":1668},{"type":254,"tag":622,"props":19177,"children":19178},{"style":629},[19179],{"type":259,"value":652},{"type":254,"tag":622,"props":19181,"children":19182},{"style":1202},[19183],{"type":259,"value":17925},{"type":254,"tag":622,"props":19185,"children":19186},{"style":629},[19187],{"type":259,"value":652},{"type":254,"tag":622,"props":19189,"children":19190},{"style":629},[19191],{"type":259,"value":3149},{"type":254,"tag":622,"props":19193,"children":19194},{"style":629},[19195],{"type":259,"value":661},{"type":254,"tag":622,"props":19197,"children":19198},{"style":664},[19199],{"type":259,"value":17942},{"type":254,"tag":622,"props":19201,"children":19202},{"style":1202},[19203],{"type":259,"value":17925},{"type":254,"tag":622,"props":19205,"children":19206},{"style":629},[19207],{"type":259,"value":652},{"type":254,"tag":622,"props":19209,"children":19210},{"style":629},[19211],{"type":259,"value":1685},{"type":254,"tag":622,"props":19213,"children":19214},{"class":624,"line":4700},[19215],{"type":254,"tag":622,"props":19216,"children":19217},{"emptyLinePlaceholder":1699},[19218],{"type":259,"value":1702},{"type":254,"tag":622,"props":19220,"children":19221},{"class":624,"line":4756},[19222],{"type":254,"tag":622,"props":19223,"children":19224},{"style":3195},[19225],{"type":259,"value":19226},"# Create federated credentials\n",{"type":254,"tag":622,"props":19228,"children":19229},{"class":624,"line":4808},[19230,19234,19238,19242,19246,19250,19254,19258,19262,19266,19270],{"type":254,"tag":622,"props":19231,"children":19232},{"style":1202},[19233],{"type":259,"value":17974},{"type":254,"tag":622,"props":19235,"children":19236},{"style":629},[19237],{"type":259,"value":17065},{"type":254,"tag":622,"props":19239,"children":19240},{"style":1202},[19241],{"type":259,"value":17983},{"type":254,"tag":622,"props":19243,"children":19244},{"style":629},[19245],{"type":259,"value":10675},{"type":254,"tag":622,"props":19247,"children":19248},{"style":1202},[19249],{"type":259,"value":17410},{"type":254,"tag":622,"props":19251,"children":19252},{"style":629},[19253],{"type":259,"value":16922},{"type":254,"tag":622,"props":19255,"children":19256},{"style":1202},[19257],{"type":259,"value":17419},{"type":254,"tag":622,"props":19259,"children":19260},{"style":629},[19261],{"type":259,"value":10675},{"type":254,"tag":622,"props":19263,"children":19264},{"style":1202},[19265],{"type":259,"value":17857},{"type":254,"tag":622,"props":19267,"children":19268},{"style":629},[19269],{"type":259,"value":16922},{"type":254,"tag":622,"props":19271,"children":19272},{"style":1202},[19273],{"type":259,"value":18016},{"type":254,"tag":622,"props":19275,"children":19276},{"class":624,"line":4864},[19277],{"type":254,"tag":622,"props":19278,"children":19279},{"emptyLinePlaceholder":1699},[19280],{"type":259,"value":1702},{"type":254,"tag":622,"props":19282,"children":19283},{"class":624,"line":4892},[19284],{"type":254,"tag":622,"props":19285,"children":19286},{"style":3195},[19287],{"type":259,"value":19288},"# Create GitHub secrets needed for the GitHub Actions\n",{"type":254,"tag":622,"props":19290,"children":19291},{"class":624,"line":4900},[19292,19296,19300,19304,19308],{"type":254,"tag":622,"props":19293,"children":19294},{"style":1202},[19295],{"type":259,"value":18060},{"type":254,"tag":622,"props":19297,"children":19298},{"style":629},[19299],{"type":259,"value":10675},{"type":254,"tag":622,"props":19301,"children":19302},{"style":1202},[19303],{"type":259,"value":18069},{"type":254,"tag":622,"props":19305,"children":19306},{"style":629},[19307],{"type":259,"value":16922},{"type":254,"tag":622,"props":19309,"children":19310},{"style":1202},[19311],{"type":259,"value":18078},{"type":254,"tag":622,"props":19313,"children":19314},{"class":624,"line":4944},[19315,19319,19323,19327,19331],{"type":254,"tag":622,"props":19316,"children":19317},{"style":1202},[19318],{"type":259,"value":18086},{"type":254,"tag":622,"props":19320,"children":19321},{"style":629},[19322],{"type":259,"value":10675},{"type":254,"tag":622,"props":19324,"children":19325},{"style":1202},[19326],{"type":259,"value":18069},{"type":254,"tag":622,"props":19328,"children":19329},{"style":629},[19330],{"type":259,"value":16922},{"type":254,"tag":622,"props":19332,"children":19333},{"style":1202},[19334],{"type":259,"value":17588},{"type":254,"tag":622,"props":19336,"children":19337},{"class":624,"line":4952},[19338,19342,19346,19350,19354],{"type":254,"tag":622,"props":19339,"children":19340},{"style":1202},[19341],{"type":259,"value":18110},{"type":254,"tag":622,"props":19343,"children":19344},{"style":629},[19345],{"type":259,"value":10675},{"type":254,"tag":622,"props":19347,"children":19348},{"style":1202},[19349],{"type":259,"value":18069},{"type":254,"tag":622,"props":19351,"children":19352},{"style":629},[19353],{"type":259,"value":16922},{"type":254,"tag":622,"props":19355,"children":19356},{"style":1202},[19357],{"type":259,"value":18127},{"type":254,"tag":622,"props":19359,"children":19360},{"class":624,"line":4960},[19361],{"type":254,"tag":622,"props":19362,"children":19363},{"emptyLinePlaceholder":1699},[19364],{"type":259,"value":1702},{"type":254,"tag":622,"props":19366,"children":19367},{"class":624,"line":4968},[19368],{"type":254,"tag":622,"props":19369,"children":19370},{"style":3195},[19371],{"type":259,"value":19372},"# Run workflow\n",{"type":254,"tag":622,"props":19374,"children":19375},{"class":624,"line":4996},[19376],{"type":254,"tag":622,"props":19377,"children":19378},{"style":1202},[19379],{"type":259,"value":18147},{"type":254,"tag":622,"props":19381,"children":19382},{"class":624,"line":5004},[19383,19387,19391,19395,19399,19403,19407,19411,19415,19419,19423,19427,19431,19435,19439,19443],{"type":254,"tag":622,"props":19384,"children":19385},{"style":629},[19386],{"type":259,"value":16922},{"type":254,"tag":622,"props":19388,"children":19389},{"style":1202},[19390],{"type":259,"value":18159},{"type":254,"tag":622,"props":19392,"children":19393},{"style":629},[19394],{"type":259,"value":17046},{"type":254,"tag":622,"props":19396,"children":19397},{"style":1202},[19398],{"type":259,"value":18168},{"type":254,"tag":622,"props":19400,"children":19401},{"style":629},[19402],{"type":259,"value":10675},{"type":254,"tag":622,"props":19404,"children":19405},{"style":1202},[19406],{"type":259,"value":18177},{"type":254,"tag":622,"props":19408,"children":19409},{"style":629},[19410],{"type":259,"value":1654},{"type":254,"tag":622,"props":19412,"children":19413},{"style":1202},[19414],{"type":259,"value":18186},{"type":254,"tag":622,"props":19416,"children":19417},{"style":629},[19418],{"type":259,"value":10675},{"type":254,"tag":622,"props":19420,"children":19421},{"style":1202},[19422],{"type":259,"value":18195},{"type":254,"tag":622,"props":19424,"children":19425},{"style":629},[19426],{"type":259,"value":17065},{"type":254,"tag":622,"props":19428,"children":19429},{"style":1202},[19430],{"type":259,"value":17070},{"type":254,"tag":622,"props":19432,"children":19433},{"style":629},[19434],{"type":259,"value":652},{"type":254,"tag":622,"props":19436,"children":19437},{"style":664},[19438],{"type":259,"value":18212},{"type":254,"tag":622,"props":19440,"children":19441},{"style":629},[19442],{"type":259,"value":652},{"type":254,"tag":622,"props":19444,"children":19445},{"style":629},[19446],{"type":259,"value":1685},{"type":254,"tag":622,"props":19448,"children":19449},{"class":624,"line":5060},[19450,19454,19458],{"type":254,"tag":622,"props":19451,"children":19452},{"style":1202},[19453],{"type":259,"value":18228},{"type":254,"tag":622,"props":19455,"children":19456},{"style":629},[19457],{"type":259,"value":16922},{"type":254,"tag":622,"props":19459,"children":19460},{"style":1202},[19461],{"type":259,"value":18237},{"type":254,"tag":622,"props":19463,"children":19464},{"class":624,"line":5116},[19465],{"type":254,"tag":622,"props":19466,"children":19467},{"emptyLinePlaceholder":1699},[19468],{"type":259,"value":1702},{"type":254,"tag":622,"props":19470,"children":19471},{"class":624,"line":5208},[19472],{"type":254,"tag":622,"props":19473,"children":19474},{"style":3195},[19475],{"type":259,"value":19476},"# Open the repostory in the browser\n",{"type":254,"tag":622,"props":19478,"children":19479},{"class":624,"line":5240},[19480,19484,19488],{"type":254,"tag":622,"props":19481,"children":19482},{"style":1202},[19483],{"type":259,"value":17051},{"type":254,"tag":622,"props":19485,"children":19486},{"style":629},[19487],{"type":259,"value":17065},{"type":254,"tag":622,"props":19489,"children":19490},{"style":1202},[19491],{"type":259,"value":19492},"w\n",{"type":254,"tag":282,"props":19494,"children":19495},{"id":6186},[19496],{"type":259,"value":6189},{"type":254,"tag":255,"props":19498,"children":19499},{},[19500,19502,19508],{"type":259,"value":19501},"I am very glad to have scripted the creation and configuration of a GitHub repository ready to deploy to Azure. Even if I had already done the ",{"type":254,"tag":262,"props":19503,"children":19505},{"href":16753,"rel":19504},[266],[19506],{"type":259,"value":19507},"same using Pulumi",{"type":259,"value":19509},", having a small script can sometimes be more convenient than having a full IaC program. In my case, I needed to automate that for a workshop, so it was easier to give participants a script to execute.",{"type":254,"tag":255,"props":19511,"children":19512},{},[19513],{"type":259,"value":19514},"However, I must admit that developing this script proved to be much more challenging than provisioning the same resources using Pulumi. I didn't expect it to take so much time: browsing the CLI documentation, finding the correct syntax, and understanding the cause of failures. In contrast, using the GitHub and Azure Pulumi providers in my TypeScript code turned out to be a much more enjoyable experience.",{"type":254,"tag":255,"props":19516,"children":19517},{},[19518,19520,19525,19526,19532],{"type":259,"value":19519},"Nevertheless, I was pleased to be introduced to the GitHub CLI, which I hadn't explored extensively until now. While I found it very useful, a few things bothered me. Not all commands can be used with the ",{"type":254,"tag":389,"props":19521,"children":19523},{"className":19522},[],[19524],{"type":259,"value":17101},{"type":259,"value":10347},{"type":254,"tag":389,"props":19527,"children":19529},{"className":19528},[],[19530],{"type":259,"value":19531},"-q",{"type":259,"value":19533}," parameters, which is not very convenient for scripting. Commands that create things (repo, workflow runs) don't return the identifier of the thing they create. I wish GitHub CLI would be more similar to Azure CLI in these matters. I have no doubt these will be improved over time.",{"type":254,"tag":255,"props":19535,"children":19536},{},[19537],{"type":259,"value":19538},"As for Azure CLI, I am still a big fan, although a bit disappointed to have struggled with the inline JSON string.",{"type":254,"tag":255,"props":19540,"children":19541},{},[19542],{"type":259,"value":19543},"Keep learning, keep sharing.",{"type":254,"tag":1260,"props":19545,"children":19546},{},[19547],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":19549},[19550,19551,19557,19558],{"id":16365,"depth":635,"text":16368},{"id":16762,"depth":635,"text":16765,"children":19552},[19553,19554,19555,19556],{"id":16768,"depth":679,"text":16771},{"id":16812,"depth":679,"text":16815},{"id":17121,"depth":679,"text":17124},{"id":18019,"depth":679,"text":18022},{"id":18249,"depth":635,"text":18252},{"id":6186,"depth":635,"text":6189},"content:1.posts:55.scripting-azure-ready-github-repository.md","1.posts/55.scripting-azure-ready-github-repository.md",{"_path":169,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":168,"description":19562,"lead":19563,"date":19564,"image":19565,"badge":19567,"tags":19568,"body":19571,"_type":1275,"_id":23742,"_source":231,"_file":23743,"_extension":1278},"If you are deploying your application to Azure from Azure Pipelines, you might want to leverage the ability to do so without using secrets thanks to Workload identity federation. In this article, I will demonstrate how to automate the configuration of your Azure DevOps project, with everything pre-configured to securely deploy applications to Azure.","Azure DevOps Workload identity federation (OIDC) with Pulumi","2023-09-21T00:00:00.000Z",{"src":19566},"/images/azuredevopsoidc.webp",{"label":16349},[6260,16352,16354,16355,10225,19569,19570,1288],"IaC","security",{"type":251,"children":19572,"toc":23729},[19573,19577,19583,19597,19610,19615,19635,19644,19662,19673,19678,19690,19695,19700,19708,19717,19722,19727,19737,19743,19749,19754,19816,19828,19887,19892,19931,19936,19981,19987,19992,20030,20044,20080,20104,20109,20461,20466,20480,20590,20595,20860,20898,20904,20918,21206,21219,21373,21378,21400,21615,21621,21626,21644,21649,22124,22139,22144,22460,22473,22479,22484,22497,22730,22735,22989,22994,23265,23270,23455,23460,23619,23632,23663,23672,23677,23681,23694,23699,23704,23709,23725],{"type":254,"tag":255,"props":19574,"children":19575},{},[19576],{"type":259,"value":19562},{"type":254,"tag":282,"props":19578,"children":19580},{"id":19579},"why-should-you-use-workload-identity-federation-for-your-deployment-pipelines",[19581],{"type":259,"value":19582},"Why should you use Workload Identity Federation for your deployment pipelines?",{"type":254,"tag":255,"props":19584,"children":19585},{},[19586,19588,19595],{"type":259,"value":19587},"I already wrote about the ",{"type":254,"tag":262,"props":19589,"children":19592},{"href":19590,"rel":19591},"https://www.techwatching.dev/posts/azure-ready-github-repository#the-problem-with-secret-credentials",[266],[19593],{"type":259,"value":19594},"problem of secret credentials",{"type":259,"value":19596},", but let me remind you 2 reasons why I think you should always avoid using secrets in your deployment pipelines:",{"type":254,"tag":332,"props":19598,"children":19599},{},[19600,19605],{"type":254,"tag":336,"props":19601,"children":19602},{},[19603],{"type":259,"value":19604},"It's more secure if you don't need a secret to authenticate to Azure",{"type":254,"tag":336,"props":19606,"children":19607},{},[19608],{"type":259,"value":19609},"It's more practical if you don't need to handle secret rotation when secrets expire",{"type":254,"tag":255,"props":19611,"children":19612},{},[19613],{"type":259,"value":19614},"This is true whatever the CI/CD platform you are using.",{"type":254,"tag":255,"props":19616,"children":19617},{},[19618,19625,19627,19633],{"type":254,"tag":262,"props":19619,"children":19622},{"href":19620,"rel":19621},"https://learn.microsoft.com/en-us/azure/active-directory/workload-identities/workload-identity-federation",[266],[19623],{"type":259,"value":19624},"Workload identity federation",{"type":259,"value":19626}," leverages OpenID Connect to solve these problems and avoid using secrets in your pipelines to authenticate to Azure. I previously published ",{"type":254,"tag":262,"props":19628,"children":19630},{"href":16753,"rel":19629},[266],[19631],{"type":259,"value":19632},"an article about using Azure OpenID Connect with Pulumi in GitHub Actions",{"type":259,"value":19634},", but that also works with Azure Pipelines.",{"type":254,"tag":255,"props":19636,"children":19637},{},[19638],{"type":254,"tag":1161,"props":19639,"children":19643},{"alt":19640,"className":19641,"src":19642},"Workload Identity Federation for Azure DevOps",[1165,1166],"/posts/images/azuredevopsoidc_schema_1.webp",[],{"type":254,"tag":427,"props":19645,"children":19647},{"icon":19646},"i-heroicons-information-circle",[19648],{"type":254,"tag":255,"props":19649,"children":19650},{},[19651,19653,19660],{"type":259,"value":19652},"Microsoft has announced the ",{"type":254,"tag":262,"props":19654,"children":19657},{"href":19655,"rel":19656},"https://devblogs.microsoft.com/devops/public-preview-of-workload-identity-federation-for-azure-pipelines/",[266],[19658],{"type":259,"value":19659},"public preview of Workload identity federation for Azure Pipelines",{"type":259,"value":19661}," on the 11th September 2023.",{"type":254,"tag":282,"props":19663,"children":19665},{"id":19664},"how-can-you-use-workload-identity-federation-to-deploy-to-azure-from-azure-pipelines",[19666,19668],{"type":259,"value":19667},"How can you use ",{"type":254,"tag":9718,"props":19669,"children":19670},{},[19671],{"type":259,"value":19672},"Workload Identity Federation to deploy to Azure from Azure Pipelines?",{"type":254,"tag":255,"props":19674,"children":19675},{},[19676],{"type":259,"value":19677},"Azure Pipelines tasks use service connections to authenticate with external services. Specifically, for Azure, it is necessary to create an Azure Resource Manager service connection.",{"type":254,"tag":255,"props":19679,"children":19680},{},[19681,19683,19689],{"type":259,"value":19682},"You can create an Azure Resource Manager service connection that uses workload identity federation by configuring it in your Azure DevOps organization portal (check the documentation ",{"type":254,"tag":262,"props":19684,"children":19687},{"href":19685,"rel":19686},"https://learn.microsoft.com/en-us/azure/devops/pipelines/library/connect-to-azure?view=azure-devops#create-an-azure-resource-manager-service-connection-using-workload-identity-federation",[266],[19688],{"type":259,"value":1414},{"type":259,"value":2280},{"type":254,"tag":255,"props":19691,"children":19692},{},[19693],{"type":259,"value":19694},"Or ... you can automate that using Infrastructure as Code 😉.",{"type":254,"tag":255,"props":19696,"children":19697},{},[19698],{"type":259,"value":19699},"Yet, who wants to manually configure things from a wizard when everything can be automated in versioned code? So let's go the IaC way.",{"type":254,"tag":427,"props":19701,"children":19702},{"icon":429},[19703],{"type":254,"tag":255,"props":19704,"children":19705},{},[19706],{"type":259,"value":19707},"All kidding aside, I genuinely believe that there are many advantages to provisioning your Azure DevOps projects and their associated resources (Repos, Service Connections, policies, pipelines, ...) using Infrastructure as Code. It takes time to properly configure Azure DevOps projects, and if they are often organized similarly, it's more efficient to automate their configuration rather than doing it manually.",{"type":254,"tag":255,"props":19709,"children":19710},{},[19711],{"type":254,"tag":1161,"props":19712,"children":19716},{"alt":19713,"className":19714,"src":19715},"Diagram to deploy from Azure Pipelines to Azure",[1165,1166],"/posts/images/azuredevopsoidc_schema_2.webp",[],{"type":254,"tag":255,"props":19718,"children":19719},{},[19720],{"type":259,"value":19721},"I will use Pulumi and its Azure DevOps provider to provision the necessary resources. The infrastructure as code will be written in C# but you could easily convert the C# code to any language that Pulumi supports (like TypeScript, I am a big fan of using TypeScript to write infrastructure code 🔥).",{"type":254,"tag":255,"props":19723,"children":19724},{},[19725],{"type":259,"value":19726},"Here is the complete solution to implement:",{"type":254,"tag":255,"props":19728,"children":19729},{},[19730],{"type":254,"tag":1161,"props":19731,"children":19736},{"alt":19732,"className":19733,"src":19734,"width":19735},"Schema of the complete solution",[1165,1166],"/posts/images/azuredevopsoidc_schema_3.webp",1000,[],{"type":254,"tag":282,"props":19738,"children":19740},{"id":19739},"automate-the-configuration-of-workload-identity-federation-in-azure-devops",[19741],{"type":259,"value":19742},"Automate the configuration of Workload identity federation in Azure DevOps",{"type":254,"tag":455,"props":19744,"children":19746},{"id":19745},"create-the-pulumi-net-project",[19747],{"type":259,"value":19748},"Create the Pulumi .NET project",{"type":254,"tag":255,"props":19750,"children":19751},{},[19752],{"type":259,"value":19753},"Let's start by scaffolding a new Pulumi project using .NET:",{"type":254,"tag":612,"props":19755,"children":19757},{"className":16836,"code":19756,"language":16838,"meta":205,"style":205},"pulumi new csharp -n AzureDevOpsWorkloadIdentity -s dev -d \"A program to set up an Azure-Ready Azure DevOps repository\"\n",[19758],{"type":254,"tag":389,"props":19759,"children":19760},{"__ignoreMap":205},[19761],{"type":254,"tag":622,"props":19762,"children":19763},{"class":624,"line":625},[19764,19769,19773,19778,19783,19788,19793,19798,19803,19807,19812],{"type":254,"tag":622,"props":19765,"children":19766},{"style":714},[19767],{"type":259,"value":19768},"pulumi",{"type":254,"tag":622,"props":19770,"children":19771},{"style":664},[19772],{"type":259,"value":1624},{"type":254,"tag":622,"props":19774,"children":19775},{"style":664},[19776],{"type":259,"value":19777}," csharp",{"type":254,"tag":622,"props":19779,"children":19780},{"style":664},[19781],{"type":259,"value":19782}," -n",{"type":254,"tag":622,"props":19784,"children":19785},{"style":664},[19786],{"type":259,"value":19787}," AzureDevOpsWorkloadIdentity",{"type":254,"tag":622,"props":19789,"children":19790},{"style":664},[19791],{"type":259,"value":19792}," -s",{"type":254,"tag":622,"props":19794,"children":19795},{"style":664},[19796],{"type":259,"value":19797}," dev",{"type":254,"tag":622,"props":19799,"children":19800},{"style":664},[19801],{"type":259,"value":19802}," -d",{"type":254,"tag":622,"props":19804,"children":19805},{"style":629},[19806],{"type":259,"value":661},{"type":254,"tag":622,"props":19808,"children":19809},{"style":664},[19810],{"type":259,"value":19811},"A program to set up an Azure-Ready Azure DevOps repository",{"type":254,"tag":622,"props":19813,"children":19814},{"style":629},[19815],{"type":259,"value":9909},{"type":254,"tag":255,"props":19817,"children":19818},{},[19819,19821,19826],{"type":259,"value":19820},"This command creates a new pulumi project and stack from the ",{"type":254,"tag":389,"props":19822,"children":19824},{"className":19823},[],[19825],{"type":259,"value":1425},{"type":259,"value":19827}," template:",{"type":254,"tag":332,"props":19829,"children":19830},{},[19831,19852,19869],{"type":254,"tag":336,"props":19832,"children":19833},{},[19834,19836,19842,19844,19850],{"type":259,"value":19835},"The name of the project \"",{"type":254,"tag":19837,"props":19838,"children":19839},"em",{},[19840],{"type":259,"value":19841},"AzureDevOpsWorkloadIdentity",{"type":259,"value":19843},"\" is specified using the ",{"type":254,"tag":389,"props":19845,"children":19847},{"className":19846},[],[19848],{"type":259,"value":19849},"-n",{"type":259,"value":19851}," option",{"type":254,"tag":336,"props":19853,"children":19854},{},[19855,19857,19861,19862,19868],{"type":259,"value":19856},"The description of the project \"",{"type":254,"tag":19837,"props":19858,"children":19859},{},[19860],{"type":259,"value":19811},{"type":259,"value":19843},{"type":254,"tag":389,"props":19863,"children":19865},{"className":19864},[],[19866],{"type":259,"value":19867},"-d",{"type":259,"value":19851},{"type":254,"tag":336,"props":19870,"children":19871},{},[19872,19874,19879,19880,19886],{"type":259,"value":19873},"The stack of the project \"",{"type":254,"tag":19837,"props":19875,"children":19876},{},[19877],{"type":259,"value":19878},"dev",{"type":259,"value":19843},{"type":254,"tag":389,"props":19881,"children":19883},{"className":19882},[],[19884],{"type":259,"value":19885},"-s",{"type":259,"value":19851},{"type":254,"tag":255,"props":19888,"children":19889},{},[19890],{"type":259,"value":19891},"This project will need 3 different providers:",{"type":254,"tag":332,"props":19893,"children":19894},{},[19895,19907,19920],{"type":254,"tag":336,"props":19896,"children":19897},{},[19898,19900],{"type":259,"value":19899},"the ",{"type":254,"tag":262,"props":19901,"children":19904},{"href":19902,"rel":19903},"https://www.pulumi.com/registry/packages/azure-native/",[266],[19905],{"type":259,"value":19906},"Azure Native provider",{"type":254,"tag":336,"props":19908,"children":19909},{},[19910,19911,19918],{"type":259,"value":19899},{"type":254,"tag":262,"props":19912,"children":19915},{"href":19913,"rel":19914},"https://www.pulumi.com/registry/packages/azuread/",[266],[19916],{"type":259,"value":19917},"Azure Active Directory provider",{"type":259,"value":19919}," (provider for Microsoft Entra ID)",{"type":254,"tag":336,"props":19921,"children":19922},{},[19923,19924],{"type":259,"value":19899},{"type":254,"tag":262,"props":19925,"children":19928},{"href":19926,"rel":19927},"https://www.pulumi.com/registry/packages/azuredevops/",[266],[19929],{"type":259,"value":19930},"Azure DevOps provider",{"type":254,"tag":255,"props":19932,"children":19933},{},[19934],{"type":259,"value":19935},"So we can add the following Nuget packages to our project:",{"type":254,"tag":332,"props":19937,"children":19938},{},[19939,19953,19967],{"type":254,"tag":336,"props":19940,"children":19941},{},[19942],{"type":254,"tag":262,"props":19943,"children":19946},{"href":19944,"rel":19945},"https://www.nuget.org/packages/Pulumi.AzureNative",[266],[19947],{"type":254,"tag":389,"props":19948,"children":19950},{"className":19949},[],[19951],{"type":259,"value":19952},"Pulumi.AzureNative",{"type":254,"tag":336,"props":19954,"children":19955},{},[19956],{"type":254,"tag":262,"props":19957,"children":19960},{"href":19958,"rel":19959},"https://www.nuget.org/packages/Pulumi.AzureAD",[266],[19961],{"type":254,"tag":389,"props":19962,"children":19964},{"className":19963},[],[19965],{"type":259,"value":19966},"Pulumi.AzureAD",{"type":254,"tag":336,"props":19968,"children":19969},{},[19970],{"type":254,"tag":262,"props":19971,"children":19974},{"href":19972,"rel":19973},"https://www.nuget.org/packages/Pulumi.AzureDevOps",[266],[19975],{"type":254,"tag":389,"props":19976,"children":19978},{"className":19977},[],[19979],{"type":259,"value":19980},"Pulumi.AzureDevOps",{"type":254,"tag":455,"props":19982,"children":19984},{"id":19983},"create-the-azure-devops-project",[19985],{"type":259,"value":19986},"Create the Azure DevOps project",{"type":254,"tag":255,"props":19988,"children":19989},{},[19990],{"type":259,"value":19991},"First, we must select the Azure DevOps organization where we wish to create a project and set its URL in our Pulumi configuration.",{"type":254,"tag":612,"props":19993,"children":19995},{"className":16836,"code":19994,"language":16838,"meta":205,"style":205},"pulumi config set azuredevops:orgServiceUrl XXXXXXXXXXXXXX --secret\n",[19996],{"type":254,"tag":389,"props":19997,"children":19998},{"__ignoreMap":205},[19999],{"type":254,"tag":622,"props":20000,"children":20001},{"class":624,"line":625},[20002,20006,20011,20015,20020,20025],{"type":254,"tag":622,"props":20003,"children":20004},{"style":714},[20005],{"type":259,"value":19768},{"type":254,"tag":622,"props":20007,"children":20008},{"style":664},[20009],{"type":259,"value":20010}," config",{"type":254,"tag":622,"props":20012,"children":20013},{"style":664},[20014],{"type":259,"value":2005},{"type":254,"tag":622,"props":20016,"children":20017},{"style":664},[20018],{"type":259,"value":20019}," azuredevops:orgServiceUrl",{"type":254,"tag":622,"props":20021,"children":20022},{"style":664},[20023],{"type":259,"value":20024}," XXXXXXXXXXXXXX",{"type":254,"tag":622,"props":20026,"children":20027},{"style":664},[20028],{"type":259,"value":20029}," --secret\n",{"type":254,"tag":255,"props":20031,"children":20032},{},[20033,20035,20042],{"type":259,"value":20034},"Second, we need to supply the necessary Azure DevOps credentials. For that, we can ",{"type":254,"tag":262,"props":20036,"children":20039},{"href":20037,"rel":20038},"https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=Windows#create-a-pat",[266],[20040],{"type":259,"value":20041},"create a personal access token",{"type":259,"value":20043}," and add it to our Pulumi configuration.",{"type":254,"tag":612,"props":20045,"children":20047},{"className":16836,"code":20046,"language":16838,"meta":205,"style":205},"pulumi config set azuredevops:personalAccessToken YYYYYYYYYYYYYY --secret\n",[20048],{"type":254,"tag":389,"props":20049,"children":20050},{"__ignoreMap":205},[20051],{"type":254,"tag":622,"props":20052,"children":20053},{"class":624,"line":625},[20054,20058,20062,20066,20071,20076],{"type":254,"tag":622,"props":20055,"children":20056},{"style":714},[20057],{"type":259,"value":19768},{"type":254,"tag":622,"props":20059,"children":20060},{"style":664},[20061],{"type":259,"value":20010},{"type":254,"tag":622,"props":20063,"children":20064},{"style":664},[20065],{"type":259,"value":2005},{"type":254,"tag":622,"props":20067,"children":20068},{"style":664},[20069],{"type":259,"value":20070}," azuredevops:personalAccessToken",{"type":254,"tag":622,"props":20072,"children":20073},{"style":664},[20074],{"type":259,"value":20075}," YYYYYYYYYYYYYY",{"type":254,"tag":622,"props":20077,"children":20078},{"style":664},[20079],{"type":259,"value":20029},{"type":254,"tag":427,"props":20081,"children":20083},{"icon":20082},"i-fluent-emoji-flat-locked-with-key",[20084],{"type":254,"tag":255,"props":20085,"children":20086},{},[20087,20089,20095,20097,20102],{"type":259,"value":20088},"I followed the documentation but to be honest, I don't think it's necessary to include the ",{"type":254,"tag":389,"props":20090,"children":20092},{"className":20091},[],[20093],{"type":259,"value":20094},"--secret",{"type":259,"value":20096}," option for the organization URL as it's not really a sensitive value that needs to be encrypted. However, ",{"type":254,"tag":9718,"props":20098,"children":20099},{},[20100],{"type":259,"value":20101},"it's mandatory to include it for the access token",{"type":259,"value":20103}," so that we can safely commit the configuration files without creating security risks.",{"type":254,"tag":255,"props":20105,"children":20106},{},[20107],{"type":259,"value":20108},"Third, we can create the DevOps project:",{"type":254,"tag":612,"props":20110,"children":20112},{"className":1423,"code":20111,"language":1425,"meta":205,"style":205},"var project = new Project(\"AzureReadyADOProject\", new()\n{\n    Description = \"New project with everything correctly configured to provision Azure resources or deploy applications to Azure\",\n    Features = new()\n    {\n        [\"boards\"] = \"disabled\",\n        [\"repositories\"] = \"enabled\",\n        [\"pipelines\"] = \"enabled\",\n        [\"testplans\"] = \"disabled\",\n        [\"artifacts\"] = \"disabled\"\n    },\n});\n",[20113],{"type":254,"tag":389,"props":20114,"children":20115},{"__ignoreMap":205},[20116,20167,20174,20203,20219,20226,20273,20318,20362,20406,20446,20454],{"type":254,"tag":622,"props":20117,"children":20118},{"class":624,"line":625},[20119,20123,20128,20132,20136,20141,20145,20149,20154,20158,20162],{"type":254,"tag":622,"props":20120,"children":20121},{"style":714},[20122],{"type":259,"value":1710},{"type":254,"tag":622,"props":20124,"children":20125},{"style":714},[20126],{"type":259,"value":20127}," project",{"type":254,"tag":622,"props":20129,"children":20130},{"style":629},[20131],{"type":259,"value":1619},{"type":254,"tag":622,"props":20133,"children":20134},{"style":629},[20135],{"type":259,"value":1624},{"type":254,"tag":622,"props":20137,"children":20138},{"style":714},[20139],{"type":259,"value":20140}," Project",{"type":254,"tag":622,"props":20142,"children":20143},{"style":629},[20144],{"type":259,"value":1668},{"type":254,"tag":622,"props":20146,"children":20147},{"style":629},[20148],{"type":259,"value":652},{"type":254,"tag":622,"props":20150,"children":20151},{"style":664},[20152],{"type":259,"value":20153},"AzureReadyADOProject",{"type":254,"tag":622,"props":20155,"children":20156},{"style":629},[20157],{"type":259,"value":652},{"type":254,"tag":622,"props":20159,"children":20160},{"style":629},[20161],{"type":259,"value":3149},{"type":254,"tag":622,"props":20163,"children":20164},{"style":629},[20165],{"type":259,"value":20166}," new()\n",{"type":254,"tag":622,"props":20168,"children":20169},{"class":624,"line":635},[20170],{"type":254,"tag":622,"props":20171,"children":20172},{"style":629},[20173],{"type":259,"value":632},{"type":254,"tag":622,"props":20175,"children":20176},{"class":624,"line":679},[20177,20182,20186,20190,20195,20199],{"type":254,"tag":622,"props":20178,"children":20179},{"style":1202},[20180],{"type":259,"value":20181},"    Description ",{"type":254,"tag":622,"props":20183,"children":20184},{"style":629},[20185],{"type":259,"value":1654},{"type":254,"tag":622,"props":20187,"children":20188},{"style":629},[20189],{"type":259,"value":661},{"type":254,"tag":622,"props":20191,"children":20192},{"style":664},[20193],{"type":259,"value":20194},"New project with everything correctly configured to provision Azure resources or deploy applications to Azure",{"type":254,"tag":622,"props":20196,"children":20197},{"style":629},[20198],{"type":259,"value":652},{"type":254,"tag":622,"props":20200,"children":20201},{"style":629},[20202],{"type":259,"value":676},{"type":254,"tag":622,"props":20204,"children":20205},{"class":624,"line":705},[20206,20211,20215],{"type":254,"tag":622,"props":20207,"children":20208},{"style":1202},[20209],{"type":259,"value":20210},"    Features ",{"type":254,"tag":622,"props":20212,"children":20213},{"style":629},[20214],{"type":259,"value":1654},{"type":254,"tag":622,"props":20216,"children":20217},{"style":629},[20218],{"type":259,"value":20166},{"type":254,"tag":622,"props":20220,"children":20221},{"class":624,"line":732},[20222],{"type":254,"tag":622,"props":20223,"children":20224},{"style":629},[20225],{"type":259,"value":3574},{"type":254,"tag":622,"props":20227,"children":20228},{"class":624,"line":759},[20229,20234,20238,20243,20247,20252,20256,20260,20265,20269],{"type":254,"tag":622,"props":20230,"children":20231},{"style":629},[20232],{"type":259,"value":20233},"        [",{"type":254,"tag":622,"props":20235,"children":20236},{"style":629},[20237],{"type":259,"value":652},{"type":254,"tag":622,"props":20239,"children":20240},{"style":664},[20241],{"type":259,"value":20242},"boards",{"type":254,"tag":622,"props":20244,"children":20245},{"style":629},[20246],{"type":259,"value":652},{"type":254,"tag":622,"props":20248,"children":20249},{"style":629},[20250],{"type":259,"value":20251},"]",{"type":254,"tag":622,"props":20253,"children":20254},{"style":629},[20255],{"type":259,"value":1619},{"type":254,"tag":622,"props":20257,"children":20258},{"style":629},[20259],{"type":259,"value":661},{"type":254,"tag":622,"props":20261,"children":20262},{"style":664},[20263],{"type":259,"value":20264},"disabled",{"type":254,"tag":622,"props":20266,"children":20267},{"style":629},[20268],{"type":259,"value":652},{"type":254,"tag":622,"props":20270,"children":20271},{"style":629},[20272],{"type":259,"value":676},{"type":254,"tag":622,"props":20274,"children":20275},{"class":624,"line":798},[20276,20280,20284,20289,20293,20297,20301,20305,20310,20314],{"type":254,"tag":622,"props":20277,"children":20278},{"style":629},[20279],{"type":259,"value":20233},{"type":254,"tag":622,"props":20281,"children":20282},{"style":629},[20283],{"type":259,"value":652},{"type":254,"tag":622,"props":20285,"children":20286},{"style":664},[20287],{"type":259,"value":20288},"repositories",{"type":254,"tag":622,"props":20290,"children":20291},{"style":629},[20292],{"type":259,"value":652},{"type":254,"tag":622,"props":20294,"children":20295},{"style":629},[20296],{"type":259,"value":20251},{"type":254,"tag":622,"props":20298,"children":20299},{"style":629},[20300],{"type":259,"value":1619},{"type":254,"tag":622,"props":20302,"children":20303},{"style":629},[20304],{"type":259,"value":661},{"type":254,"tag":622,"props":20306,"children":20307},{"style":664},[20308],{"type":259,"value":20309},"enabled",{"type":254,"tag":622,"props":20311,"children":20312},{"style":629},[20313],{"type":259,"value":652},{"type":254,"tag":622,"props":20315,"children":20316},{"style":629},[20317],{"type":259,"value":676},{"type":254,"tag":622,"props":20319,"children":20320},{"class":624,"line":836},[20321,20325,20329,20334,20338,20342,20346,20350,20354,20358],{"type":254,"tag":622,"props":20322,"children":20323},{"style":629},[20324],{"type":259,"value":20233},{"type":254,"tag":622,"props":20326,"children":20327},{"style":629},[20328],{"type":259,"value":652},{"type":254,"tag":622,"props":20330,"children":20331},{"style":664},[20332],{"type":259,"value":20333},"pipelines",{"type":254,"tag":622,"props":20335,"children":20336},{"style":629},[20337],{"type":259,"value":652},{"type":254,"tag":622,"props":20339,"children":20340},{"style":629},[20341],{"type":259,"value":20251},{"type":254,"tag":622,"props":20343,"children":20344},{"style":629},[20345],{"type":259,"value":1619},{"type":254,"tag":622,"props":20347,"children":20348},{"style":629},[20349],{"type":259,"value":661},{"type":254,"tag":622,"props":20351,"children":20352},{"style":664},[20353],{"type":259,"value":20309},{"type":254,"tag":622,"props":20355,"children":20356},{"style":629},[20357],{"type":259,"value":652},{"type":254,"tag":622,"props":20359,"children":20360},{"style":629},[20361],{"type":259,"value":676},{"type":254,"tag":622,"props":20363,"children":20364},{"class":624,"line":862},[20365,20369,20373,20378,20382,20386,20390,20394,20398,20402],{"type":254,"tag":622,"props":20366,"children":20367},{"style":629},[20368],{"type":259,"value":20233},{"type":254,"tag":622,"props":20370,"children":20371},{"style":629},[20372],{"type":259,"value":652},{"type":254,"tag":622,"props":20374,"children":20375},{"style":664},[20376],{"type":259,"value":20377},"testplans",{"type":254,"tag":622,"props":20379,"children":20380},{"style":629},[20381],{"type":259,"value":652},{"type":254,"tag":622,"props":20383,"children":20384},{"style":629},[20385],{"type":259,"value":20251},{"type":254,"tag":622,"props":20387,"children":20388},{"style":629},[20389],{"type":259,"value":1619},{"type":254,"tag":622,"props":20391,"children":20392},{"style":629},[20393],{"type":259,"value":661},{"type":254,"tag":622,"props":20395,"children":20396},{"style":664},[20397],{"type":259,"value":20264},{"type":254,"tag":622,"props":20399,"children":20400},{"style":629},[20401],{"type":259,"value":652},{"type":254,"tag":622,"props":20403,"children":20404},{"style":629},[20405],{"type":259,"value":676},{"type":254,"tag":622,"props":20407,"children":20408},{"class":624,"line":900},[20409,20413,20417,20422,20426,20430,20434,20438,20442],{"type":254,"tag":622,"props":20410,"children":20411},{"style":629},[20412],{"type":259,"value":20233},{"type":254,"tag":622,"props":20414,"children":20415},{"style":629},[20416],{"type":259,"value":652},{"type":254,"tag":622,"props":20418,"children":20419},{"style":664},[20420],{"type":259,"value":20421},"artifacts",{"type":254,"tag":622,"props":20423,"children":20424},{"style":629},[20425],{"type":259,"value":652},{"type":254,"tag":622,"props":20427,"children":20428},{"style":629},[20429],{"type":259,"value":20251},{"type":254,"tag":622,"props":20431,"children":20432},{"style":629},[20433],{"type":259,"value":1619},{"type":254,"tag":622,"props":20435,"children":20436},{"style":629},[20437],{"type":259,"value":661},{"type":254,"tag":622,"props":20439,"children":20440},{"style":664},[20441],{"type":259,"value":20264},{"type":254,"tag":622,"props":20443,"children":20444},{"style":629},[20445],{"type":259,"value":9909},{"type":254,"tag":622,"props":20447,"children":20448},{"class":624,"line":938},[20449],{"type":254,"tag":622,"props":20450,"children":20451},{"style":629},[20452],{"type":259,"value":20453},"    },\n",{"type":254,"tag":622,"props":20455,"children":20456},{"class":624,"line":976},[20457],{"type":254,"tag":622,"props":20458,"children":20459},{"style":629},[20460],{"type":259,"value":6661},{"type":254,"tag":255,"props":20462,"children":20463},{},[20464],{"type":259,"value":20465},"I intentionally disabled Azure Boards, Azure Test Plans, and Azure Artifacts as we only need Azure Repos and Azure Pipelines for this demo but you can enable what you need for your projects.",{"type":254,"tag":255,"props":20467,"children":20468},{},[20469,20471,20478],{"type":259,"value":20470},"By default, when we create an Azure DevOps project, a ",{"type":254,"tag":262,"props":20472,"children":20475},{"href":20473,"rel":20474},"https://www.pulumi.com/registry/packages/azuredevops/api-docs/git/",[266],[20476],{"type":259,"value":20477},"Git repository",{"type":259,"value":20479}," is created for us with the same name as the project. This repository can be retrieved using the following code:",{"type":254,"tag":612,"props":20481,"children":20483},{"className":1423,"code":20482,"language":1425,"meta":205,"style":205},"var repository = GetGitRepository.Invoke(new()\n{\n    ProjectId = project.Id,\n    Name = project.Name\n});\n",[20484],{"type":254,"tag":389,"props":20485,"children":20486},{"__ignoreMap":205},[20487,20522,20529,20558,20583],{"type":254,"tag":622,"props":20488,"children":20489},{"class":624,"line":625},[20490,20494,20499,20503,20508,20512,20517],{"type":254,"tag":622,"props":20491,"children":20492},{"style":714},[20493],{"type":259,"value":1710},{"type":254,"tag":622,"props":20495,"children":20496},{"style":714},[20497],{"type":259,"value":20498}," repository",{"type":254,"tag":622,"props":20500,"children":20501},{"style":629},[20502],{"type":259,"value":1619},{"type":254,"tag":622,"props":20504,"children":20505},{"style":1202},[20506],{"type":259,"value":20507}," GetGitRepository",{"type":254,"tag":622,"props":20509,"children":20510},{"style":629},[20511],{"type":259,"value":594},{"type":254,"tag":622,"props":20513,"children":20514},{"style":1735},[20515],{"type":259,"value":20516},"Invoke",{"type":254,"tag":622,"props":20518,"children":20519},{"style":629},[20520],{"type":259,"value":20521},"(new()\n",{"type":254,"tag":622,"props":20523,"children":20524},{"class":624,"line":635},[20525],{"type":254,"tag":622,"props":20526,"children":20527},{"style":629},[20528],{"type":259,"value":632},{"type":254,"tag":622,"props":20530,"children":20531},{"class":624,"line":679},[20532,20537,20541,20545,20549,20554],{"type":254,"tag":622,"props":20533,"children":20534},{"style":1202},[20535],{"type":259,"value":20536},"    ProjectId ",{"type":254,"tag":622,"props":20538,"children":20539},{"style":629},[20540],{"type":259,"value":1654},{"type":254,"tag":622,"props":20542,"children":20543},{"style":1202},[20544],{"type":259,"value":20127},{"type":254,"tag":622,"props":20546,"children":20547},{"style":629},[20548],{"type":259,"value":594},{"type":254,"tag":622,"props":20550,"children":20551},{"style":1202},[20552],{"type":259,"value":20553},"Id",{"type":254,"tag":622,"props":20555,"children":20556},{"style":629},[20557],{"type":259,"value":676},{"type":254,"tag":622,"props":20559,"children":20560},{"class":624,"line":705},[20561,20566,20570,20574,20578],{"type":254,"tag":622,"props":20562,"children":20563},{"style":1202},[20564],{"type":259,"value":20565},"    Name ",{"type":254,"tag":622,"props":20567,"children":20568},{"style":629},[20569],{"type":259,"value":1654},{"type":254,"tag":622,"props":20571,"children":20572},{"style":1202},[20573],{"type":259,"value":20127},{"type":254,"tag":622,"props":20575,"children":20576},{"style":629},[20577],{"type":259,"value":594},{"type":254,"tag":622,"props":20579,"children":20580},{"style":1202},[20581],{"type":259,"value":20582},"Name\n",{"type":254,"tag":622,"props":20584,"children":20585},{"class":624,"line":732},[20586],{"type":254,"tag":622,"props":20587,"children":20588},{"style":629},[20589],{"type":259,"value":6661},{"type":254,"tag":255,"props":20591,"children":20592},{},[20593],{"type":259,"value":20594},"We can also choose to create a new Git repository like this:",{"type":254,"tag":612,"props":20596,"children":20598},{"className":1423,"code":20597,"language":1425,"meta":205,"style":205},"var repository = new Git(\"AzureReadyADORepository\", new()\n{\n    ProjectId = project.Id,\n    Initialization = new GitInitializationArgs()\n    {\n        InitType = \"Clean\",\n        SourceType = \"Git\",\n        SourceUrl = \"https://repo.com\",\n        ServiceConnectionId = \"\"\n    },\n    DefaultBranch = \"refs/heads/main\"\n});\n",[20599],{"type":254,"tag":389,"props":20600,"children":20601},{"__ignoreMap":205},[20602,20651,20658,20685,20710,20717,20746,20775,20804,20821,20828,20853],{"type":254,"tag":622,"props":20603,"children":20604},{"class":624,"line":625},[20605,20609,20613,20617,20621,20626,20630,20634,20639,20643,20647],{"type":254,"tag":622,"props":20606,"children":20607},{"style":714},[20608],{"type":259,"value":1710},{"type":254,"tag":622,"props":20610,"children":20611},{"style":714},[20612],{"type":259,"value":20498},{"type":254,"tag":622,"props":20614,"children":20615},{"style":629},[20616],{"type":259,"value":1619},{"type":254,"tag":622,"props":20618,"children":20619},{"style":629},[20620],{"type":259,"value":1624},{"type":254,"tag":622,"props":20622,"children":20623},{"style":714},[20624],{"type":259,"value":20625}," Git",{"type":254,"tag":622,"props":20627,"children":20628},{"style":629},[20629],{"type":259,"value":1668},{"type":254,"tag":622,"props":20631,"children":20632},{"style":629},[20633],{"type":259,"value":652},{"type":254,"tag":622,"props":20635,"children":20636},{"style":664},[20637],{"type":259,"value":20638},"AzureReadyADORepository",{"type":254,"tag":622,"props":20640,"children":20641},{"style":629},[20642],{"type":259,"value":652},{"type":254,"tag":622,"props":20644,"children":20645},{"style":629},[20646],{"type":259,"value":3149},{"type":254,"tag":622,"props":20648,"children":20649},{"style":629},[20650],{"type":259,"value":20166},{"type":254,"tag":622,"props":20652,"children":20653},{"class":624,"line":635},[20654],{"type":254,"tag":622,"props":20655,"children":20656},{"style":629},[20657],{"type":259,"value":632},{"type":254,"tag":622,"props":20659,"children":20660},{"class":624,"line":679},[20661,20665,20669,20673,20677,20681],{"type":254,"tag":622,"props":20662,"children":20663},{"style":1202},[20664],{"type":259,"value":20536},{"type":254,"tag":622,"props":20666,"children":20667},{"style":629},[20668],{"type":259,"value":1654},{"type":254,"tag":622,"props":20670,"children":20671},{"style":1202},[20672],{"type":259,"value":20127},{"type":254,"tag":622,"props":20674,"children":20675},{"style":629},[20676],{"type":259,"value":594},{"type":254,"tag":622,"props":20678,"children":20679},{"style":1202},[20680],{"type":259,"value":20553},{"type":254,"tag":622,"props":20682,"children":20683},{"style":629},[20684],{"type":259,"value":676},{"type":254,"tag":622,"props":20686,"children":20687},{"class":624,"line":705},[20688,20693,20697,20701,20706],{"type":254,"tag":622,"props":20689,"children":20690},{"style":1202},[20691],{"type":259,"value":20692},"    Initialization ",{"type":254,"tag":622,"props":20694,"children":20695},{"style":629},[20696],{"type":259,"value":1654},{"type":254,"tag":622,"props":20698,"children":20699},{"style":629},[20700],{"type":259,"value":1624},{"type":254,"tag":622,"props":20702,"children":20703},{"style":714},[20704],{"type":259,"value":20705}," GitInitializationArgs",{"type":254,"tag":622,"props":20707,"children":20708},{"style":629},[20709],{"type":259,"value":1634},{"type":254,"tag":622,"props":20711,"children":20712},{"class":624,"line":732},[20713],{"type":254,"tag":622,"props":20714,"children":20715},{"style":629},[20716],{"type":259,"value":3574},{"type":254,"tag":622,"props":20718,"children":20719},{"class":624,"line":759},[20720,20725,20729,20733,20738,20742],{"type":254,"tag":622,"props":20721,"children":20722},{"style":1202},[20723],{"type":259,"value":20724},"        InitType ",{"type":254,"tag":622,"props":20726,"children":20727},{"style":629},[20728],{"type":259,"value":1654},{"type":254,"tag":622,"props":20730,"children":20731},{"style":629},[20732],{"type":259,"value":661},{"type":254,"tag":622,"props":20734,"children":20735},{"style":664},[20736],{"type":259,"value":20737},"Clean",{"type":254,"tag":622,"props":20739,"children":20740},{"style":629},[20741],{"type":259,"value":652},{"type":254,"tag":622,"props":20743,"children":20744},{"style":629},[20745],{"type":259,"value":676},{"type":254,"tag":622,"props":20747,"children":20748},{"class":624,"line":798},[20749,20754,20758,20762,20767,20771],{"type":254,"tag":622,"props":20750,"children":20751},{"style":1202},[20752],{"type":259,"value":20753},"        SourceType ",{"type":254,"tag":622,"props":20755,"children":20756},{"style":629},[20757],{"type":259,"value":1654},{"type":254,"tag":622,"props":20759,"children":20760},{"style":629},[20761],{"type":259,"value":661},{"type":254,"tag":622,"props":20763,"children":20764},{"style":664},[20765],{"type":259,"value":20766},"Git",{"type":254,"tag":622,"props":20768,"children":20769},{"style":629},[20770],{"type":259,"value":652},{"type":254,"tag":622,"props":20772,"children":20773},{"style":629},[20774],{"type":259,"value":676},{"type":254,"tag":622,"props":20776,"children":20777},{"class":624,"line":836},[20778,20783,20787,20791,20796,20800],{"type":254,"tag":622,"props":20779,"children":20780},{"style":1202},[20781],{"type":259,"value":20782},"        SourceUrl ",{"type":254,"tag":622,"props":20784,"children":20785},{"style":629},[20786],{"type":259,"value":1654},{"type":254,"tag":622,"props":20788,"children":20789},{"style":629},[20790],{"type":259,"value":661},{"type":254,"tag":622,"props":20792,"children":20793},{"style":664},[20794],{"type":259,"value":20795},"https://repo.com",{"type":254,"tag":622,"props":20797,"children":20798},{"style":629},[20799],{"type":259,"value":652},{"type":254,"tag":622,"props":20801,"children":20802},{"style":629},[20803],{"type":259,"value":676},{"type":254,"tag":622,"props":20805,"children":20806},{"class":624,"line":862},[20807,20812,20816],{"type":254,"tag":622,"props":20808,"children":20809},{"style":1202},[20810],{"type":259,"value":20811},"        ServiceConnectionId ",{"type":254,"tag":622,"props":20813,"children":20814},{"style":629},[20815],{"type":259,"value":1654},{"type":254,"tag":622,"props":20817,"children":20818},{"style":629},[20819],{"type":259,"value":20820}," \"\"\n",{"type":254,"tag":622,"props":20822,"children":20823},{"class":624,"line":900},[20824],{"type":254,"tag":622,"props":20825,"children":20826},{"style":629},[20827],{"type":259,"value":20453},{"type":254,"tag":622,"props":20829,"children":20830},{"class":624,"line":938},[20831,20836,20840,20844,20849],{"type":254,"tag":622,"props":20832,"children":20833},{"style":1202},[20834],{"type":259,"value":20835},"    DefaultBranch ",{"type":254,"tag":622,"props":20837,"children":20838},{"style":629},[20839],{"type":259,"value":1654},{"type":254,"tag":622,"props":20841,"children":20842},{"style":629},[20843],{"type":259,"value":661},{"type":254,"tag":622,"props":20845,"children":20846},{"style":664},[20847],{"type":259,"value":20848},"refs/heads/main",{"type":254,"tag":622,"props":20850,"children":20851},{"style":629},[20852],{"type":259,"value":9909},{"type":254,"tag":622,"props":20854,"children":20855},{"class":624,"line":976},[20856],{"type":254,"tag":622,"props":20857,"children":20858},{"style":629},[20859],{"type":259,"value":6661},{"type":254,"tag":427,"props":20861,"children":20862},{"icon":19646},[20863],{"type":254,"tag":255,"props":20864,"children":20865},{},[20866,20868,20874,20875,20881,20882,20888,20890,20896],{"type":259,"value":20867},"We should not have to set the ",{"type":254,"tag":389,"props":20869,"children":20871},{"className":20870},[],[20872],{"type":259,"value":20873},"SourceType",{"type":259,"value":16280},{"type":254,"tag":389,"props":20876,"children":20878},{"className":20877},[],[20879],{"type":259,"value":20880},"SourceUrl",{"type":259,"value":10347},{"type":254,"tag":389,"props":20883,"children":20885},{"className":20884},[],[20886],{"type":259,"value":20887},"ServiceConnectionId",{"type":259,"value":20889}," properties as we are initializing a clean Git repository, not importing one, but it's a workaround because of this ",{"type":254,"tag":262,"props":20891,"children":20894},{"href":20892,"rel":20893},"https://github.com/pulumi/pulumi-azuredevops/issues/66",[266],[20895],{"type":259,"value":8780},{"type":259,"value":20897}," on the provider.",{"type":254,"tag":455,"props":20899,"children":20901},{"id":20900},"configure-the-arm-service-connection-in-azure-devops",[20902],{"type":259,"value":20903},"Configure the ARM Service Connection in Azure DevOps",{"type":254,"tag":255,"props":20905,"children":20906},{},[20907,20909,20916],{"type":259,"value":20908},"In the Azure DevOps provider, the Azure Resource Manager service connection is called a ",{"type":254,"tag":262,"props":20910,"children":20913},{"href":20911,"rel":20912},"https://www.pulumi.com/registry/packages/azuredevops/api-docs/serviceendpointazurerm/#workload-identity-federation-manual-azurerm-service-endpoint-subscription-scoped",[266],[20914],{"type":259,"value":20915},"ServiceEndpointAzureRM",{"type":259,"value":20917},". We can create such a resource like this:",{"type":254,"tag":612,"props":20919,"children":20921},{"className":1423,"code":20920,"language":1425,"meta":205,"style":205},"var serviceConnection = new ServiceEndpointAzureRM(\"AzureServiceConnection\", new()\n{\n    ProjectId = project.Id,\n    ServiceEndpointName = \"azure-with-oidc\",\n    ServiceEndpointAuthenticationScheme = \"WorkloadIdentityFederation\",\n    AzurermSpnTenantid = tenantId,\n    AzurermSubscriptionId = subscriptionId,\n    AzurermSubscriptionName = subscriptionName,\n    Credentials = new ServiceEndpointAzureRMCredentialsArgs()\n    {\n        Serviceprincipalid = servicePrincipal.ApplicationId,\n    }\n});\n",[20922],{"type":254,"tag":389,"props":20923,"children":20924},{"__ignoreMap":205},[20925,20975,20982,21009,21038,21067,21088,21109,21130,21155,21162,21192,21199],{"type":254,"tag":622,"props":20926,"children":20927},{"class":624,"line":625},[20928,20932,20937,20941,20945,20950,20954,20958,20963,20967,20971],{"type":254,"tag":622,"props":20929,"children":20930},{"style":714},[20931],{"type":259,"value":1710},{"type":254,"tag":622,"props":20933,"children":20934},{"style":714},[20935],{"type":259,"value":20936}," serviceConnection",{"type":254,"tag":622,"props":20938,"children":20939},{"style":629},[20940],{"type":259,"value":1619},{"type":254,"tag":622,"props":20942,"children":20943},{"style":629},[20944],{"type":259,"value":1624},{"type":254,"tag":622,"props":20946,"children":20947},{"style":714},[20948],{"type":259,"value":20949}," ServiceEndpointAzureRM",{"type":254,"tag":622,"props":20951,"children":20952},{"style":629},[20953],{"type":259,"value":1668},{"type":254,"tag":622,"props":20955,"children":20956},{"style":629},[20957],{"type":259,"value":652},{"type":254,"tag":622,"props":20959,"children":20960},{"style":664},[20961],{"type":259,"value":20962},"AzureServiceConnection",{"type":254,"tag":622,"props":20964,"children":20965},{"style":629},[20966],{"type":259,"value":652},{"type":254,"tag":622,"props":20968,"children":20969},{"style":629},[20970],{"type":259,"value":3149},{"type":254,"tag":622,"props":20972,"children":20973},{"style":629},[20974],{"type":259,"value":20166},{"type":254,"tag":622,"props":20976,"children":20977},{"class":624,"line":635},[20978],{"type":254,"tag":622,"props":20979,"children":20980},{"style":629},[20981],{"type":259,"value":632},{"type":254,"tag":622,"props":20983,"children":20984},{"class":624,"line":679},[20985,20989,20993,20997,21001,21005],{"type":254,"tag":622,"props":20986,"children":20987},{"style":1202},[20988],{"type":259,"value":20536},{"type":254,"tag":622,"props":20990,"children":20991},{"style":629},[20992],{"type":259,"value":1654},{"type":254,"tag":622,"props":20994,"children":20995},{"style":1202},[20996],{"type":259,"value":20127},{"type":254,"tag":622,"props":20998,"children":20999},{"style":629},[21000],{"type":259,"value":594},{"type":254,"tag":622,"props":21002,"children":21003},{"style":1202},[21004],{"type":259,"value":20553},{"type":254,"tag":622,"props":21006,"children":21007},{"style":629},[21008],{"type":259,"value":676},{"type":254,"tag":622,"props":21010,"children":21011},{"class":624,"line":705},[21012,21017,21021,21025,21030,21034],{"type":254,"tag":622,"props":21013,"children":21014},{"style":1202},[21015],{"type":259,"value":21016},"    ServiceEndpointName ",{"type":254,"tag":622,"props":21018,"children":21019},{"style":629},[21020],{"type":259,"value":1654},{"type":254,"tag":622,"props":21022,"children":21023},{"style":629},[21024],{"type":259,"value":661},{"type":254,"tag":622,"props":21026,"children":21027},{"style":664},[21028],{"type":259,"value":21029},"azure-with-oidc",{"type":254,"tag":622,"props":21031,"children":21032},{"style":629},[21033],{"type":259,"value":652},{"type":254,"tag":622,"props":21035,"children":21036},{"style":629},[21037],{"type":259,"value":676},{"type":254,"tag":622,"props":21039,"children":21040},{"class":624,"line":732},[21041,21046,21050,21054,21059,21063],{"type":254,"tag":622,"props":21042,"children":21043},{"style":1202},[21044],{"type":259,"value":21045},"    ServiceEndpointAuthenticationScheme ",{"type":254,"tag":622,"props":21047,"children":21048},{"style":629},[21049],{"type":259,"value":1654},{"type":254,"tag":622,"props":21051,"children":21052},{"style":629},[21053],{"type":259,"value":661},{"type":254,"tag":622,"props":21055,"children":21056},{"style":664},[21057],{"type":259,"value":21058},"WorkloadIdentityFederation",{"type":254,"tag":622,"props":21060,"children":21061},{"style":629},[21062],{"type":259,"value":652},{"type":254,"tag":622,"props":21064,"children":21065},{"style":629},[21066],{"type":259,"value":676},{"type":254,"tag":622,"props":21068,"children":21069},{"class":624,"line":759},[21070,21075,21079,21084],{"type":254,"tag":622,"props":21071,"children":21072},{"style":1202},[21073],{"type":259,"value":21074},"    AzurermSpnTenantid ",{"type":254,"tag":622,"props":21076,"children":21077},{"style":629},[21078],{"type":259,"value":1654},{"type":254,"tag":622,"props":21080,"children":21081},{"style":1202},[21082],{"type":259,"value":21083}," tenantId",{"type":254,"tag":622,"props":21085,"children":21086},{"style":629},[21087],{"type":259,"value":676},{"type":254,"tag":622,"props":21089,"children":21090},{"class":624,"line":798},[21091,21096,21100,21105],{"type":254,"tag":622,"props":21092,"children":21093},{"style":1202},[21094],{"type":259,"value":21095},"    AzurermSubscriptionId ",{"type":254,"tag":622,"props":21097,"children":21098},{"style":629},[21099],{"type":259,"value":1654},{"type":254,"tag":622,"props":21101,"children":21102},{"style":1202},[21103],{"type":259,"value":21104}," subscriptionId",{"type":254,"tag":622,"props":21106,"children":21107},{"style":629},[21108],{"type":259,"value":676},{"type":254,"tag":622,"props":21110,"children":21111},{"class":624,"line":836},[21112,21117,21121,21126],{"type":254,"tag":622,"props":21113,"children":21114},{"style":1202},[21115],{"type":259,"value":21116},"    AzurermSubscriptionName ",{"type":254,"tag":622,"props":21118,"children":21119},{"style":629},[21120],{"type":259,"value":1654},{"type":254,"tag":622,"props":21122,"children":21123},{"style":1202},[21124],{"type":259,"value":21125}," subscriptionName",{"type":254,"tag":622,"props":21127,"children":21128},{"style":629},[21129],{"type":259,"value":676},{"type":254,"tag":622,"props":21131,"children":21132},{"class":624,"line":862},[21133,21138,21142,21146,21151],{"type":254,"tag":622,"props":21134,"children":21135},{"style":1202},[21136],{"type":259,"value":21137},"    Credentials ",{"type":254,"tag":622,"props":21139,"children":21140},{"style":629},[21141],{"type":259,"value":1654},{"type":254,"tag":622,"props":21143,"children":21144},{"style":629},[21145],{"type":259,"value":1624},{"type":254,"tag":622,"props":21147,"children":21148},{"style":714},[21149],{"type":259,"value":21150}," ServiceEndpointAzureRMCredentialsArgs",{"type":254,"tag":622,"props":21152,"children":21153},{"style":629},[21154],{"type":259,"value":1634},{"type":254,"tag":622,"props":21156,"children":21157},{"class":624,"line":900},[21158],{"type":254,"tag":622,"props":21159,"children":21160},{"style":629},[21161],{"type":259,"value":3574},{"type":254,"tag":622,"props":21163,"children":21164},{"class":624,"line":938},[21165,21170,21174,21179,21183,21188],{"type":254,"tag":622,"props":21166,"children":21167},{"style":1202},[21168],{"type":259,"value":21169},"        Serviceprincipalid ",{"type":254,"tag":622,"props":21171,"children":21172},{"style":629},[21173],{"type":259,"value":1654},{"type":254,"tag":622,"props":21175,"children":21176},{"style":1202},[21177],{"type":259,"value":21178}," servicePrincipal",{"type":254,"tag":622,"props":21180,"children":21181},{"style":629},[21182],{"type":259,"value":594},{"type":254,"tag":622,"props":21184,"children":21185},{"style":1202},[21186],{"type":259,"value":21187},"ApplicationId",{"type":254,"tag":622,"props":21189,"children":21190},{"style":629},[21191],{"type":259,"value":676},{"type":254,"tag":622,"props":21193,"children":21194},{"class":624,"line":976},[21195],{"type":254,"tag":622,"props":21196,"children":21197},{"style":629},[21198],{"type":259,"value":1093},{"type":254,"tag":622,"props":21200,"children":21201},{"class":624,"line":1014},[21202],{"type":254,"tag":622,"props":21203,"children":21204},{"style":629},[21205],{"type":259,"value":6661},{"type":254,"tag":255,"props":21207,"children":21208},{},[21209,21211,21217],{"type":259,"value":21210},"Do not worry about the service principal, we will see in the next section how to create it. The tenant and the subscription identifiers can be retrieved from the current configuration of the Azure Native provider (using the ",{"type":254,"tag":389,"props":21212,"children":21214},{"className":21213},[],[21215],{"type":259,"value":21216},"GetClientConfig.Invoke",{"type":259,"value":21218}," function):",{"type":254,"tag":612,"props":21220,"children":21222},{"className":1423,"code":21221,"language":1425,"meta":205,"style":205},"var azureConfig = GetClientConfig.Invoke();\nvar tenantId = azureConfig.Apply(c => c.tenantId);\nvar subscriptionId = azureConfig.Apply(c => c.SubscriptionId);\n",[21223],{"type":254,"tag":389,"props":21224,"children":21225},{"__ignoreMap":205},[21226,21259,21317],{"type":254,"tag":622,"props":21227,"children":21228},{"class":624,"line":625},[21229,21233,21238,21242,21247,21251,21255],{"type":254,"tag":622,"props":21230,"children":21231},{"style":714},[21232],{"type":259,"value":1710},{"type":254,"tag":622,"props":21234,"children":21235},{"style":714},[21236],{"type":259,"value":21237}," azureConfig",{"type":254,"tag":622,"props":21239,"children":21240},{"style":629},[21241],{"type":259,"value":1619},{"type":254,"tag":622,"props":21243,"children":21244},{"style":1202},[21245],{"type":259,"value":21246}," GetClientConfig",{"type":254,"tag":622,"props":21248,"children":21249},{"style":629},[21250],{"type":259,"value":594},{"type":254,"tag":622,"props":21252,"children":21253},{"style":1735},[21254],{"type":259,"value":20516},{"type":254,"tag":622,"props":21256,"children":21257},{"style":629},[21258],{"type":259,"value":2798},{"type":254,"tag":622,"props":21260,"children":21261},{"class":624,"line":635},[21262,21266,21270,21274,21278,21282,21287,21291,21296,21300,21305,21309,21313],{"type":254,"tag":622,"props":21263,"children":21264},{"style":714},[21265],{"type":259,"value":1710},{"type":254,"tag":622,"props":21267,"children":21268},{"style":714},[21269],{"type":259,"value":21083},{"type":254,"tag":622,"props":21271,"children":21272},{"style":629},[21273],{"type":259,"value":1619},{"type":254,"tag":622,"props":21275,"children":21276},{"style":1202},[21277],{"type":259,"value":21237},{"type":254,"tag":622,"props":21279,"children":21280},{"style":629},[21281],{"type":259,"value":594},{"type":254,"tag":622,"props":21283,"children":21284},{"style":1735},[21285],{"type":259,"value":21286},"Apply",{"type":254,"tag":622,"props":21288,"children":21289},{"style":629},[21290],{"type":259,"value":1668},{"type":254,"tag":622,"props":21292,"children":21293},{"style":714},[21294],{"type":259,"value":21295},"c",{"type":254,"tag":622,"props":21297,"children":21298},{"style":629},[21299],{"type":259,"value":2697},{"type":254,"tag":622,"props":21301,"children":21302},{"style":1202},[21303],{"type":259,"value":21304}," c",{"type":254,"tag":622,"props":21306,"children":21307},{"style":629},[21308],{"type":259,"value":594},{"type":254,"tag":622,"props":21310,"children":21311},{"style":1202},[21312],{"type":259,"value":17204},{"type":254,"tag":622,"props":21314,"children":21315},{"style":629},[21316],{"type":259,"value":1554},{"type":254,"tag":622,"props":21318,"children":21319},{"class":624,"line":679},[21320,21324,21328,21332,21336,21340,21344,21348,21352,21356,21360,21364,21369],{"type":254,"tag":622,"props":21321,"children":21322},{"style":714},[21323],{"type":259,"value":1710},{"type":254,"tag":622,"props":21325,"children":21326},{"style":714},[21327],{"type":259,"value":21104},{"type":254,"tag":622,"props":21329,"children":21330},{"style":629},[21331],{"type":259,"value":1619},{"type":254,"tag":622,"props":21333,"children":21334},{"style":1202},[21335],{"type":259,"value":21237},{"type":254,"tag":622,"props":21337,"children":21338},{"style":629},[21339],{"type":259,"value":594},{"type":254,"tag":622,"props":21341,"children":21342},{"style":1735},[21343],{"type":259,"value":21286},{"type":254,"tag":622,"props":21345,"children":21346},{"style":629},[21347],{"type":259,"value":1668},{"type":254,"tag":622,"props":21349,"children":21350},{"style":714},[21351],{"type":259,"value":21295},{"type":254,"tag":622,"props":21353,"children":21354},{"style":629},[21355],{"type":259,"value":2697},{"type":254,"tag":622,"props":21357,"children":21358},{"style":1202},[21359],{"type":259,"value":21304},{"type":254,"tag":622,"props":21361,"children":21362},{"style":629},[21363],{"type":259,"value":594},{"type":254,"tag":622,"props":21365,"children":21366},{"style":1202},[21367],{"type":259,"value":21368},"SubscriptionId",{"type":254,"tag":622,"props":21370,"children":21371},{"style":629},[21372],{"type":259,"value":1554},{"type":254,"tag":255,"props":21374,"children":21375},{},[21376],{"type":259,"value":21377},"For the subscription name, it's more complicated as we don't have it, and no easy way to retrieve it. To be frank, I think having to provide the subscription name while we already provide the subscription identifier is completely useless but that's how the Azure DevOps provider works.",{"type":254,"tag":255,"props":21379,"children":21380},{},[21381,21383,21390,21392,21398],{"type":259,"value":21382},"The Azure Classic provider offers a ",{"type":254,"tag":262,"props":21384,"children":21387},{"href":21385,"rel":21386},"https://www.pulumi.com/registry/packages/azure/api-docs/core/getsubscription/#azure-core-getsubscription",[266],[21388],{"type":259,"value":21389},"function",{"type":259,"value":21391}," to get a subscription by its identifier but it's not available in the Azure Native provider. I don't want to add the Azure Classic provider to my project solely for this purpose. However, it's not a big deal as it allows us to experience one of the advantages of using Pulumi: when something is not available you can just implement it or use any library that can help you, such as the ",{"type":254,"tag":262,"props":21393,"children":21396},{"href":21394,"rel":21395},"https://www.nuget.org/packages/Azure.ResourceManager",[266],[21397],{"type":259,"value":6261},{"type":259,"value":21399}," in this case.",{"type":254,"tag":612,"props":21401,"children":21403},{"className":1423,"code":21402,"language":1425,"meta":205,"style":205},"var subscriptionName = subscriptionId.Apply(s =>\n{\n    var armClient = new ArmClient(new DefaultAzureCredential());\n    var subscription = armClient.GetSubscriptionResource(new ResourceIdentifier($\"/subscriptions/{s}\")).Get();\n    return subscription.Value.Data.DisplayName;\n});\n",[21404],{"type":254,"tag":389,"props":21405,"children":21406},{"__ignoreMap":205},[21407,21447,21454,21491,21567,21608],{"type":254,"tag":622,"props":21408,"children":21409},{"class":624,"line":625},[21410,21414,21418,21422,21426,21430,21434,21438,21443],{"type":254,"tag":622,"props":21411,"children":21412},{"style":714},[21413],{"type":259,"value":1710},{"type":254,"tag":622,"props":21415,"children":21416},{"style":714},[21417],{"type":259,"value":21125},{"type":254,"tag":622,"props":21419,"children":21420},{"style":629},[21421],{"type":259,"value":1619},{"type":254,"tag":622,"props":21423,"children":21424},{"style":1202},[21425],{"type":259,"value":21104},{"type":254,"tag":622,"props":21427,"children":21428},{"style":629},[21429],{"type":259,"value":594},{"type":254,"tag":622,"props":21431,"children":21432},{"style":1735},[21433],{"type":259,"value":21286},{"type":254,"tag":622,"props":21435,"children":21436},{"style":629},[21437],{"type":259,"value":1668},{"type":254,"tag":622,"props":21439,"children":21440},{"style":714},[21441],{"type":259,"value":21442},"s",{"type":254,"tag":622,"props":21444,"children":21445},{"style":629},[21446],{"type":259,"value":6569},{"type":254,"tag":622,"props":21448,"children":21449},{"class":624,"line":635},[21450],{"type":254,"tag":622,"props":21451,"children":21452},{"style":629},[21453],{"type":259,"value":632},{"type":254,"tag":622,"props":21455,"children":21456},{"class":624,"line":679},[21457,21461,21466,21470,21474,21479,21483,21487],{"type":254,"tag":622,"props":21458,"children":21459},{"style":714},[21460],{"type":259,"value":2099},{"type":254,"tag":622,"props":21462,"children":21463},{"style":714},[21464],{"type":259,"value":21465}," armClient",{"type":254,"tag":622,"props":21467,"children":21468},{"style":629},[21469],{"type":259,"value":1619},{"type":254,"tag":622,"props":21471,"children":21472},{"style":629},[21473],{"type":259,"value":1624},{"type":254,"tag":622,"props":21475,"children":21476},{"style":714},[21477],{"type":259,"value":21478}," ArmClient",{"type":254,"tag":622,"props":21480,"children":21481},{"style":629},[21482],{"type":259,"value":6598},{"type":254,"tag":622,"props":21484,"children":21485},{"style":714},[21486],{"type":259,"value":6649},{"type":254,"tag":622,"props":21488,"children":21489},{"style":629},[21490],{"type":259,"value":2154},{"type":254,"tag":622,"props":21492,"children":21493},{"class":624,"line":705},[21494,21498,21503,21507,21511,21515,21520,21524,21529,21533,21537,21542,21546,21550,21554,21559,21563],{"type":254,"tag":622,"props":21495,"children":21496},{"style":714},[21497],{"type":259,"value":2099},{"type":254,"tag":622,"props":21499,"children":21500},{"style":714},[21501],{"type":259,"value":21502}," subscription",{"type":254,"tag":622,"props":21504,"children":21505},{"style":629},[21506],{"type":259,"value":1619},{"type":254,"tag":622,"props":21508,"children":21509},{"style":1202},[21510],{"type":259,"value":21465},{"type":254,"tag":622,"props":21512,"children":21513},{"style":629},[21514],{"type":259,"value":594},{"type":254,"tag":622,"props":21516,"children":21517},{"style":1735},[21518],{"type":259,"value":21519},"GetSubscriptionResource",{"type":254,"tag":622,"props":21521,"children":21522},{"style":629},[21523],{"type":259,"value":6598},{"type":254,"tag":622,"props":21525,"children":21526},{"style":714},[21527],{"type":259,"value":21528}," ResourceIdentifier",{"type":254,"tag":622,"props":21530,"children":21531},{"style":629},[21532],{"type":259,"value":1668},{"type":254,"tag":622,"props":21534,"children":21535},{"style":629},[21536],{"type":259,"value":9607},{"type":254,"tag":622,"props":21538,"children":21539},{"style":664},[21540],{"type":259,"value":21541},"/subscriptions/",{"type":254,"tag":622,"props":21543,"children":21544},{"style":629},[21545],{"type":259,"value":3234},{"type":254,"tag":622,"props":21547,"children":21548},{"style":1202},[21549],{"type":259,"value":21442},{"type":254,"tag":622,"props":21551,"children":21552},{"style":629},[21553],{"type":259,"value":2443},{"type":254,"tag":622,"props":21555,"children":21556},{"style":629},[21557],{"type":259,"value":21558},")).",{"type":254,"tag":622,"props":21560,"children":21561},{"style":1735},[21562],{"type":259,"value":15108},{"type":254,"tag":622,"props":21564,"children":21565},{"style":629},[21566],{"type":259,"value":2798},{"type":254,"tag":622,"props":21568,"children":21569},{"class":624,"line":732},[21570,21574,21578,21582,21586,21590,21595,21599,21604],{"type":254,"tag":622,"props":21571,"children":21572},{"style":1196},[21573],{"type":259,"value":2358},{"type":254,"tag":622,"props":21575,"children":21576},{"style":1202},[21577],{"type":259,"value":21502},{"type":254,"tag":622,"props":21579,"children":21580},{"style":629},[21581],{"type":259,"value":594},{"type":254,"tag":622,"props":21583,"children":21584},{"style":1202},[21585],{"type":259,"value":9034},{"type":254,"tag":622,"props":21587,"children":21588},{"style":629},[21589],{"type":259,"value":594},{"type":254,"tag":622,"props":21591,"children":21592},{"style":1202},[21593],{"type":259,"value":21594},"Data",{"type":254,"tag":622,"props":21596,"children":21597},{"style":629},[21598],{"type":259,"value":594},{"type":254,"tag":622,"props":21600,"children":21601},{"style":1202},[21602],{"type":259,"value":21603},"DisplayName",{"type":254,"tag":622,"props":21605,"children":21606},{"style":629},[21607],{"type":259,"value":2350},{"type":254,"tag":622,"props":21609,"children":21610},{"class":624,"line":759},[21611],{"type":254,"tag":622,"props":21612,"children":21613},{"style":629},[21614],{"type":259,"value":6661},{"type":254,"tag":455,"props":21616,"children":21618},{"id":21617},"set-up-the-necessary-microsoft-entra-id-resources",[21619],{"type":259,"value":21620},"Set up the necessary Microsoft Entra ID resources",{"type":254,"tag":255,"props":21622,"children":21623},{},[21624],{"type":259,"value":21625},"We need to set up the following resources in Microsoft Entra ID:",{"type":254,"tag":332,"props":21627,"children":21628},{},[21629,21634,21639],{"type":254,"tag":336,"props":21630,"children":21631},{},[21632],{"type":259,"value":21633},"an Application that represents the Azure DevOps service connection identity",{"type":254,"tag":336,"props":21635,"children":21636},{},[21637],{"type":259,"value":21638},"a Service Principal (related to the application above) that has the contributor role on the Azure subscription",{"type":254,"tag":336,"props":21640,"children":21641},{},[21642],{"type":259,"value":21643},"credentials for the CI/CD pipeline to authenticate to Azure on behalf of this Microsoft Entra ID application",{"type":254,"tag":255,"props":21645,"children":21646},{},[21647],{"type":259,"value":21648},"Let's take care of the first 2 points:",{"type":254,"tag":612,"props":21650,"children":21652},{"className":1423,"code":21651,"language":1425,"meta":205,"style":205},"var azureConfig = GetClientConfig.Invoke();\nvar aadApplication = new Application(\"ADOAzureReadyApp\", new()\n{\n    DisplayName = \"ADO Azure Ready App\"\n});\nvar servicePrincipal  = new ServicePrincipal(\"AzureReadyServicePrincipal\", new()\n{\n    ApplicationId = aadApplication.ApplicationId,\n});\n\nvar subscriptionId = azureConfig.Apply(c => c.SubscriptionId);\nnew RoleAssignment(\"contributor\", new()\n{\n    PrincipalId= servicePrincipal.Id,\n    PrincipalType= PrincipalType.ServicePrincipal,\n    RoleDefinitionId = AzureBuiltInRoles.Contributor,\n    Scope = Output.Format($\"/subscriptions/{subscriptionId}\")\n});\n",[21653],{"type":254,"tag":389,"props":21654,"children":21655},{"__ignoreMap":205},[21656,21687,21737,21744,21769,21776,21826,21833,21861,21868,21875,21930,21968,21975,22003,22033,22063,22117],{"type":254,"tag":622,"props":21657,"children":21658},{"class":624,"line":625},[21659,21663,21667,21671,21675,21679,21683],{"type":254,"tag":622,"props":21660,"children":21661},{"style":714},[21662],{"type":259,"value":1710},{"type":254,"tag":622,"props":21664,"children":21665},{"style":714},[21666],{"type":259,"value":21237},{"type":254,"tag":622,"props":21668,"children":21669},{"style":629},[21670],{"type":259,"value":1619},{"type":254,"tag":622,"props":21672,"children":21673},{"style":1202},[21674],{"type":259,"value":21246},{"type":254,"tag":622,"props":21676,"children":21677},{"style":629},[21678],{"type":259,"value":594},{"type":254,"tag":622,"props":21680,"children":21681},{"style":1735},[21682],{"type":259,"value":20516},{"type":254,"tag":622,"props":21684,"children":21685},{"style":629},[21686],{"type":259,"value":2798},{"type":254,"tag":622,"props":21688,"children":21689},{"class":624,"line":635},[21690,21694,21699,21703,21707,21712,21716,21720,21725,21729,21733],{"type":254,"tag":622,"props":21691,"children":21692},{"style":714},[21693],{"type":259,"value":1710},{"type":254,"tag":622,"props":21695,"children":21696},{"style":714},[21697],{"type":259,"value":21698}," aadApplication",{"type":254,"tag":622,"props":21700,"children":21701},{"style":629},[21702],{"type":259,"value":1619},{"type":254,"tag":622,"props":21704,"children":21705},{"style":629},[21706],{"type":259,"value":1624},{"type":254,"tag":622,"props":21708,"children":21709},{"style":714},[21710],{"type":259,"value":21711}," Application",{"type":254,"tag":622,"props":21713,"children":21714},{"style":629},[21715],{"type":259,"value":1668},{"type":254,"tag":622,"props":21717,"children":21718},{"style":629},[21719],{"type":259,"value":652},{"type":254,"tag":622,"props":21721,"children":21722},{"style":664},[21723],{"type":259,"value":21724},"ADOAzureReadyApp",{"type":254,"tag":622,"props":21726,"children":21727},{"style":629},[21728],{"type":259,"value":652},{"type":254,"tag":622,"props":21730,"children":21731},{"style":629},[21732],{"type":259,"value":3149},{"type":254,"tag":622,"props":21734,"children":21735},{"style":629},[21736],{"type":259,"value":20166},{"type":254,"tag":622,"props":21738,"children":21739},{"class":624,"line":679},[21740],{"type":254,"tag":622,"props":21741,"children":21742},{"style":629},[21743],{"type":259,"value":632},{"type":254,"tag":622,"props":21745,"children":21746},{"class":624,"line":705},[21747,21752,21756,21760,21765],{"type":254,"tag":622,"props":21748,"children":21749},{"style":1202},[21750],{"type":259,"value":21751},"    DisplayName ",{"type":254,"tag":622,"props":21753,"children":21754},{"style":629},[21755],{"type":259,"value":1654},{"type":254,"tag":622,"props":21757,"children":21758},{"style":629},[21759],{"type":259,"value":661},{"type":254,"tag":622,"props":21761,"children":21762},{"style":664},[21763],{"type":259,"value":21764},"ADO Azure Ready App",{"type":254,"tag":622,"props":21766,"children":21767},{"style":629},[21768],{"type":259,"value":9909},{"type":254,"tag":622,"props":21770,"children":21771},{"class":624,"line":732},[21772],{"type":254,"tag":622,"props":21773,"children":21774},{"style":629},[21775],{"type":259,"value":6661},{"type":254,"tag":622,"props":21777,"children":21778},{"class":624,"line":759},[21779,21783,21787,21792,21796,21801,21805,21809,21814,21818,21822],{"type":254,"tag":622,"props":21780,"children":21781},{"style":714},[21782],{"type":259,"value":1710},{"type":254,"tag":622,"props":21784,"children":21785},{"style":714},[21786],{"type":259,"value":21178},{"type":254,"tag":622,"props":21788,"children":21789},{"style":629},[21790],{"type":259,"value":21791},"  =",{"type":254,"tag":622,"props":21793,"children":21794},{"style":629},[21795],{"type":259,"value":1624},{"type":254,"tag":622,"props":21797,"children":21798},{"style":714},[21799],{"type":259,"value":21800}," ServicePrincipal",{"type":254,"tag":622,"props":21802,"children":21803},{"style":629},[21804],{"type":259,"value":1668},{"type":254,"tag":622,"props":21806,"children":21807},{"style":629},[21808],{"type":259,"value":652},{"type":254,"tag":622,"props":21810,"children":21811},{"style":664},[21812],{"type":259,"value":21813},"AzureReadyServicePrincipal",{"type":254,"tag":622,"props":21815,"children":21816},{"style":629},[21817],{"type":259,"value":652},{"type":254,"tag":622,"props":21819,"children":21820},{"style":629},[21821],{"type":259,"value":3149},{"type":254,"tag":622,"props":21823,"children":21824},{"style":629},[21825],{"type":259,"value":20166},{"type":254,"tag":622,"props":21827,"children":21828},{"class":624,"line":798},[21829],{"type":254,"tag":622,"props":21830,"children":21831},{"style":629},[21832],{"type":259,"value":632},{"type":254,"tag":622,"props":21834,"children":21835},{"class":624,"line":836},[21836,21841,21845,21849,21853,21857],{"type":254,"tag":622,"props":21837,"children":21838},{"style":1202},[21839],{"type":259,"value":21840},"    ApplicationId ",{"type":254,"tag":622,"props":21842,"children":21843},{"style":629},[21844],{"type":259,"value":1654},{"type":254,"tag":622,"props":21846,"children":21847},{"style":1202},[21848],{"type":259,"value":21698},{"type":254,"tag":622,"props":21850,"children":21851},{"style":629},[21852],{"type":259,"value":594},{"type":254,"tag":622,"props":21854,"children":21855},{"style":1202},[21856],{"type":259,"value":21187},{"type":254,"tag":622,"props":21858,"children":21859},{"style":629},[21860],{"type":259,"value":676},{"type":254,"tag":622,"props":21862,"children":21863},{"class":624,"line":862},[21864],{"type":254,"tag":622,"props":21865,"children":21866},{"style":629},[21867],{"type":259,"value":6661},{"type":254,"tag":622,"props":21869,"children":21870},{"class":624,"line":900},[21871],{"type":254,"tag":622,"props":21872,"children":21873},{"emptyLinePlaceholder":1699},[21874],{"type":259,"value":1702},{"type":254,"tag":622,"props":21876,"children":21877},{"class":624,"line":938},[21878,21882,21886,21890,21894,21898,21902,21906,21910,21914,21918,21922,21926],{"type":254,"tag":622,"props":21879,"children":21880},{"style":714},[21881],{"type":259,"value":1710},{"type":254,"tag":622,"props":21883,"children":21884},{"style":714},[21885],{"type":259,"value":21104},{"type":254,"tag":622,"props":21887,"children":21888},{"style":629},[21889],{"type":259,"value":1619},{"type":254,"tag":622,"props":21891,"children":21892},{"style":1202},[21893],{"type":259,"value":21237},{"type":254,"tag":622,"props":21895,"children":21896},{"style":629},[21897],{"type":259,"value":594},{"type":254,"tag":622,"props":21899,"children":21900},{"style":1735},[21901],{"type":259,"value":21286},{"type":254,"tag":622,"props":21903,"children":21904},{"style":629},[21905],{"type":259,"value":1668},{"type":254,"tag":622,"props":21907,"children":21908},{"style":714},[21909],{"type":259,"value":21295},{"type":254,"tag":622,"props":21911,"children":21912},{"style":629},[21913],{"type":259,"value":2697},{"type":254,"tag":622,"props":21915,"children":21916},{"style":1202},[21917],{"type":259,"value":21304},{"type":254,"tag":622,"props":21919,"children":21920},{"style":629},[21921],{"type":259,"value":594},{"type":254,"tag":622,"props":21923,"children":21924},{"style":1202},[21925],{"type":259,"value":21368},{"type":254,"tag":622,"props":21927,"children":21928},{"style":629},[21929],{"type":259,"value":1554},{"type":254,"tag":622,"props":21931,"children":21932},{"class":624,"line":976},[21933,21938,21943,21947,21951,21956,21960,21964],{"type":254,"tag":622,"props":21934,"children":21935},{"style":644},[21936],{"type":259,"value":21937},"new",{"type":254,"tag":622,"props":21939,"children":21940},{"style":1735},[21941],{"type":259,"value":21942}," RoleAssignment",{"type":254,"tag":622,"props":21944,"children":21945},{"style":629},[21946],{"type":259,"value":1668},{"type":254,"tag":622,"props":21948,"children":21949},{"style":629},[21950],{"type":259,"value":652},{"type":254,"tag":622,"props":21952,"children":21953},{"style":664},[21954],{"type":259,"value":21955},"contributor",{"type":254,"tag":622,"props":21957,"children":21958},{"style":629},[21959],{"type":259,"value":652},{"type":254,"tag":622,"props":21961,"children":21962},{"style":629},[21963],{"type":259,"value":3149},{"type":254,"tag":622,"props":21965,"children":21966},{"style":629},[21967],{"type":259,"value":20166},{"type":254,"tag":622,"props":21969,"children":21970},{"class":624,"line":1014},[21971],{"type":254,"tag":622,"props":21972,"children":21973},{"style":629},[21974],{"type":259,"value":632},{"type":254,"tag":622,"props":21976,"children":21977},{"class":624,"line":1052},[21978,21983,21987,21991,21995,21999],{"type":254,"tag":622,"props":21979,"children":21980},{"style":1202},[21981],{"type":259,"value":21982},"    PrincipalId",{"type":254,"tag":622,"props":21984,"children":21985},{"style":629},[21986],{"type":259,"value":1654},{"type":254,"tag":622,"props":21988,"children":21989},{"style":1202},[21990],{"type":259,"value":21178},{"type":254,"tag":622,"props":21992,"children":21993},{"style":629},[21994],{"type":259,"value":594},{"type":254,"tag":622,"props":21996,"children":21997},{"style":1202},[21998],{"type":259,"value":20553},{"type":254,"tag":622,"props":22000,"children":22001},{"style":629},[22002],{"type":259,"value":676},{"type":254,"tag":622,"props":22004,"children":22005},{"class":624,"line":1078},[22006,22011,22015,22020,22024,22029],{"type":254,"tag":622,"props":22007,"children":22008},{"style":1202},[22009],{"type":259,"value":22010},"    PrincipalType",{"type":254,"tag":622,"props":22012,"children":22013},{"style":629},[22014],{"type":259,"value":1654},{"type":254,"tag":622,"props":22016,"children":22017},{"style":1202},[22018],{"type":259,"value":22019}," PrincipalType",{"type":254,"tag":622,"props":22021,"children":22022},{"style":629},[22023],{"type":259,"value":594},{"type":254,"tag":622,"props":22025,"children":22026},{"style":1202},[22027],{"type":259,"value":22028},"ServicePrincipal",{"type":254,"tag":622,"props":22030,"children":22031},{"style":629},[22032],{"type":259,"value":676},{"type":254,"tag":622,"props":22034,"children":22035},{"class":624,"line":1087},[22036,22041,22045,22050,22054,22059],{"type":254,"tag":622,"props":22037,"children":22038},{"style":1202},[22039],{"type":259,"value":22040},"    RoleDefinitionId ",{"type":254,"tag":622,"props":22042,"children":22043},{"style":629},[22044],{"type":259,"value":1654},{"type":254,"tag":622,"props":22046,"children":22047},{"style":1202},[22048],{"type":259,"value":22049}," AzureBuiltInRoles",{"type":254,"tag":622,"props":22051,"children":22052},{"style":629},[22053],{"type":259,"value":594},{"type":254,"tag":622,"props":22055,"children":22056},{"style":1202},[22057],{"type":259,"value":22058},"Contributor",{"type":254,"tag":622,"props":22060,"children":22061},{"style":629},[22062],{"type":259,"value":676},{"type":254,"tag":622,"props":22064,"children":22065},{"class":624,"line":1096},[22066,22071,22075,22080,22084,22089,22093,22097,22101,22105,22109,22113],{"type":254,"tag":622,"props":22067,"children":22068},{"style":1202},[22069],{"type":259,"value":22070},"    Scope ",{"type":254,"tag":622,"props":22072,"children":22073},{"style":629},[22074],{"type":259,"value":1654},{"type":254,"tag":622,"props":22076,"children":22077},{"style":1202},[22078],{"type":259,"value":22079}," Output",{"type":254,"tag":622,"props":22081,"children":22082},{"style":629},[22083],{"type":259,"value":594},{"type":254,"tag":622,"props":22085,"children":22086},{"style":1735},[22087],{"type":259,"value":22088},"Format",{"type":254,"tag":622,"props":22090,"children":22091},{"style":629},[22092],{"type":259,"value":1668},{"type":254,"tag":622,"props":22094,"children":22095},{"style":629},[22096],{"type":259,"value":9607},{"type":254,"tag":622,"props":22098,"children":22099},{"style":664},[22100],{"type":259,"value":21541},{"type":254,"tag":622,"props":22102,"children":22103},{"style":629},[22104],{"type":259,"value":3234},{"type":254,"tag":622,"props":22106,"children":22107},{"style":1202},[22108],{"type":259,"value":17148},{"type":254,"tag":622,"props":22110,"children":22111},{"style":629},[22112],{"type":259,"value":2443},{"type":254,"tag":622,"props":22114,"children":22115},{"style":629},[22116],{"type":259,"value":1685},{"type":254,"tag":622,"props":22118,"children":22119},{"class":624,"line":1105},[22120],{"type":254,"tag":622,"props":22121,"children":22122},{"style":629},[22123],{"type":259,"value":6661},{"type":254,"tag":427,"props":22125,"children":22126},{"icon":19646},[22127],{"type":254,"tag":255,"props":22128,"children":22129},{},[22130,22132],{"type":259,"value":22131},"It's worth mentioning that using an Application and its associated Service Principal is not the only way to proceed, we could have created instead a ",{"type":254,"tag":262,"props":22133,"children":22136},{"href":22134,"rel":22135},"https://www.pulumi.com/registry/packages/azure-native/api-docs/managedidentity/userassignedidentity/",[266],[22137],{"type":259,"value":22138},"User Assigned Identity",{"type":254,"tag":255,"props":22140,"children":22141},{},[22142],{"type":259,"value":22143},"Now that everything is created, we can create the Federated identity credentials:",{"type":254,"tag":612,"props":22145,"children":22147},{"className":1423,"code":22146,"language":1425,"meta":205,"style":205},"new ApplicationFederatedIdentityCredential(\"ADOAzureReadyAppFederatedIdentityCredential\", new() \n{\n    ApplicationObjectId = aadApplication.ObjectId,\n    DisplayName = \"AzureReadyDeploys\",\n    Description = \"Deployments for azure-ready-repository\",\n    Audiences = new(){\"api://AzureADTokenExchange\" },\n    Issuer = serviceConnection.WorkloadIdentityFederationIssuer,\n    Subject = Output.Format($\"sc://{organisationName}/{project.Name}/{serviceConnection.ServiceEndpointName}\")\n});\n",[22148],{"type":254,"tag":389,"props":22149,"children":22150},{"__ignoreMap":205},[22151,22193,22200,22229,22257,22285,22319,22348,22453],{"type":254,"tag":622,"props":22152,"children":22153},{"class":624,"line":625},[22154,22158,22163,22167,22171,22176,22180,22184,22189],{"type":254,"tag":622,"props":22155,"children":22156},{"style":644},[22157],{"type":259,"value":21937},{"type":254,"tag":622,"props":22159,"children":22160},{"style":1735},[22161],{"type":259,"value":22162}," ApplicationFederatedIdentityCredential",{"type":254,"tag":622,"props":22164,"children":22165},{"style":629},[22166],{"type":259,"value":1668},{"type":254,"tag":622,"props":22168,"children":22169},{"style":629},[22170],{"type":259,"value":652},{"type":254,"tag":622,"props":22172,"children":22173},{"style":664},[22174],{"type":259,"value":22175},"ADOAzureReadyAppFederatedIdentityCredential",{"type":254,"tag":622,"props":22177,"children":22178},{"style":629},[22179],{"type":259,"value":652},{"type":254,"tag":622,"props":22181,"children":22182},{"style":629},[22183],{"type":259,"value":3149},{"type":254,"tag":622,"props":22185,"children":22186},{"style":629},[22187],{"type":259,"value":22188}," new()",{"type":254,"tag":622,"props":22190,"children":22191},{"style":1202},[22192],{"type":259,"value":2314},{"type":254,"tag":622,"props":22194,"children":22195},{"class":624,"line":635},[22196],{"type":254,"tag":622,"props":22197,"children":22198},{"style":629},[22199],{"type":259,"value":632},{"type":254,"tag":622,"props":22201,"children":22202},{"class":624,"line":679},[22203,22208,22212,22216,22220,22225],{"type":254,"tag":622,"props":22204,"children":22205},{"style":1202},[22206],{"type":259,"value":22207},"    ApplicationObjectId ",{"type":254,"tag":622,"props":22209,"children":22210},{"style":629},[22211],{"type":259,"value":1654},{"type":254,"tag":622,"props":22213,"children":22214},{"style":1202},[22215],{"type":259,"value":21698},{"type":254,"tag":622,"props":22217,"children":22218},{"style":629},[22219],{"type":259,"value":594},{"type":254,"tag":622,"props":22221,"children":22222},{"style":1202},[22223],{"type":259,"value":22224},"ObjectId",{"type":254,"tag":622,"props":22226,"children":22227},{"style":629},[22228],{"type":259,"value":676},{"type":254,"tag":622,"props":22230,"children":22231},{"class":624,"line":705},[22232,22236,22240,22244,22249,22253],{"type":254,"tag":622,"props":22233,"children":22234},{"style":1202},[22235],{"type":259,"value":21751},{"type":254,"tag":622,"props":22237,"children":22238},{"style":629},[22239],{"type":259,"value":1654},{"type":254,"tag":622,"props":22241,"children":22242},{"style":629},[22243],{"type":259,"value":661},{"type":254,"tag":622,"props":22245,"children":22246},{"style":664},[22247],{"type":259,"value":22248},"AzureReadyDeploys",{"type":254,"tag":622,"props":22250,"children":22251},{"style":629},[22252],{"type":259,"value":652},{"type":254,"tag":622,"props":22254,"children":22255},{"style":629},[22256],{"type":259,"value":676},{"type":254,"tag":622,"props":22258,"children":22259},{"class":624,"line":732},[22260,22264,22268,22272,22277,22281],{"type":254,"tag":622,"props":22261,"children":22262},{"style":1202},[22263],{"type":259,"value":20181},{"type":254,"tag":622,"props":22265,"children":22266},{"style":629},[22267],{"type":259,"value":1654},{"type":254,"tag":622,"props":22269,"children":22270},{"style":629},[22271],{"type":259,"value":661},{"type":254,"tag":622,"props":22273,"children":22274},{"style":664},[22275],{"type":259,"value":22276},"Deployments for azure-ready-repository",{"type":254,"tag":622,"props":22278,"children":22279},{"style":629},[22280],{"type":259,"value":652},{"type":254,"tag":622,"props":22282,"children":22283},{"style":629},[22284],{"type":259,"value":676},{"type":254,"tag":622,"props":22286,"children":22287},{"class":624,"line":759},[22288,22293,22297,22302,22306,22310,22314],{"type":254,"tag":622,"props":22289,"children":22290},{"style":1202},[22291],{"type":259,"value":22292},"    Audiences ",{"type":254,"tag":622,"props":22294,"children":22295},{"style":629},[22296],{"type":259,"value":1654},{"type":254,"tag":622,"props":22298,"children":22299},{"style":629},[22300],{"type":259,"value":22301}," new(){",{"type":254,"tag":622,"props":22303,"children":22304},{"style":629},[22305],{"type":259,"value":652},{"type":254,"tag":622,"props":22307,"children":22308},{"style":664},[22309],{"type":259,"value":17783},{"type":254,"tag":622,"props":22311,"children":22312},{"style":629},[22313],{"type":259,"value":652},{"type":254,"tag":622,"props":22315,"children":22316},{"style":629},[22317],{"type":259,"value":22318}," },\n",{"type":254,"tag":622,"props":22320,"children":22321},{"class":624,"line":798},[22322,22327,22331,22335,22339,22344],{"type":254,"tag":622,"props":22323,"children":22324},{"style":1202},[22325],{"type":259,"value":22326},"    Issuer ",{"type":254,"tag":622,"props":22328,"children":22329},{"style":629},[22330],{"type":259,"value":1654},{"type":254,"tag":622,"props":22332,"children":22333},{"style":1202},[22334],{"type":259,"value":20936},{"type":254,"tag":622,"props":22336,"children":22337},{"style":629},[22338],{"type":259,"value":594},{"type":254,"tag":622,"props":22340,"children":22341},{"style":1202},[22342],{"type":259,"value":22343},"WorkloadIdentityFederationIssuer",{"type":254,"tag":622,"props":22345,"children":22346},{"style":629},[22347],{"type":259,"value":676},{"type":254,"tag":622,"props":22349,"children":22350},{"class":624,"line":836},[22351,22356,22360,22364,22368,22372,22376,22380,22385,22389,22394,22398,22402,22406,22411,22415,22419,22423,22427,22431,22436,22440,22445,22449],{"type":254,"tag":622,"props":22352,"children":22353},{"style":1202},[22354],{"type":259,"value":22355},"    Subject ",{"type":254,"tag":622,"props":22357,"children":22358},{"style":629},[22359],{"type":259,"value":1654},{"type":254,"tag":622,"props":22361,"children":22362},{"style":1202},[22363],{"type":259,"value":22079},{"type":254,"tag":622,"props":22365,"children":22366},{"style":629},[22367],{"type":259,"value":594},{"type":254,"tag":622,"props":22369,"children":22370},{"style":1735},[22371],{"type":259,"value":22088},{"type":254,"tag":622,"props":22373,"children":22374},{"style":629},[22375],{"type":259,"value":1668},{"type":254,"tag":622,"props":22377,"children":22378},{"style":629},[22379],{"type":259,"value":9607},{"type":254,"tag":622,"props":22381,"children":22382},{"style":664},[22383],{"type":259,"value":22384},"sc://",{"type":254,"tag":622,"props":22386,"children":22387},{"style":629},[22388],{"type":259,"value":3234},{"type":254,"tag":622,"props":22390,"children":22391},{"style":1202},[22392],{"type":259,"value":22393},"organisationName",{"type":254,"tag":622,"props":22395,"children":22396},{"style":629},[22397],{"type":259,"value":2421},{"type":254,"tag":622,"props":22399,"children":22400},{"style":664},[22401],{"type":259,"value":9768},{"type":254,"tag":622,"props":22403,"children":22404},{"style":629},[22405],{"type":259,"value":3234},{"type":254,"tag":622,"props":22407,"children":22408},{"style":1202},[22409],{"type":259,"value":22410},"project",{"type":254,"tag":622,"props":22412,"children":22413},{"style":629},[22414],{"type":259,"value":594},{"type":254,"tag":622,"props":22416,"children":22417},{"style":1202},[22418],{"type":259,"value":2254},{"type":254,"tag":622,"props":22420,"children":22421},{"style":629},[22422],{"type":259,"value":2421},{"type":254,"tag":622,"props":22424,"children":22425},{"style":664},[22426],{"type":259,"value":9768},{"type":254,"tag":622,"props":22428,"children":22429},{"style":629},[22430],{"type":259,"value":3234},{"type":254,"tag":622,"props":22432,"children":22433},{"style":1202},[22434],{"type":259,"value":22435},"serviceConnection",{"type":254,"tag":622,"props":22437,"children":22438},{"style":629},[22439],{"type":259,"value":594},{"type":254,"tag":622,"props":22441,"children":22442},{"style":1202},[22443],{"type":259,"value":22444},"ServiceEndpointName",{"type":254,"tag":622,"props":22446,"children":22447},{"style":629},[22448],{"type":259,"value":2443},{"type":254,"tag":622,"props":22450,"children":22451},{"style":629},[22452],{"type":259,"value":1685},{"type":254,"tag":622,"props":22454,"children":22455},{"class":624,"line":862},[22456],{"type":254,"tag":622,"props":22457,"children":22458},{"style":629},[22459],{"type":259,"value":6661},{"type":254,"tag":255,"props":22461,"children":22462},{},[22463,22465,22471],{"type":259,"value":22464},"You can observe that the federation subject adheres to a particular format (",{"type":254,"tag":389,"props":22466,"children":22468},{"className":22467},[],[22469],{"type":259,"value":22470},"sc://\u003Corg>/\u003Cproject>/\u003Cservice connection name>",{"type":259,"value":22472},"), which identifies the service connection authorized for authentication with Azure.",{"type":254,"tag":455,"props":22474,"children":22476},{"id":22475},"create-the-deployment-pipeline",[22477],{"type":259,"value":22478},"Create the deployment pipeline",{"type":254,"tag":255,"props":22480,"children":22481},{},[22482],{"type":259,"value":22483},"We have completed the configuration of an ARM Service Connection that employs Workload Identity Federation for authentication with Azure. While we could stop at this point, it would be nice to automate the creation of a pipeline that utilizes this service connection and seize the opportunity to ensure everything works properly.",{"type":254,"tag":255,"props":22485,"children":22486},{},[22487,22489,22495],{"type":259,"value":22488},"For this purpose, I have written a very simple YAML pipeline that runs the ",{"type":254,"tag":389,"props":22490,"children":22492},{"className":22491},[],[22493],{"type":259,"value":22494},"AzureCLI",{"type":259,"value":22496}," task to show information about the Azure subscription associated with the previously created service connection.",{"type":254,"tag":612,"props":22498,"children":22500},{"className":1423,"code":22499,"language":1425,"meta":205,"style":205},"trigger:\n  - main\n\npool:\n  vmImage: ubuntu-latest\n\nsteps:\n  - task: AzureCLI@2\n    inputs:\n      azureSubscription: 'azure-with-oidc'\n      scriptType: 'pscore'\n      scriptLocation: 'inlineScript'\n      inlineScript: 'az account show --query id -o tsv'\n",[22501],{"type":254,"tag":389,"props":22502,"children":22503},{"__ignoreMap":205},[22504,22516,22529,22536,22548,22574,22581,22593,22619,22631,22655,22680,22705],{"type":254,"tag":622,"props":22505,"children":22506},{"class":624,"line":625},[22507,22512],{"type":254,"tag":622,"props":22508,"children":22509},{"style":714},[22510],{"type":259,"value":22511},"trigger",{"type":254,"tag":622,"props":22513,"children":22514},{"style":629},[22515],{"type":259,"value":16416},{"type":254,"tag":622,"props":22517,"children":22518},{"class":624,"line":635},[22519,22524],{"type":254,"tag":622,"props":22520,"children":22521},{"style":629},[22522],{"type":259,"value":22523},"  -",{"type":254,"tag":622,"props":22525,"children":22526},{"style":1202},[22527],{"type":259,"value":22528}," main\n",{"type":254,"tag":622,"props":22530,"children":22531},{"class":624,"line":679},[22532],{"type":254,"tag":622,"props":22533,"children":22534},{"emptyLinePlaceholder":1699},[22535],{"type":259,"value":1702},{"type":254,"tag":622,"props":22537,"children":22538},{"class":624,"line":705},[22539,22544],{"type":254,"tag":622,"props":22540,"children":22541},{"style":714},[22542],{"type":259,"value":22543},"pool",{"type":254,"tag":622,"props":22545,"children":22546},{"style":629},[22547],{"type":259,"value":16416},{"type":254,"tag":622,"props":22549,"children":22550},{"class":624,"line":732},[22551,22556,22560,22565,22569],{"type":254,"tag":622,"props":22552,"children":22553},{"style":714},[22554],{"type":259,"value":22555},"  vmImage",{"type":254,"tag":622,"props":22557,"children":22558},{"style":629},[22559],{"type":259,"value":474},{"type":254,"tag":622,"props":22561,"children":22562},{"style":1202},[22563],{"type":259,"value":22564}," ubuntu",{"type":254,"tag":622,"props":22566,"children":22567},{"style":629},[22568],{"type":259,"value":17065},{"type":254,"tag":622,"props":22570,"children":22571},{"style":1202},[22572],{"type":259,"value":22573},"latest\n",{"type":254,"tag":622,"props":22575,"children":22576},{"class":624,"line":759},[22577],{"type":254,"tag":622,"props":22578,"children":22579},{"emptyLinePlaceholder":1699},[22580],{"type":259,"value":1702},{"type":254,"tag":622,"props":22582,"children":22583},{"class":624,"line":798},[22584,22589],{"type":254,"tag":622,"props":22585,"children":22586},{"style":714},[22587],{"type":259,"value":22588},"steps",{"type":254,"tag":622,"props":22590,"children":22591},{"style":629},[22592],{"type":259,"value":16416},{"type":254,"tag":622,"props":22594,"children":22595},{"class":624,"line":836},[22596,22600,22605,22609,22614],{"type":254,"tag":622,"props":22597,"children":22598},{"style":629},[22599],{"type":259,"value":22523},{"type":254,"tag":622,"props":22601,"children":22602},{"style":714},[22603],{"type":259,"value":22604}," task",{"type":254,"tag":622,"props":22606,"children":22607},{"style":629},[22608],{"type":259,"value":474},{"type":254,"tag":622,"props":22610,"children":22611},{"style":1202},[22612],{"type":259,"value":22613}," AzureCLI@",{"type":254,"tag":622,"props":22615,"children":22616},{"style":741},[22617],{"type":259,"value":22618},"2\n",{"type":254,"tag":622,"props":22620,"children":22621},{"class":624,"line":862},[22622,22627],{"type":254,"tag":622,"props":22623,"children":22624},{"style":714},[22625],{"type":259,"value":22626},"    inputs",{"type":254,"tag":622,"props":22628,"children":22629},{"style":629},[22630],{"type":259,"value":16416},{"type":254,"tag":622,"props":22632,"children":22633},{"class":624,"line":900},[22634,22639,22643,22647,22651],{"type":254,"tag":622,"props":22635,"children":22636},{"style":714},[22637],{"type":259,"value":22638},"      azureSubscription",{"type":254,"tag":622,"props":22640,"children":22641},{"style":629},[22642],{"type":259,"value":474},{"type":254,"tag":622,"props":22644,"children":22645},{"style":629},[22646],{"type":259,"value":16556},{"type":254,"tag":622,"props":22648,"children":22649},{"style":664},[22650],{"type":259,"value":21029},{"type":254,"tag":622,"props":22652,"children":22653},{"style":629},[22654],{"type":259,"value":16566},{"type":254,"tag":622,"props":22656,"children":22657},{"class":624,"line":938},[22658,22663,22667,22671,22676],{"type":254,"tag":622,"props":22659,"children":22660},{"style":714},[22661],{"type":259,"value":22662},"      scriptType",{"type":254,"tag":622,"props":22664,"children":22665},{"style":629},[22666],{"type":259,"value":474},{"type":254,"tag":622,"props":22668,"children":22669},{"style":629},[22670],{"type":259,"value":16556},{"type":254,"tag":622,"props":22672,"children":22673},{"style":664},[22674],{"type":259,"value":22675},"pscore",{"type":254,"tag":622,"props":22677,"children":22678},{"style":629},[22679],{"type":259,"value":16566},{"type":254,"tag":622,"props":22681,"children":22682},{"class":624,"line":976},[22683,22688,22692,22696,22701],{"type":254,"tag":622,"props":22684,"children":22685},{"style":714},[22686],{"type":259,"value":22687},"      scriptLocation",{"type":254,"tag":622,"props":22689,"children":22690},{"style":629},[22691],{"type":259,"value":474},{"type":254,"tag":622,"props":22693,"children":22694},{"style":629},[22695],{"type":259,"value":16556},{"type":254,"tag":622,"props":22697,"children":22698},{"style":664},[22699],{"type":259,"value":22700},"inlineScript",{"type":254,"tag":622,"props":22702,"children":22703},{"style":629},[22704],{"type":259,"value":16566},{"type":254,"tag":622,"props":22706,"children":22707},{"class":624,"line":1014},[22708,22713,22717,22721,22726],{"type":254,"tag":622,"props":22709,"children":22710},{"style":714},[22711],{"type":259,"value":22712},"      inlineScript",{"type":254,"tag":622,"props":22714,"children":22715},{"style":629},[22716],{"type":259,"value":474},{"type":254,"tag":622,"props":22718,"children":22719},{"style":629},[22720],{"type":259,"value":16556},{"type":254,"tag":622,"props":22722,"children":22723},{"style":664},[22724],{"type":259,"value":22725},"az account show --query id -o tsv",{"type":254,"tag":622,"props":22727,"children":22728},{"style":629},[22729],{"type":259,"value":16566},{"type":254,"tag":255,"props":22731,"children":22732},{},[22733],{"type":259,"value":22734},"We can add this file in the Git repository:",{"type":254,"tag":612,"props":22736,"children":22738},{"className":1423,"code":22737,"language":1425,"meta":205,"style":205},"var pipelineFile = new GitRepositoryFile(\"AzurePipeline\", new()\n{\n    File = \"azure-pipelines.yaml\",\n    RepositoryId = repository.Apply(r => r.Id),\n    CommitMessage = \"Add preconfigured pipeline file\",\n    Content = File.ReadAllText(\"azure-pipelines.yml\"),\n    Branch = \"refs/heads/main\"\n});\n",[22739],{"type":254,"tag":389,"props":22740,"children":22741},{"__ignoreMap":205},[22742,22792,22799,22828,22882,22911,22958,22982],{"type":254,"tag":622,"props":22743,"children":22744},{"class":624,"line":625},[22745,22749,22754,22758,22762,22767,22771,22775,22780,22784,22788],{"type":254,"tag":622,"props":22746,"children":22747},{"style":714},[22748],{"type":259,"value":1710},{"type":254,"tag":622,"props":22750,"children":22751},{"style":714},[22752],{"type":259,"value":22753}," pipelineFile",{"type":254,"tag":622,"props":22755,"children":22756},{"style":629},[22757],{"type":259,"value":1619},{"type":254,"tag":622,"props":22759,"children":22760},{"style":629},[22761],{"type":259,"value":1624},{"type":254,"tag":622,"props":22763,"children":22764},{"style":714},[22765],{"type":259,"value":22766}," GitRepositoryFile",{"type":254,"tag":622,"props":22768,"children":22769},{"style":629},[22770],{"type":259,"value":1668},{"type":254,"tag":622,"props":22772,"children":22773},{"style":629},[22774],{"type":259,"value":652},{"type":254,"tag":622,"props":22776,"children":22777},{"style":664},[22778],{"type":259,"value":22779},"AzurePipeline",{"type":254,"tag":622,"props":22781,"children":22782},{"style":629},[22783],{"type":259,"value":652},{"type":254,"tag":622,"props":22785,"children":22786},{"style":629},[22787],{"type":259,"value":3149},{"type":254,"tag":622,"props":22789,"children":22790},{"style":629},[22791],{"type":259,"value":20166},{"type":254,"tag":622,"props":22793,"children":22794},{"class":624,"line":635},[22795],{"type":254,"tag":622,"props":22796,"children":22797},{"style":629},[22798],{"type":259,"value":632},{"type":254,"tag":622,"props":22800,"children":22801},{"class":624,"line":679},[22802,22807,22811,22815,22820,22824],{"type":254,"tag":622,"props":22803,"children":22804},{"style":1202},[22805],{"type":259,"value":22806},"    File ",{"type":254,"tag":622,"props":22808,"children":22809},{"style":629},[22810],{"type":259,"value":1654},{"type":254,"tag":622,"props":22812,"children":22813},{"style":629},[22814],{"type":259,"value":661},{"type":254,"tag":622,"props":22816,"children":22817},{"style":664},[22818],{"type":259,"value":22819},"azure-pipelines.yaml",{"type":254,"tag":622,"props":22821,"children":22822},{"style":629},[22823],{"type":259,"value":652},{"type":254,"tag":622,"props":22825,"children":22826},{"style":629},[22827],{"type":259,"value":676},{"type":254,"tag":622,"props":22829,"children":22830},{"class":624,"line":705},[22831,22836,22840,22844,22848,22852,22856,22861,22865,22870,22874,22878],{"type":254,"tag":622,"props":22832,"children":22833},{"style":1202},[22834],{"type":259,"value":22835},"    RepositoryId ",{"type":254,"tag":622,"props":22837,"children":22838},{"style":629},[22839],{"type":259,"value":1654},{"type":254,"tag":622,"props":22841,"children":22842},{"style":1202},[22843],{"type":259,"value":20498},{"type":254,"tag":622,"props":22845,"children":22846},{"style":629},[22847],{"type":259,"value":594},{"type":254,"tag":622,"props":22849,"children":22850},{"style":1735},[22851],{"type":259,"value":21286},{"type":254,"tag":622,"props":22853,"children":22854},{"style":629},[22855],{"type":259,"value":1668},{"type":254,"tag":622,"props":22857,"children":22858},{"style":714},[22859],{"type":259,"value":22860},"r",{"type":254,"tag":622,"props":22862,"children":22863},{"style":629},[22864],{"type":259,"value":2697},{"type":254,"tag":622,"props":22866,"children":22867},{"style":1202},[22868],{"type":259,"value":22869}," r",{"type":254,"tag":622,"props":22871,"children":22872},{"style":629},[22873],{"type":259,"value":594},{"type":254,"tag":622,"props":22875,"children":22876},{"style":1202},[22877],{"type":259,"value":20553},{"type":254,"tag":622,"props":22879,"children":22880},{"style":629},[22881],{"type":259,"value":12556},{"type":254,"tag":622,"props":22883,"children":22884},{"class":624,"line":732},[22885,22890,22894,22898,22903,22907],{"type":254,"tag":622,"props":22886,"children":22887},{"style":1202},[22888],{"type":259,"value":22889},"    CommitMessage ",{"type":254,"tag":622,"props":22891,"children":22892},{"style":629},[22893],{"type":259,"value":1654},{"type":254,"tag":622,"props":22895,"children":22896},{"style":629},[22897],{"type":259,"value":661},{"type":254,"tag":622,"props":22899,"children":22900},{"style":664},[22901],{"type":259,"value":22902},"Add preconfigured pipeline file",{"type":254,"tag":622,"props":22904,"children":22905},{"style":629},[22906],{"type":259,"value":652},{"type":254,"tag":622,"props":22908,"children":22909},{"style":629},[22910],{"type":259,"value":676},{"type":254,"tag":622,"props":22912,"children":22913},{"class":624,"line":759},[22914,22919,22923,22928,22932,22937,22941,22945,22950,22954],{"type":254,"tag":622,"props":22915,"children":22916},{"style":1202},[22917],{"type":259,"value":22918},"    Content ",{"type":254,"tag":622,"props":22920,"children":22921},{"style":629},[22922],{"type":259,"value":1654},{"type":254,"tag":622,"props":22924,"children":22925},{"style":1202},[22926],{"type":259,"value":22927}," File",{"type":254,"tag":622,"props":22929,"children":22930},{"style":629},[22931],{"type":259,"value":594},{"type":254,"tag":622,"props":22933,"children":22934},{"style":1735},[22935],{"type":259,"value":22936},"ReadAllText",{"type":254,"tag":622,"props":22938,"children":22939},{"style":629},[22940],{"type":259,"value":1668},{"type":254,"tag":622,"props":22942,"children":22943},{"style":629},[22944],{"type":259,"value":652},{"type":254,"tag":622,"props":22946,"children":22947},{"style":664},[22948],{"type":259,"value":22949},"azure-pipelines.yml",{"type":254,"tag":622,"props":22951,"children":22952},{"style":629},[22953],{"type":259,"value":652},{"type":254,"tag":622,"props":22955,"children":22956},{"style":629},[22957],{"type":259,"value":12556},{"type":254,"tag":622,"props":22959,"children":22960},{"class":624,"line":798},[22961,22966,22970,22974,22978],{"type":254,"tag":622,"props":22962,"children":22963},{"style":1202},[22964],{"type":259,"value":22965},"    Branch ",{"type":254,"tag":622,"props":22967,"children":22968},{"style":629},[22969],{"type":259,"value":1654},{"type":254,"tag":622,"props":22971,"children":22972},{"style":629},[22973],{"type":259,"value":661},{"type":254,"tag":622,"props":22975,"children":22976},{"style":664},[22977],{"type":259,"value":20848},{"type":254,"tag":622,"props":22979,"children":22980},{"style":629},[22981],{"type":259,"value":9909},{"type":254,"tag":622,"props":22983,"children":22984},{"class":624,"line":836},[22985],{"type":254,"tag":622,"props":22986,"children":22987},{"style":629},[22988],{"type":259,"value":6661},{"type":254,"tag":255,"props":22990,"children":22991},{},[22992],{"type":259,"value":22993},"Now, we have to create the pipeline itself:",{"type":254,"tag":612,"props":22995,"children":22997},{"className":1423,"code":22996,"language":1425,"meta":205,"style":205},"var pipeline = new BuildDefinition(\"deployToAzure\", new()\n{\n    ProjectId = project.Id,\n    Repository = new BuildDefinitionRepositoryArgs()\n    {\n        RepoId = repository.Apply(r => r.Id),\n        BranchName = \"refs/heads/main\",\n        YmlPath = pipelineFile.File,\n        RepoType = \"TfsGit\"\n    }\n});\n",[22998],{"type":254,"tag":389,"props":22999,"children":23000},{"__ignoreMap":205},[23001,23051,23058,23085,23110,23117,23169,23197,23226,23251,23258],{"type":254,"tag":622,"props":23002,"children":23003},{"class":624,"line":625},[23004,23008,23013,23017,23021,23026,23030,23034,23039,23043,23047],{"type":254,"tag":622,"props":23005,"children":23006},{"style":714},[23007],{"type":259,"value":1710},{"type":254,"tag":622,"props":23009,"children":23010},{"style":714},[23011],{"type":259,"value":23012}," pipeline",{"type":254,"tag":622,"props":23014,"children":23015},{"style":629},[23016],{"type":259,"value":1619},{"type":254,"tag":622,"props":23018,"children":23019},{"style":629},[23020],{"type":259,"value":1624},{"type":254,"tag":622,"props":23022,"children":23023},{"style":714},[23024],{"type":259,"value":23025}," BuildDefinition",{"type":254,"tag":622,"props":23027,"children":23028},{"style":629},[23029],{"type":259,"value":1668},{"type":254,"tag":622,"props":23031,"children":23032},{"style":629},[23033],{"type":259,"value":652},{"type":254,"tag":622,"props":23035,"children":23036},{"style":664},[23037],{"type":259,"value":23038},"deployToAzure",{"type":254,"tag":622,"props":23040,"children":23041},{"style":629},[23042],{"type":259,"value":652},{"type":254,"tag":622,"props":23044,"children":23045},{"style":629},[23046],{"type":259,"value":3149},{"type":254,"tag":622,"props":23048,"children":23049},{"style":629},[23050],{"type":259,"value":20166},{"type":254,"tag":622,"props":23052,"children":23053},{"class":624,"line":635},[23054],{"type":254,"tag":622,"props":23055,"children":23056},{"style":629},[23057],{"type":259,"value":632},{"type":254,"tag":622,"props":23059,"children":23060},{"class":624,"line":679},[23061,23065,23069,23073,23077,23081],{"type":254,"tag":622,"props":23062,"children":23063},{"style":1202},[23064],{"type":259,"value":20536},{"type":254,"tag":622,"props":23066,"children":23067},{"style":629},[23068],{"type":259,"value":1654},{"type":254,"tag":622,"props":23070,"children":23071},{"style":1202},[23072],{"type":259,"value":20127},{"type":254,"tag":622,"props":23074,"children":23075},{"style":629},[23076],{"type":259,"value":594},{"type":254,"tag":622,"props":23078,"children":23079},{"style":1202},[23080],{"type":259,"value":20553},{"type":254,"tag":622,"props":23082,"children":23083},{"style":629},[23084],{"type":259,"value":676},{"type":254,"tag":622,"props":23086,"children":23087},{"class":624,"line":705},[23088,23093,23097,23101,23106],{"type":254,"tag":622,"props":23089,"children":23090},{"style":1202},[23091],{"type":259,"value":23092},"    Repository ",{"type":254,"tag":622,"props":23094,"children":23095},{"style":629},[23096],{"type":259,"value":1654},{"type":254,"tag":622,"props":23098,"children":23099},{"style":629},[23100],{"type":259,"value":1624},{"type":254,"tag":622,"props":23102,"children":23103},{"style":714},[23104],{"type":259,"value":23105}," BuildDefinitionRepositoryArgs",{"type":254,"tag":622,"props":23107,"children":23108},{"style":629},[23109],{"type":259,"value":1634},{"type":254,"tag":622,"props":23111,"children":23112},{"class":624,"line":732},[23113],{"type":254,"tag":622,"props":23114,"children":23115},{"style":629},[23116],{"type":259,"value":3574},{"type":254,"tag":622,"props":23118,"children":23119},{"class":624,"line":759},[23120,23125,23129,23133,23137,23141,23145,23149,23153,23157,23161,23165],{"type":254,"tag":622,"props":23121,"children":23122},{"style":1202},[23123],{"type":259,"value":23124},"        RepoId ",{"type":254,"tag":622,"props":23126,"children":23127},{"style":629},[23128],{"type":259,"value":1654},{"type":254,"tag":622,"props":23130,"children":23131},{"style":1202},[23132],{"type":259,"value":20498},{"type":254,"tag":622,"props":23134,"children":23135},{"style":629},[23136],{"type":259,"value":594},{"type":254,"tag":622,"props":23138,"children":23139},{"style":1735},[23140],{"type":259,"value":21286},{"type":254,"tag":622,"props":23142,"children":23143},{"style":629},[23144],{"type":259,"value":1668},{"type":254,"tag":622,"props":23146,"children":23147},{"style":714},[23148],{"type":259,"value":22860},{"type":254,"tag":622,"props":23150,"children":23151},{"style":629},[23152],{"type":259,"value":2697},{"type":254,"tag":622,"props":23154,"children":23155},{"style":1202},[23156],{"type":259,"value":22869},{"type":254,"tag":622,"props":23158,"children":23159},{"style":629},[23160],{"type":259,"value":594},{"type":254,"tag":622,"props":23162,"children":23163},{"style":1202},[23164],{"type":259,"value":20553},{"type":254,"tag":622,"props":23166,"children":23167},{"style":629},[23168],{"type":259,"value":12556},{"type":254,"tag":622,"props":23170,"children":23171},{"class":624,"line":798},[23172,23177,23181,23185,23189,23193],{"type":254,"tag":622,"props":23173,"children":23174},{"style":1202},[23175],{"type":259,"value":23176},"        BranchName ",{"type":254,"tag":622,"props":23178,"children":23179},{"style":629},[23180],{"type":259,"value":1654},{"type":254,"tag":622,"props":23182,"children":23183},{"style":629},[23184],{"type":259,"value":661},{"type":254,"tag":622,"props":23186,"children":23187},{"style":664},[23188],{"type":259,"value":20848},{"type":254,"tag":622,"props":23190,"children":23191},{"style":629},[23192],{"type":259,"value":652},{"type":254,"tag":622,"props":23194,"children":23195},{"style":629},[23196],{"type":259,"value":676},{"type":254,"tag":622,"props":23198,"children":23199},{"class":624,"line":836},[23200,23205,23209,23213,23217,23222],{"type":254,"tag":622,"props":23201,"children":23202},{"style":1202},[23203],{"type":259,"value":23204},"        YmlPath ",{"type":254,"tag":622,"props":23206,"children":23207},{"style":629},[23208],{"type":259,"value":1654},{"type":254,"tag":622,"props":23210,"children":23211},{"style":1202},[23212],{"type":259,"value":22753},{"type":254,"tag":622,"props":23214,"children":23215},{"style":629},[23216],{"type":259,"value":594},{"type":254,"tag":622,"props":23218,"children":23219},{"style":1202},[23220],{"type":259,"value":23221},"File",{"type":254,"tag":622,"props":23223,"children":23224},{"style":629},[23225],{"type":259,"value":676},{"type":254,"tag":622,"props":23227,"children":23228},{"class":624,"line":862},[23229,23234,23238,23242,23247],{"type":254,"tag":622,"props":23230,"children":23231},{"style":1202},[23232],{"type":259,"value":23233},"        RepoType ",{"type":254,"tag":622,"props":23235,"children":23236},{"style":629},[23237],{"type":259,"value":1654},{"type":254,"tag":622,"props":23239,"children":23240},{"style":629},[23241],{"type":259,"value":661},{"type":254,"tag":622,"props":23243,"children":23244},{"style":664},[23245],{"type":259,"value":23246},"TfsGit",{"type":254,"tag":622,"props":23248,"children":23249},{"style":629},[23250],{"type":259,"value":9909},{"type":254,"tag":622,"props":23252,"children":23253},{"class":624,"line":900},[23254],{"type":254,"tag":622,"props":23255,"children":23256},{"style":629},[23257],{"type":259,"value":1093},{"type":254,"tag":622,"props":23259,"children":23260},{"class":624,"line":938},[23261],{"type":254,"tag":622,"props":23262,"children":23263},{"style":629},[23264],{"type":259,"value":6661},{"type":254,"tag":255,"props":23266,"children":23267},{},[23268],{"type":259,"value":23269},"To complete the automation process, we can authorize the pipeline to utilize the service connection, eliminating the need for manual intervention through the portal:",{"type":254,"tag":612,"props":23271,"children":23273},{"className":1423,"code":23272,"language":1425,"meta":205,"style":205},"new PipelineAuthorization(\"azureOidcPipelineAuthorization\", new()\n{\n    ProjectId = project.Id,\n    Type = \"endpoint\",\n    PipelineId = pipeline.Id.Apply(int.Parse),\n    ResourceId = serviceConnection.Id\n});\n",[23274],{"type":254,"tag":389,"props":23275,"children":23276},{"__ignoreMap":205},[23277,23314,23321,23348,23377,23423,23448],{"type":254,"tag":622,"props":23278,"children":23279},{"class":624,"line":625},[23280,23284,23289,23293,23297,23302,23306,23310],{"type":254,"tag":622,"props":23281,"children":23282},{"style":644},[23283],{"type":259,"value":21937},{"type":254,"tag":622,"props":23285,"children":23286},{"style":1735},[23287],{"type":259,"value":23288}," PipelineAuthorization",{"type":254,"tag":622,"props":23290,"children":23291},{"style":629},[23292],{"type":259,"value":1668},{"type":254,"tag":622,"props":23294,"children":23295},{"style":629},[23296],{"type":259,"value":652},{"type":254,"tag":622,"props":23298,"children":23299},{"style":664},[23300],{"type":259,"value":23301},"azureOidcPipelineAuthorization",{"type":254,"tag":622,"props":23303,"children":23304},{"style":629},[23305],{"type":259,"value":652},{"type":254,"tag":622,"props":23307,"children":23308},{"style":629},[23309],{"type":259,"value":3149},{"type":254,"tag":622,"props":23311,"children":23312},{"style":629},[23313],{"type":259,"value":20166},{"type":254,"tag":622,"props":23315,"children":23316},{"class":624,"line":635},[23317],{"type":254,"tag":622,"props":23318,"children":23319},{"style":629},[23320],{"type":259,"value":632},{"type":254,"tag":622,"props":23322,"children":23323},{"class":624,"line":679},[23324,23328,23332,23336,23340,23344],{"type":254,"tag":622,"props":23325,"children":23326},{"style":1202},[23327],{"type":259,"value":20536},{"type":254,"tag":622,"props":23329,"children":23330},{"style":629},[23331],{"type":259,"value":1654},{"type":254,"tag":622,"props":23333,"children":23334},{"style":1202},[23335],{"type":259,"value":20127},{"type":254,"tag":622,"props":23337,"children":23338},{"style":629},[23339],{"type":259,"value":594},{"type":254,"tag":622,"props":23341,"children":23342},{"style":1202},[23343],{"type":259,"value":20553},{"type":254,"tag":622,"props":23345,"children":23346},{"style":629},[23347],{"type":259,"value":676},{"type":254,"tag":622,"props":23349,"children":23350},{"class":624,"line":705},[23351,23356,23360,23364,23369,23373],{"type":254,"tag":622,"props":23352,"children":23353},{"style":1202},[23354],{"type":259,"value":23355},"    Type ",{"type":254,"tag":622,"props":23357,"children":23358},{"style":629},[23359],{"type":259,"value":1654},{"type":254,"tag":622,"props":23361,"children":23362},{"style":629},[23363],{"type":259,"value":661},{"type":254,"tag":622,"props":23365,"children":23366},{"style":664},[23367],{"type":259,"value":23368},"endpoint",{"type":254,"tag":622,"props":23370,"children":23371},{"style":629},[23372],{"type":259,"value":652},{"type":254,"tag":622,"props":23374,"children":23375},{"style":629},[23376],{"type":259,"value":676},{"type":254,"tag":622,"props":23378,"children":23379},{"class":624,"line":732},[23380,23385,23389,23393,23397,23401,23405,23409,23414,23419],{"type":254,"tag":622,"props":23381,"children":23382},{"style":1202},[23383],{"type":259,"value":23384},"    PipelineId ",{"type":254,"tag":622,"props":23386,"children":23387},{"style":629},[23388],{"type":259,"value":1654},{"type":254,"tag":622,"props":23390,"children":23391},{"style":1202},[23392],{"type":259,"value":23012},{"type":254,"tag":622,"props":23394,"children":23395},{"style":629},[23396],{"type":259,"value":594},{"type":254,"tag":622,"props":23398,"children":23399},{"style":1202},[23400],{"type":259,"value":20553},{"type":254,"tag":622,"props":23402,"children":23403},{"style":629},[23404],{"type":259,"value":594},{"type":254,"tag":622,"props":23406,"children":23407},{"style":1735},[23408],{"type":259,"value":21286},{"type":254,"tag":622,"props":23410,"children":23411},{"style":629},[23412],{"type":259,"value":23413},"(int.",{"type":254,"tag":622,"props":23415,"children":23416},{"style":1202},[23417],{"type":259,"value":23418},"Parse",{"type":254,"tag":622,"props":23420,"children":23421},{"style":629},[23422],{"type":259,"value":12556},{"type":254,"tag":622,"props":23424,"children":23425},{"class":624,"line":759},[23426,23431,23435,23439,23443],{"type":254,"tag":622,"props":23427,"children":23428},{"style":1202},[23429],{"type":259,"value":23430},"    ResourceId ",{"type":254,"tag":622,"props":23432,"children":23433},{"style":629},[23434],{"type":259,"value":1654},{"type":254,"tag":622,"props":23436,"children":23437},{"style":1202},[23438],{"type":259,"value":20936},{"type":254,"tag":622,"props":23440,"children":23441},{"style":629},[23442],{"type":259,"value":594},{"type":254,"tag":622,"props":23444,"children":23445},{"style":1202},[23446],{"type":259,"value":23447},"Id\n",{"type":254,"tag":622,"props":23449,"children":23450},{"class":624,"line":798},[23451],{"type":254,"tag":622,"props":23452,"children":23453},{"style":629},[23454],{"type":259,"value":6661},{"type":254,"tag":255,"props":23456,"children":23457},{},[23458],{"type":259,"value":23459},"The last thing we can do is create a stack output to expose the URL of the created pipeline:",{"type":254,"tag":612,"props":23461,"children":23463},{"className":1423,"code":23462,"language":1425,"meta":205,"style":205},"return new Dictionary\u003Cstring, object?>\n{\n    [\"pipelineUrl\"] = Output.Format($\"{organizationUrl}{project.Name}/_build?definitionId={pipeline.Id}\")\n};\n",[23464],{"type":254,"tag":389,"props":23465,"children":23466},{"__ignoreMap":205},[23467,23501,23508,23612],{"type":254,"tag":622,"props":23468,"children":23469},{"class":624,"line":625},[23470,23475,23479,23484,23488,23492,23496],{"type":254,"tag":622,"props":23471,"children":23472},{"style":1196},[23473],{"type":259,"value":23474},"return",{"type":254,"tag":622,"props":23476,"children":23477},{"style":629},[23478],{"type":259,"value":1624},{"type":254,"tag":622,"props":23480,"children":23481},{"style":714},[23482],{"type":259,"value":23483}," Dictionary",{"type":254,"tag":622,"props":23485,"children":23486},{"style":629},[23487],{"type":259,"value":1743},{"type":254,"tag":622,"props":23489,"children":23490},{"style":629},[23491],{"type":259,"value":2851},{"type":254,"tag":622,"props":23493,"children":23494},{"style":629},[23495],{"type":259,"value":3149},{"type":254,"tag":622,"props":23497,"children":23498},{"style":629},[23499],{"type":259,"value":23500}," object?>\n",{"type":254,"tag":622,"props":23502,"children":23503},{"class":624,"line":635},[23504],{"type":254,"tag":622,"props":23505,"children":23506},{"style":629},[23507],{"type":259,"value":632},{"type":254,"tag":622,"props":23509,"children":23510},{"class":624,"line":679},[23511,23515,23519,23524,23528,23532,23536,23540,23544,23548,23552,23557,23562,23566,23570,23574,23578,23582,23587,23591,23596,23600,23604,23608],{"type":254,"tag":622,"props":23512,"children":23513},{"style":629},[23514],{"type":259,"value":9494},{"type":254,"tag":622,"props":23516,"children":23517},{"style":629},[23518],{"type":259,"value":652},{"type":254,"tag":622,"props":23520,"children":23521},{"style":664},[23522],{"type":259,"value":23523},"pipelineUrl",{"type":254,"tag":622,"props":23525,"children":23526},{"style":629},[23527],{"type":259,"value":652},{"type":254,"tag":622,"props":23529,"children":23530},{"style":629},[23531],{"type":259,"value":20251},{"type":254,"tag":622,"props":23533,"children":23534},{"style":629},[23535],{"type":259,"value":1619},{"type":254,"tag":622,"props":23537,"children":23538},{"style":1202},[23539],{"type":259,"value":22079},{"type":254,"tag":622,"props":23541,"children":23542},{"style":629},[23543],{"type":259,"value":594},{"type":254,"tag":622,"props":23545,"children":23546},{"style":1735},[23547],{"type":259,"value":22088},{"type":254,"tag":622,"props":23549,"children":23550},{"style":629},[23551],{"type":259,"value":1668},{"type":254,"tag":622,"props":23553,"children":23554},{"style":629},[23555],{"type":259,"value":23556},"$\"{",{"type":254,"tag":622,"props":23558,"children":23559},{"style":1202},[23560],{"type":259,"value":23561},"organizationUrl",{"type":254,"tag":622,"props":23563,"children":23564},{"style":629},[23565],{"type":259,"value":3252},{"type":254,"tag":622,"props":23567,"children":23568},{"style":1202},[23569],{"type":259,"value":22410},{"type":254,"tag":622,"props":23571,"children":23572},{"style":629},[23573],{"type":259,"value":594},{"type":254,"tag":622,"props":23575,"children":23576},{"style":1202},[23577],{"type":259,"value":2254},{"type":254,"tag":622,"props":23579,"children":23580},{"style":629},[23581],{"type":259,"value":2421},{"type":254,"tag":622,"props":23583,"children":23584},{"style":664},[23585],{"type":259,"value":23586},"/_build?definitionId=",{"type":254,"tag":622,"props":23588,"children":23589},{"style":629},[23590],{"type":259,"value":3234},{"type":254,"tag":622,"props":23592,"children":23593},{"style":1202},[23594],{"type":259,"value":23595},"pipeline",{"type":254,"tag":622,"props":23597,"children":23598},{"style":629},[23599],{"type":259,"value":594},{"type":254,"tag":622,"props":23601,"children":23602},{"style":1202},[23603],{"type":259,"value":20553},{"type":254,"tag":622,"props":23605,"children":23606},{"style":629},[23607],{"type":259,"value":2443},{"type":254,"tag":622,"props":23609,"children":23610},{"style":629},[23611],{"type":259,"value":1685},{"type":254,"tag":622,"props":23613,"children":23614},{"class":624,"line":705},[23615],{"type":254,"tag":622,"props":23616,"children":23617},{"style":629},[23618],{"type":259,"value":1693},{"type":254,"tag":255,"props":23620,"children":23621},{},[23622,23624,23630],{"type":259,"value":23623},"Now we can execute the ",{"type":254,"tag":389,"props":23625,"children":23627},{"className":23626},[],[23628],{"type":259,"value":23629},"pulumi up",{"type":259,"value":23631}," command to provision all these resources and then open the pipeline page in our browser to test the pipeline.",{"type":254,"tag":427,"props":23633,"children":23634},{"icon":567},[23635],{"type":254,"tag":255,"props":23636,"children":23637},{},[23638,23640,23646,23648,23655,23657],{"type":259,"value":23639},"On Windows, you can use the ",{"type":254,"tag":389,"props":23641,"children":23643},{"className":23642},[],[23644],{"type":259,"value":23645},"start $(pulumi stack output pipelineUrl)",{"type":259,"value":23647}," command to directly open the browser on the pipeline page. If you are using ",{"type":254,"tag":262,"props":23649,"children":23652},{"href":23650,"rel":23651},"https://www.nushell.sh/",[266],[23653],{"type":259,"value":23654},"Nushell",{"type":259,"value":23656}," the command will be ",{"type":254,"tag":389,"props":23658,"children":23660},{"className":23659},[],[23661],{"type":259,"value":23662},"pulumi stack output pipelineUrl | start $in",{"type":254,"tag":255,"props":23664,"children":23665},{},[23666],{"type":254,"tag":1161,"props":23667,"children":23671},{"alt":23668,"className":23669,"src":23670},"Results of the pipeline run in Azure DevOps",[1165,1166],"/posts/images/azuredevopsoidc_portal.webp",[],{"type":254,"tag":255,"props":23673,"children":23674},{},[23675],{"type":259,"value":23676},"Everything is working as expected.",{"type":254,"tag":282,"props":23678,"children":23679},{"id":10572},[23680],{"type":259,"value":10575},{"type":254,"tag":255,"props":23682,"children":23683},{},[23684,23686,23692],{"type":259,"value":23685},"In this article, we demonstrated how to automate the configuration of an Azure DevOps project using Workload Identity Federation for secure deployments to Azure. We covered the provisioning of the Microsoft Entra ID and Azure DevOps resources necessary to make this work. It's very similar to ",{"type":254,"tag":262,"props":23687,"children":23689},{"href":16753,"rel":23688},[266],[23690],{"type":259,"value":23691},"what can be done for GitHub",{"type":259,"value":23693}," but with the specificities of Azure DevOps.",{"type":254,"tag":255,"props":23695,"children":23696},{},[23697],{"type":259,"value":23698},"It was an opportunity for me to work with the Azure DevOps provider. Even if it does the job, I must admit I was somewhat disappointed with the developer experience which I found to be not very intuitive, with poorly named resources and an overreliance on strings as parameters. I assume that the Azure DevOps APIs are primarily responsible for this, as they are what the provider calls upon.",{"type":254,"tag":255,"props":23700,"children":23701},{},[23702],{"type":259,"value":23703},"One thing I find interesting with Azure DevOps is that YAML pipelines do not need to be updated to take advantage of workload identity federation as long as the Azure Pipelines tasks you are using support it and your ARM service connection has been converted to workload identity federation.",{"type":254,"tag":255,"props":23705,"children":23706},{},[23707],{"type":259,"value":23708},"Anyway, regardless of the CI/CD platform you are using, I believe that employing Workload Identity Federation to deploy code to Azure from pipelines is the right approach.",{"type":254,"tag":255,"props":23710,"children":23711},{},[23712,23714,23724],{"type":259,"value":23713},"You can find the complete source code used for this article ",{"type":254,"tag":262,"props":23715,"children":23718},{"href":23716,"rel":23717},"https://github.com/TechWatching/AzureDevOpsWorkloadIdentity",[266],[23719],{"type":254,"tag":9718,"props":23720,"children":23721},{},[23722],{"type":259,"value":23723},"in this GitHub repository",{"type":259,"value":594},{"type":254,"tag":1260,"props":23726,"children":23727},{},[23728],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":23730},[23731,23732,23734,23741],{"id":19579,"depth":635,"text":19582},{"id":19664,"depth":635,"text":23733},"How can you use Workload Identity Federation to deploy to Azure from Azure Pipelines?",{"id":19739,"depth":635,"text":19742,"children":23735},[23736,23737,23738,23739,23740],{"id":19745,"depth":679,"text":19748},{"id":19983,"depth":679,"text":19986},{"id":20900,"depth":679,"text":20903},{"id":21617,"depth":679,"text":21620},{"id":22475,"depth":679,"text":22478},{"id":10572,"depth":635,"text":10575},"content:1.posts:54.ado-workload-identity-federation.md","1.posts/54.ado-workload-identity-federation.md",{"_path":166,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":165,"description":23745,"lead":23746,"date":23747,"image":23748,"badge":23750,"tags":23751,"body":23752,"_type":1275,"_id":31095,"_source":231,"_file":31096,"_extension":1278},"Creating an application and deploying it to Azure is not complicated. You write some code on your machine, do some clicks in the Azure portal, or run some Azure CLI commands from your terminal and that's it: your application is up and running in Azure.","Using Azure OpenID Connect with Pulumi in GitHub Actions","2023-07-20T00:00:00.000Z",{"src":23749},"/images/azureOIDC.webp",{"label":16349},[6260,16352,16354,16355,10225,19569,19570],{"type":251,"children":23753,"toc":31070},[23754,23758,23763,23775,23781,23791,23800,23806,23817,23851,23860,23866,23878,23916,23925,23944,23950,23955,23960,23968,23973,23981,23986,24007,24025,24045,24051,24059,24065,24070,24092,24097,24120,24125,24131,24137,24142,24197,24202,24252,24292,24296,24328,24341,24359,24363,24386,24410,24425,24430,24683,24704,24726,24758,24770,24775,25023,25039,25044,25329,25354,25359,25373,25382,25388,25393,25398,25697,25709,25810,25815,25904,25924,25987,25995,26001,26006,26011,26027,26032,26183,26188,26685,26702,26708,26713,26718,27030,27043,27641,27654,27679,27687,27715,27748,27841,27863,28063,28068,28456,28461,28493,28516,28522,28534,28543,28548,28557,28562,28600,28605,28610,28660,28672,28685,28702,28723,28732,28737,28741,28747,28752,28757,28763,28768,28786,28791,28797,28802,28888,28894,28899,28904,31050,31061,31066],{"type":254,"tag":255,"props":23755,"children":23756},{},[23757],{"type":259,"value":23745},{"type":254,"tag":255,"props":23759,"children":23760},{},[23761],{"type":259,"value":23762},"Yet, that's not real life, at least not what you will do when working on a professional project. Your code needs to be versioned and pushed to a location where your colleagues can work on it. The provisioning of Azure resources and deployment to Azure should be carried out using a properly configured CI/CD pipeline with the necessary authorization.",{"type":254,"tag":255,"props":23764,"children":23765},{},[23766,23768,23773],{"type":259,"value":23767},"That's a lot of work that would need to be done each time you start a new project. So let's automate that using Pulumi to simplify the process and create an \"",{"type":254,"tag":19837,"props":23769,"children":23770},{},[23771],{"type":259,"value":23772},"Azure-Ready GitHub repository",{"type":259,"value":23774},"\".",{"type":254,"tag":282,"props":23776,"children":23778},{"id":23777},"whats-an-azure-ready-github-repository",[23779],{"type":259,"value":23780},"What's an Azure-Ready GitHub repository?",{"type":254,"tag":255,"props":23782,"children":23783},{},[23784,23785,23789],{"type":259,"value":652},{"type":254,"tag":19837,"props":23786,"children":23787},{},[23788],{"type":259,"value":23772},{"type":259,"value":23790},"\" is not an official term or concept, it's just something I've come up with to describe a Github repository that has everything correctly configured to provision Azure resources or deploy applications to Azure from a GitHub Actions CI/CD pipeline.",{"type":254,"tag":255,"props":23792,"children":23793},{},[23794],{"type":254,"tag":1161,"props":23795,"children":23799},{"alt":23796,"className":23797,"src":23798},"Diagram of a GitHub repository interacting with Azure.",[1165,1166],"/posts/images/azurereadygithub_overview_1.webp",[],{"type":254,"tag":455,"props":23801,"children":23803},{"id":23802},"the-github-part",[23804],{"type":259,"value":23805},"The GitHub part",{"type":254,"tag":255,"props":23807,"children":23808},{},[23809,23811,23815],{"type":259,"value":23810},"On the GitHub side, to have an ",{"type":254,"tag":19837,"props":23812,"children":23813},{},[23814],{"type":259,"value":23772},{"type":259,"value":23816},", we need:",{"type":254,"tag":332,"props":23818,"children":23819},{},[23820,23833,23838],{"type":254,"tag":336,"props":23821,"children":23822},{},[23823,23825,23831],{"type":259,"value":23824},"the GitHub repository itself (already initialized with a ",{"type":254,"tag":389,"props":23826,"children":23828},{"className":23827},[],[23829],{"type":259,"value":23830},"main",{"type":259,"value":23832}," branch)",{"type":254,"tag":336,"props":23834,"children":23835},{},[23836],{"type":259,"value":23837},"the necessary GitHub Actions variables/secrets to authenticate to the correct Azure subscription",{"type":254,"tag":336,"props":23839,"children":23840},{},[23841,23843,23849],{"type":259,"value":23842},"a YAML file located in the ",{"type":254,"tag":389,"props":23844,"children":23846},{"className":23845},[],[23847],{"type":259,"value":23848},".github/workflows/",{"type":259,"value":23850}," folder that contains the CI/CD pipeline that provisions resources in Azure",{"type":254,"tag":255,"props":23852,"children":23853},{},[23854],{"type":254,"tag":1161,"props":23855,"children":23859},{"alt":23856,"className":23857,"src":23858},"A diagram of the GitHub repository to create.",[1165,1166],"/posts/images/azurereadygithub_github_1.webp",[],{"type":254,"tag":455,"props":23861,"children":23863},{"id":23862},"the-azure-part",[23864],{"type":259,"value":23865},"The Azure part",{"type":254,"tag":255,"props":23867,"children":23868},{},[23869,23871,23876],{"type":259,"value":23870},"On the Azure side, to have an ",{"type":254,"tag":19837,"props":23872,"children":23873},{},[23874],{"type":259,"value":23875},"Azure-Ready GitHub repository,",{"type":259,"value":23877}," we need:",{"type":254,"tag":332,"props":23879,"children":23880},{},[23881,23886],{"type":254,"tag":336,"props":23882,"children":23883},{},[23884],{"type":259,"value":23885},"the existing Azure subscription to which resources are deployed",{"type":254,"tag":336,"props":23887,"children":23888},{},[23889,23891,23896,23898],{"type":259,"value":23890},"an ",{"type":254,"tag":19837,"props":23892,"children":23893},{},[23894],{"type":259,"value":23895},"identity",{"type":259,"value":23897}," in the Azure Active Directory of the desired tenant so that the GitHub CI/CD pipeline can authenticate to Azure and interact with the subscription",{"type":254,"tag":332,"props":23899,"children":23900},{},[23901,23906,23911],{"type":254,"tag":336,"props":23902,"children":23903},{},[23904],{"type":259,"value":23905},"an Azure AD application that represents the GitHub Actions pipeline identity",{"type":254,"tag":336,"props":23907,"children":23908},{},[23909],{"type":259,"value":23910},"a Service Principal (related to the Azure AD application) that has the contributor role on the Azure subscription",{"type":254,"tag":336,"props":23912,"children":23913},{},[23914],{"type":259,"value":23915},"credentials for the CI/CD pipeline to authenticate to Azure on behalf of this Azure AD application",{"type":254,"tag":255,"props":23917,"children":23918},{},[23919],{"type":254,"tag":1161,"props":23920,"children":23924},{"alt":23921,"className":23922,"src":23923},"A diagram of the resources to configure in Azure.",[1165,1166],"/posts/images/azurereadygithub_azure_1.webp",[],{"type":254,"tag":427,"props":23926,"children":23927},{"icon":19646},[23928],{"type":254,"tag":255,"props":23929,"children":23930},{},[23931,23936,23938,23942],{"type":254,"tag":19837,"props":23932,"children":23933},{},[23934],{"type":259,"value":23935},"Azure Active Directory",{"type":259,"value":23937}," has recently been renamed ",{"type":254,"tag":19837,"props":23939,"children":23940},{},[23941],{"type":259,"value":16356},{"type":259,"value":23943}," (as of the time of writing). However, I will continue to use the term Azure Active Directory throughout the rest of the article. Please note that both terms refer to the same service.",{"type":254,"tag":455,"props":23945,"children":23947},{"id":23946},"the-problem-with-secret-credentials",[23948],{"type":259,"value":23949},"The problem with secret credentials",{"type":254,"tag":255,"props":23951,"children":23952},{},[23953],{"type":259,"value":23954},"People tend to use secret credentials to authenticate their pipeline to Azure and that's not the best thing to do.",{"type":254,"tag":255,"props":23956,"children":23957},{},[23958],{"type":259,"value":23959},"From a security standpoint, depending on secrets always poses a security risk. Even if in that case the secret would be safely stored in a GitHub secret and never exposed publicly, it's still better to avoid secrets when we can.",{"type":254,"tag":427,"props":23961,"children":23962},{"icon":20082},[23963],{"type":254,"tag":255,"props":23964,"children":23965},{},[23966],{"type":259,"value":23967},"That's precisely why when hosting applications in Azure, we use Managed Identities and IAM roles instead of relying on secrets. Yet, here we can't use Managed Identities for GitHub Actions pipelines.",{"type":254,"tag":255,"props":23969,"children":23970},{},[23971],{"type":259,"value":23972},"From a practical standpoint, depending on secrets can quickly become problematic as they expire and thus require rotation. Of course, you can set up alerting or automate secret rotation but that's something you would prefer to avoid managing.",{"type":254,"tag":427,"props":23974,"children":23975},{"icon":429},[23976],{"type":254,"tag":255,"props":23977,"children":23978},{},[23979],{"type":259,"value":23980},"I recently encountered a situation in Azure DevOps where a deployment failed due to the expiration of an Azure AD Application secret associated with the Service Connection used in the pipeline, and we were not alerted about it. That's the kind of scenario that can easily happen with secrets and that you want to avoid.",{"type":254,"tag":255,"props":23982,"children":23983},{},[23984],{"type":259,"value":23985},"So what can we do about that?",{"type":254,"tag":255,"props":23987,"children":23988},{},[23989,23991,23996,23998,24005],{"type":259,"value":23990},"👉 We can stop using secret credentials and use ",{"type":254,"tag":262,"props":23992,"children":23994},{"href":19620,"rel":23993},[266],[23995],{"type":259,"value":19624},{"type":259,"value":23997}," instead. I suggest you have a look at this ",{"type":254,"tag":262,"props":23999,"children":24002},{"href":24000,"rel":24001},"https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect",[266],[24003],{"type":259,"value":24004},"GitHub documentation page",{"type":259,"value":24006}," as well to better understand how it works but basically, you can remember the following:",{"type":254,"tag":332,"props":24008,"children":24009},{},[24010,24015,24020],{"type":254,"tag":336,"props":24011,"children":24012},{},[24013],{"type":259,"value":24014},"this mechanism relies on Open ID Connect and trust between Azure and GitHub",{"type":254,"tag":336,"props":24016,"children":24017},{},[24018],{"type":259,"value":24019},"the GitHub pipeline does not need an Azure AD application secret anymore to authenticate to Azure",{"type":254,"tag":336,"props":24021,"children":24022},{},[24023],{"type":259,"value":24024},"it's not an Azure thing only, it's an open standard that also works with other cloud providers and other platforms than Github",{"type":254,"tag":255,"props":24026,"children":24027},{},[24028,24030,24035,24037,24043],{"type":259,"value":24029},"To establish the trust relationship between the Azure AD application and the GitHub repository, a ",{"type":254,"tag":19837,"props":24031,"children":24032},{},[24033],{"type":259,"value":24034},"Federated Identity Credential",{"type":259,"value":24036}," must be created in the Azure Active Directory. You can find how to do that manually from the portal in the ",{"type":254,"tag":262,"props":24038,"children":24041},{"href":24039,"rel":24040},"https://learn.microsoft.com/en-us/azure/active-directory/workload-identities/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-azp",[266],[24042],{"type":259,"value":6340},{"type":259,"value":24044}," but we are going to directly automate that 😉.",{"type":254,"tag":455,"props":24046,"children":24048},{"id":24047},"the-complete-solution-to-implement",[24049],{"type":259,"value":24050},"The complete solution to implement",{"type":254,"tag":255,"props":24052,"children":24053},{},[24054],{"type":254,"tag":1161,"props":24055,"children":24058},{"alt":16739,"className":24056,"src":24057},[1165,1166],"/posts/images/azurereadygithub_overview_2.webp",[],{"type":254,"tag":282,"props":24060,"children":24062},{"id":24061},"why-use-pulumi-in-that-context",[24063],{"type":259,"value":24064},"Why use Pulumi in that context?",{"type":254,"tag":255,"props":24066,"children":24067},{},[24068],{"type":259,"value":24069},"You might wonder why I chose to automate this process using Pulumi instead of writing a Bash or PowerShell script that would execute commands from the GitHub CLI and the Azure CLI.",{"type":254,"tag":427,"props":24071,"children":24072},{"icon":567},[24073],{"type":254,"tag":255,"props":24074,"children":24075},{},[24076,24078,24083,24085,24090],{"type":259,"value":24077},"By the way, you should check ",{"type":254,"tag":262,"props":24079,"children":24081},{"href":16805,"rel":24080},[266],[24082],{"type":259,"value":16353},{"type":259,"value":24084}," if you have not done it yet, it's very handy. And if you have read my article about ",{"type":254,"tag":262,"props":24086,"children":24088},{"href":17271,"rel":24087},[266],[24089],{"type":259,"value":16351},{"type":259,"value":24091},", you know it's a very convenient tool as well.",{"type":254,"tag":255,"props":24093,"children":24094},{},[24095],{"type":259,"value":24096},"I think Pulumi is a better choice here because:",{"type":254,"tag":332,"props":24098,"children":24099},{},[24100,24105,24110,24115],{"type":254,"tag":336,"props":24101,"children":24102},{},[24103],{"type":259,"value":24104},"a script is imperative by nature, but declarative infrastructure seems more suitable to avoid dealing with idempotency",{"type":254,"tag":336,"props":24106,"children":24107},{},[24108],{"type":259,"value":24109},"Pulumi can interact with both GitHub and Azure using its providers",{"type":254,"tag":336,"props":24111,"children":24112},{},[24113],{"type":259,"value":24114},"the code will be easier to write and maintain",{"type":254,"tag":336,"props":24116,"children":24117},{},[24118],{"type":259,"value":24119},"the code could be integrated into any application (including a future self-service infrastructure portal) using Pulumi Automation API",{"type":254,"tag":255,"props":24121,"children":24122},{},[24123],{"type":259,"value":24124},"In this article, the Pulumi code will be in TypeScript but it would work in any language supported by Pulumi.",{"type":254,"tag":282,"props":24126,"children":24128},{"id":24127},"automate-the-creation-of-the-azure-ready-github-repository",[24129],{"type":259,"value":24130},"Automate the creation of the Azure-Ready GitHub Repository",{"type":254,"tag":455,"props":24132,"children":24134},{"id":24133},"create-the-pulumi-project",[24135],{"type":259,"value":24136},"Create the Pulumi project",{"type":254,"tag":255,"props":24138,"children":24139},{},[24140],{"type":259,"value":24141},"Let's start by scaffolding a new Pulumi project using TypeScript:",{"type":254,"tag":612,"props":24143,"children":24145},{"className":10656,"code":24144,"language":10658,"meta":205,"style":205},"pulumi new typescript -n AzureOIDC -s dev -d \"A program to set up an Azure-Ready GitHub repository\"\n",[24146],{"type":254,"tag":389,"props":24147,"children":24148},{"__ignoreMap":205},[24149],{"type":254,"tag":622,"props":24150,"children":24151},{"class":624,"line":625},[24152,24157,24161,24166,24170,24175,24179,24184,24188,24193],{"type":254,"tag":622,"props":24153,"children":24154},{"style":1202},[24155],{"type":259,"value":24156},"pulumi new typescript ",{"type":254,"tag":622,"props":24158,"children":24159},{"style":629},[24160],{"type":259,"value":17065},{"type":254,"tag":622,"props":24162,"children":24163},{"style":1202},[24164],{"type":259,"value":24165},"n AzureOIDC ",{"type":254,"tag":622,"props":24167,"children":24168},{"style":629},[24169],{"type":259,"value":17065},{"type":254,"tag":622,"props":24171,"children":24172},{"style":1202},[24173],{"type":259,"value":24174},"s dev ",{"type":254,"tag":622,"props":24176,"children":24177},{"style":629},[24178],{"type":259,"value":17065},{"type":254,"tag":622,"props":24180,"children":24181},{"style":1202},[24182],{"type":259,"value":24183},"d ",{"type":254,"tag":622,"props":24185,"children":24186},{"style":629},[24187],{"type":259,"value":652},{"type":254,"tag":622,"props":24189,"children":24190},{"style":664},[24191],{"type":259,"value":24192},"A program to set up an Azure-Ready GitHub repository",{"type":254,"tag":622,"props":24194,"children":24195},{"style":629},[24196],{"type":259,"value":9909},{"type":254,"tag":255,"props":24198,"children":24199},{},[24200],{"type":259,"value":24201},"This command creates a new pulumi project and stack from the TypeScript template:",{"type":254,"tag":332,"props":24203,"children":24204},{},[24205,24222,24237],{"type":254,"tag":336,"props":24206,"children":24207},{},[24208,24209,24214,24216,24221],{"type":259,"value":19835},{"type":254,"tag":19837,"props":24210,"children":24211},{},[24212],{"type":259,"value":24213},"AzureOIDC\"",{"type":259,"value":24215}," is specified using the ",{"type":254,"tag":389,"props":24217,"children":24219},{"className":24218},[],[24220],{"type":259,"value":19849},{"type":259,"value":19851},{"type":254,"tag":336,"props":24223,"children":24224},{},[24225,24226,24230,24231,24236],{"type":259,"value":19856},{"type":254,"tag":19837,"props":24227,"children":24228},{},[24229],{"type":259,"value":24192},{"type":259,"value":19843},{"type":254,"tag":389,"props":24232,"children":24234},{"className":24233},[],[24235],{"type":259,"value":19867},{"type":259,"value":19851},{"type":254,"tag":336,"props":24238,"children":24239},{},[24240,24241,24245,24246,24251],{"type":259,"value":19873},{"type":254,"tag":19837,"props":24242,"children":24243},{},[24244],{"type":259,"value":19878},{"type":259,"value":19843},{"type":254,"tag":389,"props":24247,"children":24249},{"className":24248},[],[24250],{"type":259,"value":19885},{"type":259,"value":19851},{"type":254,"tag":427,"props":24253,"children":24254},{"icon":19646},[24255],{"type":254,"tag":255,"props":24256,"children":24257},{},[24258,24260,24266,24268,24274,24276,24282,24284,24290],{"type":259,"value":24259},"By default, the ",{"type":254,"tag":389,"props":24261,"children":24263},{"className":24262},[],[24264],{"type":259,"value":24265},"pulumi new",{"type":259,"value":24267}," command installs the dependencies when creating the project. You can prevent this by specifying the ",{"type":254,"tag":389,"props":24269,"children":24271},{"className":24270},[],[24272],{"type":259,"value":24273},"-g",{"type":259,"value":24275}," option, which is useful when you want to use another package manager than the default one (",{"type":254,"tag":389,"props":24277,"children":24279},{"className":24278},[],[24280],{"type":259,"value":24281},"pnpm",{"type":259,"value":24283}," instead of ",{"type":254,"tag":389,"props":24285,"children":24287},{"className":24286},[],[24288],{"type":259,"value":24289},"npm",{"type":259,"value":24291}," for instance).",{"type":254,"tag":255,"props":24293,"children":24294},{},[24295],{"type":259,"value":19891},{"type":254,"tag":332,"props":24297,"children":24298},{},[24299,24308,24317],{"type":254,"tag":336,"props":24300,"children":24301},{},[24302,24303],{"type":259,"value":19899},{"type":254,"tag":262,"props":24304,"children":24306},{"href":19902,"rel":24305},[266],[24307],{"type":259,"value":19906},{"type":254,"tag":336,"props":24309,"children":24310},{},[24311,24312],{"type":259,"value":19899},{"type":254,"tag":262,"props":24313,"children":24315},{"href":19913,"rel":24314},[266],[24316],{"type":259,"value":19917},{"type":254,"tag":336,"props":24318,"children":24319},{},[24320,24321],{"type":259,"value":19899},{"type":254,"tag":262,"props":24322,"children":24325},{"href":24323,"rel":24324},"https://www.pulumi.com/registry/packages/github/",[266],[24326],{"type":259,"value":24327},"GitHub provider",{"type":254,"tag":255,"props":24329,"children":24330},{},[24331,24333,24339],{"type":259,"value":24332},"So we can add the following packages to our ",{"type":254,"tag":389,"props":24334,"children":24336},{"className":24335},[],[24337],{"type":259,"value":24338},"package.json",{"type":259,"value":24340}," file:",{"type":254,"tag":332,"props":24342,"children":24343},{},[24344,24349,24354],{"type":254,"tag":336,"props":24345,"children":24346},{},[24347],{"type":259,"value":24348},"@pulumi/azure-native",{"type":254,"tag":336,"props":24350,"children":24351},{},[24352],{"type":259,"value":24353},"@pulumi/azuread",{"type":254,"tag":336,"props":24355,"children":24356},{},[24357],{"type":259,"value":24358},"@pulumi/github",{"type":254,"tag":455,"props":24360,"children":24361},{"id":16812},[24362],{"type":259,"value":16815},{"type":254,"tag":255,"props":24364,"children":24365},{},[24366,24368,24375,24377,24384],{"type":259,"value":24367},"To use the GitHub provider, we have to provide GitHub credentials. For that, we can create a personal access token (I prefer to create a ",{"type":254,"tag":262,"props":24369,"children":24372},{"href":24370,"rel":24371},"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#fine-grained-personal-access-tokens",[266],[24373],{"type":259,"value":24374},"fine-grained personal access token",{"type":259,"value":24376}," although a ",{"type":254,"tag":262,"props":24378,"children":24381},{"href":24379,"rel":24380},"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic",[266],[24382],{"type":259,"value":24383},"classic personal access token",{"type":259,"value":24385}," would also work). Next, we simply set the GitHub token in our Pulumi configuration, and the GitHub provider will automatically use it:",{"type":254,"tag":612,"props":24387,"children":24389},{"className":10656,"code":24388,"language":10658,"meta":205,"style":205},"pulumi config set github:token XXXXXXXXXXXXXX --secret\n",[24390],{"type":254,"tag":389,"props":24391,"children":24392},{"__ignoreMap":205},[24393],{"type":254,"tag":622,"props":24394,"children":24395},{"class":624,"line":625},[24396,24401,24405],{"type":254,"tag":622,"props":24397,"children":24398},{"style":1202},[24399],{"type":259,"value":24400},"pulumi config set github:token XXXXXXXXXXXXXX ",{"type":254,"tag":622,"props":24402,"children":24403},{"style":629},[24404],{"type":259,"value":10675},{"type":254,"tag":622,"props":24406,"children":24407},{"style":1202},[24408],{"type":259,"value":24409},"secret\n",{"type":254,"tag":427,"props":24411,"children":24412},{"icon":20082},[24413],{"type":254,"tag":255,"props":24414,"children":24415},{},[24416,24418,24423],{"type":259,"value":24417},"Don't forget to include the ",{"type":254,"tag":389,"props":24419,"children":24421},{"className":24420},[],[24422],{"type":259,"value":20094},{"type":259,"value":24424}," option when setting sensitive configurations, as this ensures that Pulumi encrypts the information. By doing so, we can safely commit the configuration files without creating security risks.",{"type":254,"tag":255,"props":24426,"children":24427},{},[24428],{"type":259,"value":24429},"Now, it's time to create our GitHub repository!",{"type":254,"tag":612,"props":24431,"children":24435},{"className":24432,"code":24433,"language":24434,"meta":205,"style":205},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import * as github from \"@pulumi/github\";\n\nconst repository = new github.Repository(\"azure-ready-repository\", {\n  name: \"azure-ready-repository\",\n  visibility: \"public\",\n  autoInit: true\n});\n\nexport const repositoryCloneUrl = repository.httpCloneUrl;\n","typescript",[24436],{"type":254,"tag":389,"props":24437,"children":24438},{"__ignoreMap":205},[24439,24483,24490,24550,24578,24606,24622,24637,24644],{"type":254,"tag":622,"props":24440,"children":24441},{"class":624,"line":625},[24442,24447,24452,24457,24462,24467,24471,24475,24479],{"type":254,"tag":622,"props":24443,"children":24444},{"style":1196},[24445],{"type":259,"value":24446},"import",{"type":254,"tag":622,"props":24448,"children":24449},{"style":629},[24450],{"type":259,"value":24451}," *",{"type":254,"tag":622,"props":24453,"children":24454},{"style":1196},[24455],{"type":259,"value":24456}," as",{"type":254,"tag":622,"props":24458,"children":24459},{"style":1202},[24460],{"type":259,"value":24461}," github ",{"type":254,"tag":622,"props":24463,"children":24464},{"style":1196},[24465],{"type":259,"value":24466},"from",{"type":254,"tag":622,"props":24468,"children":24469},{"style":629},[24470],{"type":259,"value":661},{"type":254,"tag":622,"props":24472,"children":24473},{"style":664},[24474],{"type":259,"value":24358},{"type":254,"tag":622,"props":24476,"children":24477},{"style":629},[24478],{"type":259,"value":652},{"type":254,"tag":622,"props":24480,"children":24481},{"style":629},[24482],{"type":259,"value":2350},{"type":254,"tag":622,"props":24484,"children":24485},{"class":624,"line":635},[24486],{"type":254,"tag":622,"props":24487,"children":24488},{"emptyLinePlaceholder":1699},[24489],{"type":259,"value":1702},{"type":254,"tag":622,"props":24491,"children":24492},{"class":624,"line":679},[24493,24498,24503,24507,24511,24516,24520,24525,24529,24533,24538,24542,24546],{"type":254,"tag":622,"props":24494,"children":24495},{"style":644},[24496],{"type":259,"value":24497},"const",{"type":254,"tag":622,"props":24499,"children":24500},{"style":1202},[24501],{"type":259,"value":24502}," repository ",{"type":254,"tag":622,"props":24504,"children":24505},{"style":629},[24506],{"type":259,"value":1654},{"type":254,"tag":622,"props":24508,"children":24509},{"style":629},[24510],{"type":259,"value":1624},{"type":254,"tag":622,"props":24512,"children":24513},{"style":1202},[24514],{"type":259,"value":24515}," github",{"type":254,"tag":622,"props":24517,"children":24518},{"style":629},[24519],{"type":259,"value":594},{"type":254,"tag":622,"props":24521,"children":24522},{"style":1735},[24523],{"type":259,"value":24524},"Repository",{"type":254,"tag":622,"props":24526,"children":24527},{"style":1202},[24528],{"type":259,"value":1668},{"type":254,"tag":622,"props":24530,"children":24531},{"style":629},[24532],{"type":259,"value":652},{"type":254,"tag":622,"props":24534,"children":24535},{"style":664},[24536],{"type":259,"value":24537},"azure-ready-repository",{"type":254,"tag":622,"props":24539,"children":24540},{"style":629},[24541],{"type":259,"value":652},{"type":254,"tag":622,"props":24543,"children":24544},{"style":629},[24545],{"type":259,"value":3149},{"type":254,"tag":622,"props":24547,"children":24548},{"style":629},[24549],{"type":259,"value":702},{"type":254,"tag":622,"props":24551,"children":24552},{"class":624,"line":705},[24553,24558,24562,24566,24570,24574],{"type":254,"tag":622,"props":24554,"children":24555},{"style":768},[24556],{"type":259,"value":24557},"  name",{"type":254,"tag":622,"props":24559,"children":24560},{"style":629},[24561],{"type":259,"value":474},{"type":254,"tag":622,"props":24563,"children":24564},{"style":629},[24565],{"type":259,"value":661},{"type":254,"tag":622,"props":24567,"children":24568},{"style":664},[24569],{"type":259,"value":24537},{"type":254,"tag":622,"props":24571,"children":24572},{"style":629},[24573],{"type":259,"value":652},{"type":254,"tag":622,"props":24575,"children":24576},{"style":629},[24577],{"type":259,"value":676},{"type":254,"tag":622,"props":24579,"children":24580},{"class":624,"line":732},[24581,24586,24590,24594,24598,24602],{"type":254,"tag":622,"props":24582,"children":24583},{"style":768},[24584],{"type":259,"value":24585},"  visibility",{"type":254,"tag":622,"props":24587,"children":24588},{"style":629},[24589],{"type":259,"value":474},{"type":254,"tag":622,"props":24591,"children":24592},{"style":629},[24593],{"type":259,"value":661},{"type":254,"tag":622,"props":24595,"children":24596},{"style":664},[24597],{"type":259,"value":1437},{"type":254,"tag":622,"props":24599,"children":24600},{"style":629},[24601],{"type":259,"value":652},{"type":254,"tag":622,"props":24603,"children":24604},{"style":629},[24605],{"type":259,"value":676},{"type":254,"tag":622,"props":24607,"children":24608},{"class":624,"line":759},[24609,24614,24618],{"type":254,"tag":622,"props":24610,"children":24611},{"style":768},[24612],{"type":259,"value":24613},"  autoInit",{"type":254,"tag":622,"props":24615,"children":24616},{"style":629},[24617],{"type":259,"value":474},{"type":254,"tag":622,"props":24619,"children":24620},{"style":7715},[24621],{"type":259,"value":1075},{"type":254,"tag":622,"props":24623,"children":24624},{"class":624,"line":798},[24625,24629,24633],{"type":254,"tag":622,"props":24626,"children":24627},{"style":629},[24628],{"type":259,"value":2421},{"type":254,"tag":622,"props":24630,"children":24631},{"style":1202},[24632],{"type":259,"value":2309},{"type":254,"tag":622,"props":24634,"children":24635},{"style":629},[24636],{"type":259,"value":2350},{"type":254,"tag":622,"props":24638,"children":24639},{"class":624,"line":836},[24640],{"type":254,"tag":622,"props":24641,"children":24642},{"emptyLinePlaceholder":1699},[24643],{"type":259,"value":1702},{"type":254,"tag":622,"props":24645,"children":24646},{"class":624,"line":862},[24647,24652,24657,24662,24666,24670,24674,24679],{"type":254,"tag":622,"props":24648,"children":24649},{"style":1196},[24650],{"type":259,"value":24651},"export",{"type":254,"tag":622,"props":24653,"children":24654},{"style":644},[24655],{"type":259,"value":24656}," const",{"type":254,"tag":622,"props":24658,"children":24659},{"style":1202},[24660],{"type":259,"value":24661}," repositoryCloneUrl ",{"type":254,"tag":622,"props":24663,"children":24664},{"style":629},[24665],{"type":259,"value":1654},{"type":254,"tag":622,"props":24667,"children":24668},{"style":1202},[24669],{"type":259,"value":20498},{"type":254,"tag":622,"props":24671,"children":24672},{"style":629},[24673],{"type":259,"value":594},{"type":254,"tag":622,"props":24675,"children":24676},{"style":1202},[24677],{"type":259,"value":24678},"httpCloneUrl",{"type":254,"tag":622,"props":24680,"children":24681},{"style":629},[24682],{"type":259,"value":2350},{"type":254,"tag":255,"props":24684,"children":24685},{},[24686,24688,24695,24697,24702],{"type":259,"value":24687},"Pulumi has an ",{"type":254,"tag":262,"props":24689,"children":24692},{"href":24690,"rel":24691},"https://www.pulumi.com/docs/concepts/resources/names/#autonaming",[266],[24693],{"type":259,"value":24694},"auto-naming capability",{"type":259,"value":24696}," that is very convenient to prevent name collisions or to ensure zero-downtime resource updates. Yet, in this context, I prefer to avoid a random suffix in my GitHub repository name, that's why I am specifying the ",{"type":254,"tag":389,"props":24698,"children":24700},{"className":24699},[],[24701],{"type":259,"value":16394},{"type":259,"value":24703}," property to override the auto-naming behavior.",{"type":254,"tag":255,"props":24705,"children":24706},{},[24707,24709,24716,24718,24724],{"type":259,"value":24708},"The last line creates a stack ",{"type":254,"tag":262,"props":24710,"children":24713},{"href":24711,"rel":24712},"https://www.pulumi.com/docs/concepts/stack/#outputs",[266],[24714],{"type":259,"value":24715},"output",{"type":259,"value":24717}," named ",{"type":254,"tag":389,"props":24719,"children":24721},{"className":24720},[],[24722],{"type":259,"value":24723},"repositoryCloneUrl",{"type":259,"value":24725}," so that we can easily get the URL to clone our newly created repository.",{"type":254,"tag":427,"props":24727,"children":24728},{"icon":19646},[24729],{"type":254,"tag":255,"props":24730,"children":24731},{},[24732,24734,24740,24742,24748,24750,24756],{"type":259,"value":24733},"I wanted the repository to be initialized, that's why I set the ",{"type":254,"tag":389,"props":24735,"children":24737},{"className":24736},[],[24738],{"type":259,"value":24739},"autoInit",{"type":259,"value":24741}," property to ",{"type":254,"tag":389,"props":24743,"children":24745},{"className":24744},[],[24746],{"type":259,"value":24747},"true",{"type":259,"value":24749}," but you should set it to ",{"type":254,"tag":389,"props":24751,"children":24753},{"className":24752},[],[24754],{"type":259,"value":24755},"false",{"type":259,"value":24757}," if you have an existing local git repository that you want to push on this GitHub repository.",{"type":254,"tag":455,"props":24759,"children":24761},{"id":24760},"create-the-identity-in-azure-active-directory-for-the-github-actions-workflow",[24762,24764,24768],{"type":259,"value":24763},"Create the ",{"type":254,"tag":19837,"props":24765,"children":24766},{},[24767],{"type":259,"value":23895},{"type":259,"value":24769}," in Azure Active Directory for the GitHub Actions workflow",{"type":254,"tag":255,"props":24771,"children":24772},{},[24773],{"type":259,"value":24774},"Creating an Azure AD application and its service principal is not very complicated:",{"type":254,"tag":612,"props":24776,"children":24778},{"className":24432,"code":24777,"language":24434,"meta":205,"style":205},"import * as azuread from \"@pulumi/azuread\";\n\nconst aadApplication = new azuread.Application(\"AzureReadyApp\", { displayName: \"Azure Ready App\" });\nconst servicePrincipal = new azuread.ServicePrincipal(\"AzureReadServicePrincipal\", {\n  applicationId: aadApplication.applicationId,\n});\n",[24779],{"type":254,"tag":389,"props":24780,"children":24781},{"__ignoreMap":205},[24782,24822,24829,24922,24979,25008],{"type":254,"tag":622,"props":24783,"children":24784},{"class":624,"line":625},[24785,24789,24793,24797,24802,24806,24810,24814,24818],{"type":254,"tag":622,"props":24786,"children":24787},{"style":1196},[24788],{"type":259,"value":24446},{"type":254,"tag":622,"props":24790,"children":24791},{"style":629},[24792],{"type":259,"value":24451},{"type":254,"tag":622,"props":24794,"children":24795},{"style":1196},[24796],{"type":259,"value":24456},{"type":254,"tag":622,"props":24798,"children":24799},{"style":1202},[24800],{"type":259,"value":24801}," azuread ",{"type":254,"tag":622,"props":24803,"children":24804},{"style":1196},[24805],{"type":259,"value":24466},{"type":254,"tag":622,"props":24807,"children":24808},{"style":629},[24809],{"type":259,"value":661},{"type":254,"tag":622,"props":24811,"children":24812},{"style":664},[24813],{"type":259,"value":24353},{"type":254,"tag":622,"props":24815,"children":24816},{"style":629},[24817],{"type":259,"value":652},{"type":254,"tag":622,"props":24819,"children":24820},{"style":629},[24821],{"type":259,"value":2350},{"type":254,"tag":622,"props":24823,"children":24824},{"class":624,"line":635},[24825],{"type":254,"tag":622,"props":24826,"children":24827},{"emptyLinePlaceholder":1699},[24828],{"type":259,"value":1702},{"type":254,"tag":622,"props":24830,"children":24831},{"class":624,"line":679},[24832,24836,24841,24845,24849,24854,24858,24863,24867,24871,24876,24880,24884,24888,24893,24897,24901,24906,24910,24914,24918],{"type":254,"tag":622,"props":24833,"children":24834},{"style":644},[24835],{"type":259,"value":24497},{"type":254,"tag":622,"props":24837,"children":24838},{"style":1202},[24839],{"type":259,"value":24840}," aadApplication ",{"type":254,"tag":622,"props":24842,"children":24843},{"style":629},[24844],{"type":259,"value":1654},{"type":254,"tag":622,"props":24846,"children":24847},{"style":629},[24848],{"type":259,"value":1624},{"type":254,"tag":622,"props":24850,"children":24851},{"style":1202},[24852],{"type":259,"value":24853}," azuread",{"type":254,"tag":622,"props":24855,"children":24856},{"style":629},[24857],{"type":259,"value":594},{"type":254,"tag":622,"props":24859,"children":24860},{"style":1735},[24861],{"type":259,"value":24862},"Application",{"type":254,"tag":622,"props":24864,"children":24865},{"style":1202},[24866],{"type":259,"value":1668},{"type":254,"tag":622,"props":24868,"children":24869},{"style":629},[24870],{"type":259,"value":652},{"type":254,"tag":622,"props":24872,"children":24873},{"style":664},[24874],{"type":259,"value":24875},"AzureReadyApp",{"type":254,"tag":622,"props":24877,"children":24878},{"style":629},[24879],{"type":259,"value":652},{"type":254,"tag":622,"props":24881,"children":24882},{"style":629},[24883],{"type":259,"value":3149},{"type":254,"tag":622,"props":24885,"children":24886},{"style":629},[24887],{"type":259,"value":1990},{"type":254,"tag":622,"props":24889,"children":24890},{"style":768},[24891],{"type":259,"value":24892}," displayName",{"type":254,"tag":622,"props":24894,"children":24895},{"style":629},[24896],{"type":259,"value":474},{"type":254,"tag":622,"props":24898,"children":24899},{"style":629},[24900],{"type":259,"value":661},{"type":254,"tag":622,"props":24902,"children":24903},{"style":664},[24904],{"type":259,"value":24905},"Azure Ready App",{"type":254,"tag":622,"props":24907,"children":24908},{"style":629},[24909],{"type":259,"value":652},{"type":254,"tag":622,"props":24911,"children":24912},{"style":629},[24913],{"type":259,"value":14256},{"type":254,"tag":622,"props":24915,"children":24916},{"style":1202},[24917],{"type":259,"value":2309},{"type":254,"tag":622,"props":24919,"children":24920},{"style":629},[24921],{"type":259,"value":2350},{"type":254,"tag":622,"props":24923,"children":24924},{"class":624,"line":705},[24925,24929,24934,24938,24942,24946,24950,24954,24958,24962,24967,24971,24975],{"type":254,"tag":622,"props":24926,"children":24927},{"style":644},[24928],{"type":259,"value":24497},{"type":254,"tag":622,"props":24930,"children":24931},{"style":1202},[24932],{"type":259,"value":24933}," servicePrincipal ",{"type":254,"tag":622,"props":24935,"children":24936},{"style":629},[24937],{"type":259,"value":1654},{"type":254,"tag":622,"props":24939,"children":24940},{"style":629},[24941],{"type":259,"value":1624},{"type":254,"tag":622,"props":24943,"children":24944},{"style":1202},[24945],{"type":259,"value":24853},{"type":254,"tag":622,"props":24947,"children":24948},{"style":629},[24949],{"type":259,"value":594},{"type":254,"tag":622,"props":24951,"children":24952},{"style":1735},[24953],{"type":259,"value":22028},{"type":254,"tag":622,"props":24955,"children":24956},{"style":1202},[24957],{"type":259,"value":1668},{"type":254,"tag":622,"props":24959,"children":24960},{"style":629},[24961],{"type":259,"value":652},{"type":254,"tag":622,"props":24963,"children":24964},{"style":664},[24965],{"type":259,"value":24966},"AzureReadServicePrincipal",{"type":254,"tag":622,"props":24968,"children":24969},{"style":629},[24970],{"type":259,"value":652},{"type":254,"tag":622,"props":24972,"children":24973},{"style":629},[24974],{"type":259,"value":3149},{"type":254,"tag":622,"props":24976,"children":24977},{"style":629},[24978],{"type":259,"value":702},{"type":254,"tag":622,"props":24980,"children":24981},{"class":624,"line":732},[24982,24987,24991,24995,24999,25004],{"type":254,"tag":622,"props":24983,"children":24984},{"style":768},[24985],{"type":259,"value":24986},"  applicationId",{"type":254,"tag":622,"props":24988,"children":24989},{"style":629},[24990],{"type":259,"value":474},{"type":254,"tag":622,"props":24992,"children":24993},{"style":1202},[24994],{"type":259,"value":21698},{"type":254,"tag":622,"props":24996,"children":24997},{"style":629},[24998],{"type":259,"value":594},{"type":254,"tag":622,"props":25000,"children":25001},{"style":1202},[25002],{"type":259,"value":25003},"applicationId",{"type":254,"tag":622,"props":25005,"children":25006},{"style":629},[25007],{"type":259,"value":676},{"type":254,"tag":622,"props":25009,"children":25010},{"class":624,"line":759},[25011,25015,25019],{"type":254,"tag":622,"props":25012,"children":25013},{"style":629},[25014],{"type":259,"value":2421},{"type":254,"tag":622,"props":25016,"children":25017},{"style":1202},[25018],{"type":259,"value":2309},{"type":254,"tag":622,"props":25020,"children":25021},{"style":629},[25022],{"type":259,"value":2350},{"type":254,"tag":255,"props":25024,"children":25025},{},[25026,25028,25037],{"type":259,"value":25027},"The OIDC trust thing is a bit more complex. Fortunately, Microsoft's documentation has a detailed page ",{"type":254,"tag":262,"props":25029,"children":25031},{"href":24039,"rel":25030},[266],[25032],{"type":254,"tag":19837,"props":25033,"children":25034},{},[25035],{"type":259,"value":25036},"Configuring an app to trust an external identity provider",{"type":259,"value":25038}," that explains everything and shows how to add a federated identity for GitHub Actions using the Azure Portal, Azure CLI, or Azure PowerShell.",{"type":254,"tag":255,"props":25040,"children":25041},{},[25042],{"type":259,"value":25043},"Let's do the same thing using TypeScript and Pulumi Azure AD provider:",{"type":254,"tag":612,"props":25045,"children":25047},{"className":24432,"code":25046,"language":24434,"meta":205,"style":205},"new azuread.ApplicationFederatedIdentityCredential(\"AzureReadyAppFederatedIdentityCredential\", {\n  applicationObjectId: aadApplication.objectId,\n  displayName: \"AzureReadyDeploys\",\n  description: \"Deployments for azure-ready-repository\",\n  audiences: [\"api://AzureADTokenExchange\"],\n  issuer: \"https://token.actions.githubusercontent.com\",\n  subject: pulumi.interpolate`repo:${repository.fullName}:ref:refs/heads/main`,\n});\n",[25048],{"type":254,"tag":389,"props":25049,"children":25050},{"__ignoreMap":205},[25051,25096,25125,25153,25181,25217,25245,25314],{"type":254,"tag":622,"props":25052,"children":25053},{"class":624,"line":625},[25054,25058,25062,25066,25071,25075,25079,25084,25088,25092],{"type":254,"tag":622,"props":25055,"children":25056},{"style":629},[25057],{"type":259,"value":21937},{"type":254,"tag":622,"props":25059,"children":25060},{"style":1202},[25061],{"type":259,"value":24853},{"type":254,"tag":622,"props":25063,"children":25064},{"style":629},[25065],{"type":259,"value":594},{"type":254,"tag":622,"props":25067,"children":25068},{"style":1735},[25069],{"type":259,"value":25070},"ApplicationFederatedIdentityCredential",{"type":254,"tag":622,"props":25072,"children":25073},{"style":1202},[25074],{"type":259,"value":1668},{"type":254,"tag":622,"props":25076,"children":25077},{"style":629},[25078],{"type":259,"value":652},{"type":254,"tag":622,"props":25080,"children":25081},{"style":664},[25082],{"type":259,"value":25083},"AzureReadyAppFederatedIdentityCredential",{"type":254,"tag":622,"props":25085,"children":25086},{"style":629},[25087],{"type":259,"value":652},{"type":254,"tag":622,"props":25089,"children":25090},{"style":629},[25091],{"type":259,"value":3149},{"type":254,"tag":622,"props":25093,"children":25094},{"style":629},[25095],{"type":259,"value":702},{"type":254,"tag":622,"props":25097,"children":25098},{"class":624,"line":635},[25099,25104,25108,25112,25116,25121],{"type":254,"tag":622,"props":25100,"children":25101},{"style":768},[25102],{"type":259,"value":25103},"  applicationObjectId",{"type":254,"tag":622,"props":25105,"children":25106},{"style":629},[25107],{"type":259,"value":474},{"type":254,"tag":622,"props":25109,"children":25110},{"style":1202},[25111],{"type":259,"value":21698},{"type":254,"tag":622,"props":25113,"children":25114},{"style":629},[25115],{"type":259,"value":594},{"type":254,"tag":622,"props":25117,"children":25118},{"style":1202},[25119],{"type":259,"value":25120},"objectId",{"type":254,"tag":622,"props":25122,"children":25123},{"style":629},[25124],{"type":259,"value":676},{"type":254,"tag":622,"props":25126,"children":25127},{"class":624,"line":679},[25128,25133,25137,25141,25145,25149],{"type":254,"tag":622,"props":25129,"children":25130},{"style":768},[25131],{"type":259,"value":25132},"  displayName",{"type":254,"tag":622,"props":25134,"children":25135},{"style":629},[25136],{"type":259,"value":474},{"type":254,"tag":622,"props":25138,"children":25139},{"style":629},[25140],{"type":259,"value":661},{"type":254,"tag":622,"props":25142,"children":25143},{"style":664},[25144],{"type":259,"value":22248},{"type":254,"tag":622,"props":25146,"children":25147},{"style":629},[25148],{"type":259,"value":652},{"type":254,"tag":622,"props":25150,"children":25151},{"style":629},[25152],{"type":259,"value":676},{"type":254,"tag":622,"props":25154,"children":25155},{"class":624,"line":705},[25156,25161,25165,25169,25173,25177],{"type":254,"tag":622,"props":25157,"children":25158},{"style":768},[25159],{"type":259,"value":25160},"  description",{"type":254,"tag":622,"props":25162,"children":25163},{"style":629},[25164],{"type":259,"value":474},{"type":254,"tag":622,"props":25166,"children":25167},{"style":629},[25168],{"type":259,"value":661},{"type":254,"tag":622,"props":25170,"children":25171},{"style":664},[25172],{"type":259,"value":22276},{"type":254,"tag":622,"props":25174,"children":25175},{"style":629},[25176],{"type":259,"value":652},{"type":254,"tag":622,"props":25178,"children":25179},{"style":629},[25180],{"type":259,"value":676},{"type":254,"tag":622,"props":25182,"children":25183},{"class":624,"line":732},[25184,25189,25193,25197,25201,25205,25209,25213],{"type":254,"tag":622,"props":25185,"children":25186},{"style":768},[25187],{"type":259,"value":25188},"  audiences",{"type":254,"tag":622,"props":25190,"children":25191},{"style":629},[25192],{"type":259,"value":474},{"type":254,"tag":622,"props":25194,"children":25195},{"style":1202},[25196],{"type":259,"value":13676},{"type":254,"tag":622,"props":25198,"children":25199},{"style":629},[25200],{"type":259,"value":652},{"type":254,"tag":622,"props":25202,"children":25203},{"style":664},[25204],{"type":259,"value":17783},{"type":254,"tag":622,"props":25206,"children":25207},{"style":629},[25208],{"type":259,"value":652},{"type":254,"tag":622,"props":25210,"children":25211},{"style":1202},[25212],{"type":259,"value":20251},{"type":254,"tag":622,"props":25214,"children":25215},{"style":629},[25216],{"type":259,"value":676},{"type":254,"tag":622,"props":25218,"children":25219},{"class":624,"line":759},[25220,25225,25229,25233,25237,25241],{"type":254,"tag":622,"props":25221,"children":25222},{"style":768},[25223],{"type":259,"value":25224},"  issuer",{"type":254,"tag":622,"props":25226,"children":25227},{"style":629},[25228],{"type":259,"value":474},{"type":254,"tag":622,"props":25230,"children":25231},{"style":629},[25232],{"type":259,"value":661},{"type":254,"tag":622,"props":25234,"children":25235},{"style":664},[25236],{"type":259,"value":17671},{"type":254,"tag":622,"props":25238,"children":25239},{"style":629},[25240],{"type":259,"value":652},{"type":254,"tag":622,"props":25242,"children":25243},{"style":629},[25244],{"type":259,"value":676},{"type":254,"tag":622,"props":25246,"children":25247},{"class":624,"line":798},[25248,25253,25257,25262,25266,25271,25276,25280,25284,25289,25293,25298,25302,25306,25310],{"type":254,"tag":622,"props":25249,"children":25250},{"style":768},[25251],{"type":259,"value":25252},"  subject",{"type":254,"tag":622,"props":25254,"children":25255},{"style":629},[25256],{"type":259,"value":474},{"type":254,"tag":622,"props":25258,"children":25259},{"style":1202},[25260],{"type":259,"value":25261}," pulumi",{"type":254,"tag":622,"props":25263,"children":25264},{"style":629},[25265],{"type":259,"value":594},{"type":254,"tag":622,"props":25267,"children":25268},{"style":1735},[25269],{"type":259,"value":25270},"interpolate",{"type":254,"tag":622,"props":25272,"children":25273},{"style":629},[25274],{"type":259,"value":25275},"`",{"type":254,"tag":622,"props":25277,"children":25278},{"style":664},[25279],{"type":259,"value":17696},{"type":254,"tag":622,"props":25281,"children":25282},{"style":629},[25283],{"type":259,"value":17340},{"type":254,"tag":622,"props":25285,"children":25286},{"style":1202},[25287],{"type":259,"value":25288},"repository",{"type":254,"tag":622,"props":25290,"children":25291},{"style":629},[25292],{"type":259,"value":594},{"type":254,"tag":622,"props":25294,"children":25295},{"style":1202},[25296],{"type":259,"value":25297},"fullName",{"type":254,"tag":622,"props":25299,"children":25300},{"style":629},[25301],{"type":259,"value":2421},{"type":254,"tag":622,"props":25303,"children":25304},{"style":664},[25305],{"type":259,"value":17713},{"type":254,"tag":622,"props":25307,"children":25308},{"style":629},[25309],{"type":259,"value":25275},{"type":254,"tag":622,"props":25311,"children":25312},{"style":629},[25313],{"type":259,"value":676},{"type":254,"tag":622,"props":25315,"children":25316},{"class":624,"line":836},[25317,25321,25325],{"type":254,"tag":622,"props":25318,"children":25319},{"style":629},[25320],{"type":259,"value":2421},{"type":254,"tag":622,"props":25322,"children":25323},{"style":1202},[25324],{"type":259,"value":2309},{"type":254,"tag":622,"props":25326,"children":25327},{"style":629},[25328],{"type":259,"value":2350},{"type":254,"tag":255,"props":25330,"children":25331},{},[25332,25333,25338,25340,25345,25347,25352],{"type":259,"value":10970},{"type":254,"tag":389,"props":25334,"children":25336},{"className":25335},[],[25337],{"type":259,"value":17815},{"type":259,"value":25339}," property is what identifies the repository where the GitHub Actions workflow will be authorized to exchange its GitHub token for an Azure access token. It's worth noting that it will only work if the GitHub Actions workflow is run on the git reference (branch or tag) or the environment you specify in ",{"type":254,"tag":389,"props":25341,"children":25343},{"className":25342},[],[25344],{"type":259,"value":17815},{"type":259,"value":25346},". You can also specify that only workflows triggered by a pull request should be authorized. Here, I have used the ",{"type":254,"tag":389,"props":25348,"children":25350},{"className":25349},[],[25351],{"type":259,"value":23830},{"type":259,"value":25353}," branch but I could create multiple Federated Identity Credentials with different subjects if needed.",{"type":254,"tag":255,"props":25355,"children":25356},{},[25357],{"type":259,"value":25358},"With this configuration, the GitHub Actions workflow we create next will be able to obtain a valid Azure access token.",{"type":254,"tag":255,"props":25360,"children":25361},{},[25362,25364,25371],{"type":259,"value":25363},"If you are interested in gaining a better understanding of how all this works, you can refer to ",{"type":254,"tag":262,"props":25365,"children":25368},{"href":25366,"rel":25367},"https://learn.microsoft.com/en-us/azure/active-directory/workload-identities/workload-identity-federation#how-it-works",[266],[25369],{"type":259,"value":25370},"this diagram",{"type":259,"value":25372}," from Microsoft's documentation (with GitHub serving as the external identity provider in our case).",{"type":254,"tag":255,"props":25374,"children":25375},{},[25376],{"type":254,"tag":1161,"props":25377,"children":25381},{"alt":25378,"className":25379,"src":25380},"Sequence diagram explaining Azure OIDC.",[1165,1166],"/posts/images/azurereadygithub_identityfederation.webp",[],{"type":254,"tag":455,"props":25383,"children":25385},{"id":25384},"authorize-the-service-principal-to-provision-resources-on-the-subscription",[25386],{"type":259,"value":25387},"Authorize the Service Principal to provision resources on the subscription",{"type":254,"tag":255,"props":25389,"children":25390},{},[25391],{"type":259,"value":25392},"We have created everything we need to get a valid Azure access token, but we still have not authorized the application to provision resources on our subscription.",{"type":254,"tag":255,"props":25394,"children":25395},{},[25396],{"type":259,"value":25397},"We can do that by giving the Contributor role to our service principal.",{"type":254,"tag":612,"props":25399,"children":25401},{"className":24432,"code":25400,"language":24434,"meta":205,"style":205},"import * as authorization from \"@pulumi/azure-native/authorization\";\nimport { azureBuiltInRoles } from \"./builtInRoles\";\n\nnew authorization.RoleAssignment(\"contributor\", {\n  principalId: servicePrincipal.id,\n  principalType: authorization.PrincipalType.ServicePrincipal,\n  roleDefinitionId: azureBuiltInRoles.contributor,\n  scope: pulumi.interpolate`/subscriptions/${subscriptionId}`,\n});\n",[25402],{"type":254,"tag":389,"props":25403,"children":25404},{"__ignoreMap":205},[25405,25446,25488,25495,25540,25568,25605,25633,25682],{"type":254,"tag":622,"props":25406,"children":25407},{"class":624,"line":625},[25408,25412,25416,25420,25425,25429,25433,25438,25442],{"type":254,"tag":622,"props":25409,"children":25410},{"style":1196},[25411],{"type":259,"value":24446},{"type":254,"tag":622,"props":25413,"children":25414},{"style":629},[25415],{"type":259,"value":24451},{"type":254,"tag":622,"props":25417,"children":25418},{"style":1196},[25419],{"type":259,"value":24456},{"type":254,"tag":622,"props":25421,"children":25422},{"style":1202},[25423],{"type":259,"value":25424}," authorization ",{"type":254,"tag":622,"props":25426,"children":25427},{"style":1196},[25428],{"type":259,"value":24466},{"type":254,"tag":622,"props":25430,"children":25431},{"style":629},[25432],{"type":259,"value":661},{"type":254,"tag":622,"props":25434,"children":25435},{"style":664},[25436],{"type":259,"value":25437},"@pulumi/azure-native/authorization",{"type":254,"tag":622,"props":25439,"children":25440},{"style":629},[25441],{"type":259,"value":652},{"type":254,"tag":622,"props":25443,"children":25444},{"style":629},[25445],{"type":259,"value":2350},{"type":254,"tag":622,"props":25447,"children":25448},{"class":624,"line":635},[25449,25453,25457,25462,25466,25471,25475,25480,25484],{"type":254,"tag":622,"props":25450,"children":25451},{"style":1196},[25452],{"type":259,"value":24446},{"type":254,"tag":622,"props":25454,"children":25455},{"style":629},[25456],{"type":259,"value":1990},{"type":254,"tag":622,"props":25458,"children":25459},{"style":1202},[25460],{"type":259,"value":25461}," azureBuiltInRoles",{"type":254,"tag":622,"props":25463,"children":25464},{"style":629},[25465],{"type":259,"value":14256},{"type":254,"tag":622,"props":25467,"children":25468},{"style":1196},[25469],{"type":259,"value":25470}," from",{"type":254,"tag":622,"props":25472,"children":25473},{"style":629},[25474],{"type":259,"value":661},{"type":254,"tag":622,"props":25476,"children":25477},{"style":664},[25478],{"type":259,"value":25479},"./builtInRoles",{"type":254,"tag":622,"props":25481,"children":25482},{"style":629},[25483],{"type":259,"value":652},{"type":254,"tag":622,"props":25485,"children":25486},{"style":629},[25487],{"type":259,"value":2350},{"type":254,"tag":622,"props":25489,"children":25490},{"class":624,"line":679},[25491],{"type":254,"tag":622,"props":25492,"children":25493},{"emptyLinePlaceholder":1699},[25494],{"type":259,"value":1702},{"type":254,"tag":622,"props":25496,"children":25497},{"class":624,"line":705},[25498,25502,25507,25511,25516,25520,25524,25528,25532,25536],{"type":254,"tag":622,"props":25499,"children":25500},{"style":629},[25501],{"type":259,"value":21937},{"type":254,"tag":622,"props":25503,"children":25504},{"style":1202},[25505],{"type":259,"value":25506}," authorization",{"type":254,"tag":622,"props":25508,"children":25509},{"style":629},[25510],{"type":259,"value":594},{"type":254,"tag":622,"props":25512,"children":25513},{"style":1735},[25514],{"type":259,"value":25515},"RoleAssignment",{"type":254,"tag":622,"props":25517,"children":25518},{"style":1202},[25519],{"type":259,"value":1668},{"type":254,"tag":622,"props":25521,"children":25522},{"style":629},[25523],{"type":259,"value":652},{"type":254,"tag":622,"props":25525,"children":25526},{"style":664},[25527],{"type":259,"value":21955},{"type":254,"tag":622,"props":25529,"children":25530},{"style":629},[25531],{"type":259,"value":652},{"type":254,"tag":622,"props":25533,"children":25534},{"style":629},[25535],{"type":259,"value":3149},{"type":254,"tag":622,"props":25537,"children":25538},{"style":629},[25539],{"type":259,"value":702},{"type":254,"tag":622,"props":25541,"children":25542},{"class":624,"line":732},[25543,25548,25552,25556,25560,25564],{"type":254,"tag":622,"props":25544,"children":25545},{"style":768},[25546],{"type":259,"value":25547},"  principalId",{"type":254,"tag":622,"props":25549,"children":25550},{"style":629},[25551],{"type":259,"value":474},{"type":254,"tag":622,"props":25553,"children":25554},{"style":1202},[25555],{"type":259,"value":21178},{"type":254,"tag":622,"props":25557,"children":25558},{"style":629},[25559],{"type":259,"value":594},{"type":254,"tag":622,"props":25561,"children":25562},{"style":1202},[25563],{"type":259,"value":17175},{"type":254,"tag":622,"props":25565,"children":25566},{"style":629},[25567],{"type":259,"value":676},{"type":254,"tag":622,"props":25569,"children":25570},{"class":624,"line":759},[25571,25576,25580,25584,25588,25593,25597,25601],{"type":254,"tag":622,"props":25572,"children":25573},{"style":768},[25574],{"type":259,"value":25575},"  principalType",{"type":254,"tag":622,"props":25577,"children":25578},{"style":629},[25579],{"type":259,"value":474},{"type":254,"tag":622,"props":25581,"children":25582},{"style":1202},[25583],{"type":259,"value":25506},{"type":254,"tag":622,"props":25585,"children":25586},{"style":629},[25587],{"type":259,"value":594},{"type":254,"tag":622,"props":25589,"children":25590},{"style":1202},[25591],{"type":259,"value":25592},"PrincipalType",{"type":254,"tag":622,"props":25594,"children":25595},{"style":629},[25596],{"type":259,"value":594},{"type":254,"tag":622,"props":25598,"children":25599},{"style":1202},[25600],{"type":259,"value":22028},{"type":254,"tag":622,"props":25602,"children":25603},{"style":629},[25604],{"type":259,"value":676},{"type":254,"tag":622,"props":25606,"children":25607},{"class":624,"line":798},[25608,25613,25617,25621,25625,25629],{"type":254,"tag":622,"props":25609,"children":25610},{"style":768},[25611],{"type":259,"value":25612},"  roleDefinitionId",{"type":254,"tag":622,"props":25614,"children":25615},{"style":629},[25616],{"type":259,"value":474},{"type":254,"tag":622,"props":25618,"children":25619},{"style":1202},[25620],{"type":259,"value":25461},{"type":254,"tag":622,"props":25622,"children":25623},{"style":629},[25624],{"type":259,"value":594},{"type":254,"tag":622,"props":25626,"children":25627},{"style":1202},[25628],{"type":259,"value":21955},{"type":254,"tag":622,"props":25630,"children":25631},{"style":629},[25632],{"type":259,"value":676},{"type":254,"tag":622,"props":25634,"children":25635},{"class":624,"line":836},[25636,25641,25645,25649,25653,25657,25661,25665,25669,25673,25678],{"type":254,"tag":622,"props":25637,"children":25638},{"style":768},[25639],{"type":259,"value":25640},"  scope",{"type":254,"tag":622,"props":25642,"children":25643},{"style":629},[25644],{"type":259,"value":474},{"type":254,"tag":622,"props":25646,"children":25647},{"style":1202},[25648],{"type":259,"value":25261},{"type":254,"tag":622,"props":25650,"children":25651},{"style":629},[25652],{"type":259,"value":594},{"type":254,"tag":622,"props":25654,"children":25655},{"style":1735},[25656],{"type":259,"value":25270},{"type":254,"tag":622,"props":25658,"children":25659},{"style":629},[25660],{"type":259,"value":25275},{"type":254,"tag":622,"props":25662,"children":25663},{"style":664},[25664],{"type":259,"value":21541},{"type":254,"tag":622,"props":25666,"children":25667},{"style":629},[25668],{"type":259,"value":17340},{"type":254,"tag":622,"props":25670,"children":25671},{"style":1202},[25672],{"type":259,"value":17148},{"type":254,"tag":622,"props":25674,"children":25675},{"style":629},[25676],{"type":259,"value":25677},"}`",{"type":254,"tag":622,"props":25679,"children":25680},{"style":629},[25681],{"type":259,"value":676},{"type":254,"tag":622,"props":25683,"children":25684},{"class":624,"line":862},[25685,25689,25693],{"type":254,"tag":622,"props":25686,"children":25687},{"style":629},[25688],{"type":259,"value":2421},{"type":254,"tag":622,"props":25690,"children":25691},{"style":1202},[25692],{"type":259,"value":2309},{"type":254,"tag":622,"props":25694,"children":25695},{"style":629},[25696],{"type":259,"value":2350},{"type":254,"tag":255,"props":25698,"children":25699},{},[25700,25702,25707],{"type":259,"value":25701},"I intentionally did not declare the variable ",{"type":254,"tag":389,"props":25703,"children":25705},{"className":25704},[],[25706],{"type":259,"value":17148},{"type":259,"value":25708}," in the code above. It's because it's up to you to choose how you will provide it. You may want to set it in the configuration and retrieve it from it :",{"type":254,"tag":612,"props":25710,"children":25712},{"className":24432,"code":25711,"language":24434,"meta":205,"style":205},"const config = new pulumi.Config();\nconst subscriptionId = config.get(\"subscriptionId\");\n",[25713],{"type":254,"tag":389,"props":25714,"children":25715},{"__ignoreMap":205},[25716,25757],{"type":254,"tag":622,"props":25717,"children":25718},{"class":624,"line":625},[25719,25723,25728,25732,25736,25740,25744,25749,25753],{"type":254,"tag":622,"props":25720,"children":25721},{"style":644},[25722],{"type":259,"value":24497},{"type":254,"tag":622,"props":25724,"children":25725},{"style":1202},[25726],{"type":259,"value":25727}," config ",{"type":254,"tag":622,"props":25729,"children":25730},{"style":629},[25731],{"type":259,"value":1654},{"type":254,"tag":622,"props":25733,"children":25734},{"style":629},[25735],{"type":259,"value":1624},{"type":254,"tag":622,"props":25737,"children":25738},{"style":1202},[25739],{"type":259,"value":25261},{"type":254,"tag":622,"props":25741,"children":25742},{"style":629},[25743],{"type":259,"value":594},{"type":254,"tag":622,"props":25745,"children":25746},{"style":1735},[25747],{"type":259,"value":25748},"Config",{"type":254,"tag":622,"props":25750,"children":25751},{"style":1202},[25752],{"type":259,"value":2724},{"type":254,"tag":622,"props":25754,"children":25755},{"style":629},[25756],{"type":259,"value":2350},{"type":254,"tag":622,"props":25758,"children":25759},{"class":624,"line":635},[25760,25764,25769,25773,25777,25781,25786,25790,25794,25798,25802,25806],{"type":254,"tag":622,"props":25761,"children":25762},{"style":644},[25763],{"type":259,"value":24497},{"type":254,"tag":622,"props":25765,"children":25766},{"style":1202},[25767],{"type":259,"value":25768}," subscriptionId ",{"type":254,"tag":622,"props":25770,"children":25771},{"style":629},[25772],{"type":259,"value":1654},{"type":254,"tag":622,"props":25774,"children":25775},{"style":1202},[25776],{"type":259,"value":20010},{"type":254,"tag":622,"props":25778,"children":25779},{"style":629},[25780],{"type":259,"value":594},{"type":254,"tag":622,"props":25782,"children":25783},{"style":1735},[25784],{"type":259,"value":25785},"get",{"type":254,"tag":622,"props":25787,"children":25788},{"style":1202},[25789],{"type":259,"value":1668},{"type":254,"tag":622,"props":25791,"children":25792},{"style":629},[25793],{"type":259,"value":652},{"type":254,"tag":622,"props":25795,"children":25796},{"style":664},[25797],{"type":259,"value":17148},{"type":254,"tag":622,"props":25799,"children":25800},{"style":629},[25801],{"type":259,"value":652},{"type":254,"tag":622,"props":25803,"children":25804},{"style":1202},[25805],{"type":259,"value":2309},{"type":254,"tag":622,"props":25807,"children":25808},{"style":629},[25809],{"type":259,"value":2350},{"type":254,"tag":255,"props":25811,"children":25812},{},[25813],{"type":259,"value":25814},"Or your might want to retrieve it from the current configuration of the Azure native provider :",{"type":254,"tag":612,"props":25816,"children":25818},{"className":24432,"code":25817,"language":24434,"meta":205,"style":205},"const azureConfig = pulumi.output(authorization.getClientConfig());\nconst subscriptionId = azureConfig.subscriptionId;\n",[25819],{"type":254,"tag":389,"props":25820,"children":25821},{"__ignoreMap":205},[25822,25873],{"type":254,"tag":622,"props":25823,"children":25824},{"class":624,"line":625},[25825,25829,25834,25838,25842,25846,25850,25855,25859,25864,25869],{"type":254,"tag":622,"props":25826,"children":25827},{"style":644},[25828],{"type":259,"value":24497},{"type":254,"tag":622,"props":25830,"children":25831},{"style":1202},[25832],{"type":259,"value":25833}," azureConfig ",{"type":254,"tag":622,"props":25835,"children":25836},{"style":629},[25837],{"type":259,"value":1654},{"type":254,"tag":622,"props":25839,"children":25840},{"style":1202},[25841],{"type":259,"value":25261},{"type":254,"tag":622,"props":25843,"children":25844},{"style":629},[25845],{"type":259,"value":594},{"type":254,"tag":622,"props":25847,"children":25848},{"style":1735},[25849],{"type":259,"value":24715},{"type":254,"tag":622,"props":25851,"children":25852},{"style":1202},[25853],{"type":259,"value":25854},"(authorization",{"type":254,"tag":622,"props":25856,"children":25857},{"style":629},[25858],{"type":259,"value":594},{"type":254,"tag":622,"props":25860,"children":25861},{"style":1735},[25862],{"type":259,"value":25863},"getClientConfig",{"type":254,"tag":622,"props":25865,"children":25866},{"style":1202},[25867],{"type":259,"value":25868},"())",{"type":254,"tag":622,"props":25870,"children":25871},{"style":629},[25872],{"type":259,"value":2350},{"type":254,"tag":622,"props":25874,"children":25875},{"class":624,"line":635},[25876,25880,25884,25888,25892,25896,25900],{"type":254,"tag":622,"props":25877,"children":25878},{"style":644},[25879],{"type":259,"value":24497},{"type":254,"tag":622,"props":25881,"children":25882},{"style":1202},[25883],{"type":259,"value":25768},{"type":254,"tag":622,"props":25885,"children":25886},{"style":629},[25887],{"type":259,"value":1654},{"type":254,"tag":622,"props":25889,"children":25890},{"style":1202},[25891],{"type":259,"value":21237},{"type":254,"tag":622,"props":25893,"children":25894},{"style":629},[25895],{"type":259,"value":594},{"type":254,"tag":622,"props":25897,"children":25898},{"style":1202},[25899],{"type":259,"value":17148},{"type":254,"tag":622,"props":25901,"children":25902},{"style":629},[25903],{"type":259,"value":2350},{"type":254,"tag":255,"props":25905,"children":25906},{},[25907,25909,25915,25917,25923],{"type":259,"value":25908},"Concerning, the contributor role definition identifier, I could have dynamically retrieved it using Azure APIs (like ",{"type":254,"tag":262,"props":25910,"children":25913},{"href":25911,"rel":25912},"https://github.com/pulumi/examples/blob/master/azure-ts-call-azure-sdk/index.ts",[266],[25914],{"type":259,"value":1414},{"type":259,"value":25916},"). But honestly, as these identifiers don't change it's much easier to hardcode it in a dedicated ",{"type":254,"tag":389,"props":25918,"children":25920},{"className":25919},[],[25921],{"type":259,"value":25922},"builtInRoles.ts",{"type":259,"value":3669},{"type":254,"tag":612,"props":25925,"children":25927},{"className":24432,"code":25926,"language":24434,"meta":205,"style":205},"export const azureBuiltInRoles = {\n  contributor : \"/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c\"\n};\n",[25928],{"type":254,"tag":389,"props":25929,"children":25930},{"__ignoreMap":205},[25931,25955,25980],{"type":254,"tag":622,"props":25932,"children":25933},{"class":624,"line":625},[25934,25938,25942,25947,25951],{"type":254,"tag":622,"props":25935,"children":25936},{"style":1196},[25937],{"type":259,"value":24651},{"type":254,"tag":622,"props":25939,"children":25940},{"style":644},[25941],{"type":259,"value":24656},{"type":254,"tag":622,"props":25943,"children":25944},{"style":1202},[25945],{"type":259,"value":25946}," azureBuiltInRoles ",{"type":254,"tag":622,"props":25948,"children":25949},{"style":629},[25950],{"type":259,"value":1654},{"type":254,"tag":622,"props":25952,"children":25953},{"style":629},[25954],{"type":259,"value":702},{"type":254,"tag":622,"props":25956,"children":25957},{"class":624,"line":635},[25958,25963,25967,25971,25976],{"type":254,"tag":622,"props":25959,"children":25960},{"style":768},[25961],{"type":259,"value":25962},"  contributor ",{"type":254,"tag":622,"props":25964,"children":25965},{"style":629},[25966],{"type":259,"value":474},{"type":254,"tag":622,"props":25968,"children":25969},{"style":629},[25970],{"type":259,"value":661},{"type":254,"tag":622,"props":25972,"children":25973},{"style":664},[25974],{"type":259,"value":25975},"/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c",{"type":254,"tag":622,"props":25977,"children":25978},{"style":629},[25979],{"type":259,"value":9909},{"type":254,"tag":622,"props":25981,"children":25982},{"class":624,"line":679},[25983],{"type":254,"tag":622,"props":25984,"children":25985},{"style":629},[25986],{"type":259,"value":1693},{"type":254,"tag":427,"props":25988,"children":25989},{"icon":567},[25990],{"type":254,"tag":255,"props":25991,"children":25992},{},[25993],{"type":259,"value":25994},"Please note that you don't have to work on the subscription scope. If you prefer to assign the contributor role (or any other role) to an existing resource group rather than the entire subscription, you can certainly do that as well.",{"type":254,"tag":455,"props":25996,"children":25998},{"id":25997},"add-the-configuration-for-the-github-actions-workflow",[25999],{"type":259,"value":26000},"Add the configuration for the GitHub Actions workflow",{"type":254,"tag":255,"props":26002,"children":26003},{},[26004],{"type":259,"value":26005},"The next step is to correctly set the configuration for the GitHub Actions of our Azure-Ready GitHub repository.",{"type":254,"tag":255,"props":26007,"children":26008},{},[26009],{"type":259,"value":26010},"The workflow requires three pieces of information for the OIDC authentication to function properly:",{"type":254,"tag":476,"props":26012,"children":26013},{},[26014,26018,26022],{"type":254,"tag":336,"props":26015,"children":26016},{},[26017],{"type":259,"value":18035},{"type":254,"tag":336,"props":26019,"children":26020},{},[26021],{"type":259,"value":18040},{"type":254,"tag":336,"props":26023,"children":26024},{},[26025],{"type":259,"value":26026},"The application identifier (also known as client ID) of the previously created Azure AD application",{"type":254,"tag":255,"props":26028,"children":26029},{},[26030],{"type":259,"value":26031},"These identifiers are not secrets, they are just identifiers so we could directly set them as GitHub Actions variables like this:",{"type":254,"tag":612,"props":26033,"children":26035},{"className":24432,"code":26034,"language":24434,"meta":205,"style":205},"new github.ActionsVariable(\"tenantId\", {\n  repository: repository.name,\n  variableName: \"ARM_TENANT_ID\",\n  value: azureConfig.tenantId,\n});\n",[26036],{"type":254,"tag":389,"props":26037,"children":26038},{"__ignoreMap":205},[26039,26083,26111,26140,26168],{"type":254,"tag":622,"props":26040,"children":26041},{"class":624,"line":625},[26042,26046,26050,26054,26059,26063,26067,26071,26075,26079],{"type":254,"tag":622,"props":26043,"children":26044},{"style":629},[26045],{"type":259,"value":21937},{"type":254,"tag":622,"props":26047,"children":26048},{"style":1202},[26049],{"type":259,"value":24515},{"type":254,"tag":622,"props":26051,"children":26052},{"style":629},[26053],{"type":259,"value":594},{"type":254,"tag":622,"props":26055,"children":26056},{"style":1735},[26057],{"type":259,"value":26058},"ActionsVariable",{"type":254,"tag":622,"props":26060,"children":26061},{"style":1202},[26062],{"type":259,"value":1668},{"type":254,"tag":622,"props":26064,"children":26065},{"style":629},[26066],{"type":259,"value":652},{"type":254,"tag":622,"props":26068,"children":26069},{"style":664},[26070],{"type":259,"value":17204},{"type":254,"tag":622,"props":26072,"children":26073},{"style":629},[26074],{"type":259,"value":652},{"type":254,"tag":622,"props":26076,"children":26077},{"style":629},[26078],{"type":259,"value":3149},{"type":254,"tag":622,"props":26080,"children":26081},{"style":629},[26082],{"type":259,"value":702},{"type":254,"tag":622,"props":26084,"children":26085},{"class":624,"line":635},[26086,26091,26095,26099,26103,26107],{"type":254,"tag":622,"props":26087,"children":26088},{"style":768},[26089],{"type":259,"value":26090},"  repository",{"type":254,"tag":622,"props":26092,"children":26093},{"style":629},[26094],{"type":259,"value":474},{"type":254,"tag":622,"props":26096,"children":26097},{"style":1202},[26098],{"type":259,"value":20498},{"type":254,"tag":622,"props":26100,"children":26101},{"style":629},[26102],{"type":259,"value":594},{"type":254,"tag":622,"props":26104,"children":26105},{"style":1202},[26106],{"type":259,"value":16394},{"type":254,"tag":622,"props":26108,"children":26109},{"style":629},[26110],{"type":259,"value":676},{"type":254,"tag":622,"props":26112,"children":26113},{"class":624,"line":679},[26114,26119,26123,26127,26132,26136],{"type":254,"tag":622,"props":26115,"children":26116},{"style":768},[26117],{"type":259,"value":26118},"  variableName",{"type":254,"tag":622,"props":26120,"children":26121},{"style":629},[26122],{"type":259,"value":474},{"type":254,"tag":622,"props":26124,"children":26125},{"style":629},[26126],{"type":259,"value":661},{"type":254,"tag":622,"props":26128,"children":26129},{"style":664},[26130],{"type":259,"value":26131},"ARM_TENANT_ID",{"type":254,"tag":622,"props":26133,"children":26134},{"style":629},[26135],{"type":259,"value":652},{"type":254,"tag":622,"props":26137,"children":26138},{"style":629},[26139],{"type":259,"value":676},{"type":254,"tag":622,"props":26141,"children":26142},{"class":624,"line":705},[26143,26148,26152,26156,26160,26164],{"type":254,"tag":622,"props":26144,"children":26145},{"style":768},[26146],{"type":259,"value":26147},"  value",{"type":254,"tag":622,"props":26149,"children":26150},{"style":629},[26151],{"type":259,"value":474},{"type":254,"tag":622,"props":26153,"children":26154},{"style":1202},[26155],{"type":259,"value":21237},{"type":254,"tag":622,"props":26157,"children":26158},{"style":629},[26159],{"type":259,"value":594},{"type":254,"tag":622,"props":26161,"children":26162},{"style":1202},[26163],{"type":259,"value":17204},{"type":254,"tag":622,"props":26165,"children":26166},{"style":629},[26167],{"type":259,"value":676},{"type":254,"tag":622,"props":26169,"children":26170},{"class":624,"line":732},[26171,26175,26179],{"type":254,"tag":622,"props":26172,"children":26173},{"style":629},[26174],{"type":259,"value":2421},{"type":254,"tag":622,"props":26176,"children":26177},{"style":1202},[26178],{"type":259,"value":2309},{"type":254,"tag":622,"props":26180,"children":26181},{"style":629},[26182],{"type":259,"value":2350},{"type":254,"tag":255,"props":26184,"children":26185},{},[26186],{"type":259,"value":26187},"However, I like to keep my tenant id and my subscription id private so we will store them in GitHub secrets but that's not mandatory at all.",{"type":254,"tag":612,"props":26189,"children":26191},{"className":24432,"code":26190,"language":24434,"meta":205,"style":205},"const azureConfig = pulumi.output(authorization.getClientConfig());\n\nnew github.ActionsSecret(\"tenantId\", {\n  repository: repository.name,\n  secretName: \"ARM_TENANT_ID\",\n  plaintextValue: azureConfig.tenantId,\n});\n\nnew github.ActionsSecret(\"subscriptionId\", {\n  repository: repository.name,\n  secretName: \"ARM_SUBSCRIPTION_ID\",\n  plaintextValue: azureConfig.subscriptionId,\n});\n\nnew github.ActionsSecret(\"clientId\", {\n  repository: repository.name,\n  secretName: \"ARM_CLIENT_ID\",\n  plaintextValue: aadApplication.applicationId,\n});\n",[26192],{"type":254,"tag":389,"props":26193,"children":26194},{"__ignoreMap":205},[26195,26242,26249,26293,26320,26348,26376,26391,26398,26441,26468,26496,26523,26538,26545,26588,26615,26643,26670],{"type":254,"tag":622,"props":26196,"children":26197},{"class":624,"line":625},[26198,26202,26206,26210,26214,26218,26222,26226,26230,26234,26238],{"type":254,"tag":622,"props":26199,"children":26200},{"style":644},[26201],{"type":259,"value":24497},{"type":254,"tag":622,"props":26203,"children":26204},{"style":1202},[26205],{"type":259,"value":25833},{"type":254,"tag":622,"props":26207,"children":26208},{"style":629},[26209],{"type":259,"value":1654},{"type":254,"tag":622,"props":26211,"children":26212},{"style":1202},[26213],{"type":259,"value":25261},{"type":254,"tag":622,"props":26215,"children":26216},{"style":629},[26217],{"type":259,"value":594},{"type":254,"tag":622,"props":26219,"children":26220},{"style":1735},[26221],{"type":259,"value":24715},{"type":254,"tag":622,"props":26223,"children":26224},{"style":1202},[26225],{"type":259,"value":25854},{"type":254,"tag":622,"props":26227,"children":26228},{"style":629},[26229],{"type":259,"value":594},{"type":254,"tag":622,"props":26231,"children":26232},{"style":1735},[26233],{"type":259,"value":25863},{"type":254,"tag":622,"props":26235,"children":26236},{"style":1202},[26237],{"type":259,"value":25868},{"type":254,"tag":622,"props":26239,"children":26240},{"style":629},[26241],{"type":259,"value":2350},{"type":254,"tag":622,"props":26243,"children":26244},{"class":624,"line":635},[26245],{"type":254,"tag":622,"props":26246,"children":26247},{"emptyLinePlaceholder":1699},[26248],{"type":259,"value":1702},{"type":254,"tag":622,"props":26250,"children":26251},{"class":624,"line":679},[26252,26256,26260,26264,26269,26273,26277,26281,26285,26289],{"type":254,"tag":622,"props":26253,"children":26254},{"style":629},[26255],{"type":259,"value":21937},{"type":254,"tag":622,"props":26257,"children":26258},{"style":1202},[26259],{"type":259,"value":24515},{"type":254,"tag":622,"props":26261,"children":26262},{"style":629},[26263],{"type":259,"value":594},{"type":254,"tag":622,"props":26265,"children":26266},{"style":1735},[26267],{"type":259,"value":26268},"ActionsSecret",{"type":254,"tag":622,"props":26270,"children":26271},{"style":1202},[26272],{"type":259,"value":1668},{"type":254,"tag":622,"props":26274,"children":26275},{"style":629},[26276],{"type":259,"value":652},{"type":254,"tag":622,"props":26278,"children":26279},{"style":664},[26280],{"type":259,"value":17204},{"type":254,"tag":622,"props":26282,"children":26283},{"style":629},[26284],{"type":259,"value":652},{"type":254,"tag":622,"props":26286,"children":26287},{"style":629},[26288],{"type":259,"value":3149},{"type":254,"tag":622,"props":26290,"children":26291},{"style":629},[26292],{"type":259,"value":702},{"type":254,"tag":622,"props":26294,"children":26295},{"class":624,"line":705},[26296,26300,26304,26308,26312,26316],{"type":254,"tag":622,"props":26297,"children":26298},{"style":768},[26299],{"type":259,"value":26090},{"type":254,"tag":622,"props":26301,"children":26302},{"style":629},[26303],{"type":259,"value":474},{"type":254,"tag":622,"props":26305,"children":26306},{"style":1202},[26307],{"type":259,"value":20498},{"type":254,"tag":622,"props":26309,"children":26310},{"style":629},[26311],{"type":259,"value":594},{"type":254,"tag":622,"props":26313,"children":26314},{"style":1202},[26315],{"type":259,"value":16394},{"type":254,"tag":622,"props":26317,"children":26318},{"style":629},[26319],{"type":259,"value":676},{"type":254,"tag":622,"props":26321,"children":26322},{"class":624,"line":732},[26323,26328,26332,26336,26340,26344],{"type":254,"tag":622,"props":26324,"children":26325},{"style":768},[26326],{"type":259,"value":26327},"  secretName",{"type":254,"tag":622,"props":26329,"children":26330},{"style":629},[26331],{"type":259,"value":474},{"type":254,"tag":622,"props":26333,"children":26334},{"style":629},[26335],{"type":259,"value":661},{"type":254,"tag":622,"props":26337,"children":26338},{"style":664},[26339],{"type":259,"value":26131},{"type":254,"tag":622,"props":26341,"children":26342},{"style":629},[26343],{"type":259,"value":652},{"type":254,"tag":622,"props":26345,"children":26346},{"style":629},[26347],{"type":259,"value":676},{"type":254,"tag":622,"props":26349,"children":26350},{"class":624,"line":759},[26351,26356,26360,26364,26368,26372],{"type":254,"tag":622,"props":26352,"children":26353},{"style":768},[26354],{"type":259,"value":26355},"  plaintextValue",{"type":254,"tag":622,"props":26357,"children":26358},{"style":629},[26359],{"type":259,"value":474},{"type":254,"tag":622,"props":26361,"children":26362},{"style":1202},[26363],{"type":259,"value":21237},{"type":254,"tag":622,"props":26365,"children":26366},{"style":629},[26367],{"type":259,"value":594},{"type":254,"tag":622,"props":26369,"children":26370},{"style":1202},[26371],{"type":259,"value":17204},{"type":254,"tag":622,"props":26373,"children":26374},{"style":629},[26375],{"type":259,"value":676},{"type":254,"tag":622,"props":26377,"children":26378},{"class":624,"line":798},[26379,26383,26387],{"type":254,"tag":622,"props":26380,"children":26381},{"style":629},[26382],{"type":259,"value":2421},{"type":254,"tag":622,"props":26384,"children":26385},{"style":1202},[26386],{"type":259,"value":2309},{"type":254,"tag":622,"props":26388,"children":26389},{"style":629},[26390],{"type":259,"value":2350},{"type":254,"tag":622,"props":26392,"children":26393},{"class":624,"line":836},[26394],{"type":254,"tag":622,"props":26395,"children":26396},{"emptyLinePlaceholder":1699},[26397],{"type":259,"value":1702},{"type":254,"tag":622,"props":26399,"children":26400},{"class":624,"line":862},[26401,26405,26409,26413,26417,26421,26425,26429,26433,26437],{"type":254,"tag":622,"props":26402,"children":26403},{"style":629},[26404],{"type":259,"value":21937},{"type":254,"tag":622,"props":26406,"children":26407},{"style":1202},[26408],{"type":259,"value":24515},{"type":254,"tag":622,"props":26410,"children":26411},{"style":629},[26412],{"type":259,"value":594},{"type":254,"tag":622,"props":26414,"children":26415},{"style":1735},[26416],{"type":259,"value":26268},{"type":254,"tag":622,"props":26418,"children":26419},{"style":1202},[26420],{"type":259,"value":1668},{"type":254,"tag":622,"props":26422,"children":26423},{"style":629},[26424],{"type":259,"value":652},{"type":254,"tag":622,"props":26426,"children":26427},{"style":664},[26428],{"type":259,"value":17148},{"type":254,"tag":622,"props":26430,"children":26431},{"style":629},[26432],{"type":259,"value":652},{"type":254,"tag":622,"props":26434,"children":26435},{"style":629},[26436],{"type":259,"value":3149},{"type":254,"tag":622,"props":26438,"children":26439},{"style":629},[26440],{"type":259,"value":702},{"type":254,"tag":622,"props":26442,"children":26443},{"class":624,"line":900},[26444,26448,26452,26456,26460,26464],{"type":254,"tag":622,"props":26445,"children":26446},{"style":768},[26447],{"type":259,"value":26090},{"type":254,"tag":622,"props":26449,"children":26450},{"style":629},[26451],{"type":259,"value":474},{"type":254,"tag":622,"props":26453,"children":26454},{"style":1202},[26455],{"type":259,"value":20498},{"type":254,"tag":622,"props":26457,"children":26458},{"style":629},[26459],{"type":259,"value":594},{"type":254,"tag":622,"props":26461,"children":26462},{"style":1202},[26463],{"type":259,"value":16394},{"type":254,"tag":622,"props":26465,"children":26466},{"style":629},[26467],{"type":259,"value":676},{"type":254,"tag":622,"props":26469,"children":26470},{"class":624,"line":938},[26471,26475,26479,26483,26488,26492],{"type":254,"tag":622,"props":26472,"children":26473},{"style":768},[26474],{"type":259,"value":26327},{"type":254,"tag":622,"props":26476,"children":26477},{"style":629},[26478],{"type":259,"value":474},{"type":254,"tag":622,"props":26480,"children":26481},{"style":629},[26482],{"type":259,"value":661},{"type":254,"tag":622,"props":26484,"children":26485},{"style":664},[26486],{"type":259,"value":26487},"ARM_SUBSCRIPTION_ID",{"type":254,"tag":622,"props":26489,"children":26490},{"style":629},[26491],{"type":259,"value":652},{"type":254,"tag":622,"props":26493,"children":26494},{"style":629},[26495],{"type":259,"value":676},{"type":254,"tag":622,"props":26497,"children":26498},{"class":624,"line":976},[26499,26503,26507,26511,26515,26519],{"type":254,"tag":622,"props":26500,"children":26501},{"style":768},[26502],{"type":259,"value":26355},{"type":254,"tag":622,"props":26504,"children":26505},{"style":629},[26506],{"type":259,"value":474},{"type":254,"tag":622,"props":26508,"children":26509},{"style":1202},[26510],{"type":259,"value":21237},{"type":254,"tag":622,"props":26512,"children":26513},{"style":629},[26514],{"type":259,"value":594},{"type":254,"tag":622,"props":26516,"children":26517},{"style":1202},[26518],{"type":259,"value":17148},{"type":254,"tag":622,"props":26520,"children":26521},{"style":629},[26522],{"type":259,"value":676},{"type":254,"tag":622,"props":26524,"children":26525},{"class":624,"line":1014},[26526,26530,26534],{"type":254,"tag":622,"props":26527,"children":26528},{"style":629},[26529],{"type":259,"value":2421},{"type":254,"tag":622,"props":26531,"children":26532},{"style":1202},[26533],{"type":259,"value":2309},{"type":254,"tag":622,"props":26535,"children":26536},{"style":629},[26537],{"type":259,"value":2350},{"type":254,"tag":622,"props":26539,"children":26540},{"class":624,"line":1052},[26541],{"type":254,"tag":622,"props":26542,"children":26543},{"emptyLinePlaceholder":1699},[26544],{"type":259,"value":1702},{"type":254,"tag":622,"props":26546,"children":26547},{"class":624,"line":1078},[26548,26552,26556,26560,26564,26568,26572,26576,26580,26584],{"type":254,"tag":622,"props":26549,"children":26550},{"style":629},[26551],{"type":259,"value":21937},{"type":254,"tag":622,"props":26553,"children":26554},{"style":1202},[26555],{"type":259,"value":24515},{"type":254,"tag":622,"props":26557,"children":26558},{"style":629},[26559],{"type":259,"value":594},{"type":254,"tag":622,"props":26561,"children":26562},{"style":1735},[26563],{"type":259,"value":26268},{"type":254,"tag":622,"props":26565,"children":26566},{"style":1202},[26567],{"type":259,"value":1668},{"type":254,"tag":622,"props":26569,"children":26570},{"style":629},[26571],{"type":259,"value":652},{"type":254,"tag":622,"props":26573,"children":26574},{"style":664},[26575],{"type":259,"value":551},{"type":254,"tag":622,"props":26577,"children":26578},{"style":629},[26579],{"type":259,"value":652},{"type":254,"tag":622,"props":26581,"children":26582},{"style":629},[26583],{"type":259,"value":3149},{"type":254,"tag":622,"props":26585,"children":26586},{"style":629},[26587],{"type":259,"value":702},{"type":254,"tag":622,"props":26589,"children":26590},{"class":624,"line":1087},[26591,26595,26599,26603,26607,26611],{"type":254,"tag":622,"props":26592,"children":26593},{"style":768},[26594],{"type":259,"value":26090},{"type":254,"tag":622,"props":26596,"children":26597},{"style":629},[26598],{"type":259,"value":474},{"type":254,"tag":622,"props":26600,"children":26601},{"style":1202},[26602],{"type":259,"value":20498},{"type":254,"tag":622,"props":26604,"children":26605},{"style":629},[26606],{"type":259,"value":594},{"type":254,"tag":622,"props":26608,"children":26609},{"style":1202},[26610],{"type":259,"value":16394},{"type":254,"tag":622,"props":26612,"children":26613},{"style":629},[26614],{"type":259,"value":676},{"type":254,"tag":622,"props":26616,"children":26617},{"class":624,"line":1096},[26618,26622,26626,26630,26635,26639],{"type":254,"tag":622,"props":26619,"children":26620},{"style":768},[26621],{"type":259,"value":26327},{"type":254,"tag":622,"props":26623,"children":26624},{"style":629},[26625],{"type":259,"value":474},{"type":254,"tag":622,"props":26627,"children":26628},{"style":629},[26629],{"type":259,"value":661},{"type":254,"tag":622,"props":26631,"children":26632},{"style":664},[26633],{"type":259,"value":26634},"ARM_CLIENT_ID",{"type":254,"tag":622,"props":26636,"children":26637},{"style":629},[26638],{"type":259,"value":652},{"type":254,"tag":622,"props":26640,"children":26641},{"style":629},[26642],{"type":259,"value":676},{"type":254,"tag":622,"props":26644,"children":26645},{"class":624,"line":1105},[26646,26650,26654,26658,26662,26666],{"type":254,"tag":622,"props":26647,"children":26648},{"style":768},[26649],{"type":259,"value":26355},{"type":254,"tag":622,"props":26651,"children":26652},{"style":629},[26653],{"type":259,"value":474},{"type":254,"tag":622,"props":26655,"children":26656},{"style":1202},[26657],{"type":259,"value":21698},{"type":254,"tag":622,"props":26659,"children":26660},{"style":629},[26661],{"type":259,"value":594},{"type":254,"tag":622,"props":26663,"children":26664},{"style":1202},[26665],{"type":259,"value":25003},{"type":254,"tag":622,"props":26667,"children":26668},{"style":629},[26669],{"type":259,"value":676},{"type":254,"tag":622,"props":26671,"children":26672},{"class":624,"line":4131},[26673,26677,26681],{"type":254,"tag":622,"props":26674,"children":26675},{"style":629},[26676],{"type":259,"value":2421},{"type":254,"tag":622,"props":26678,"children":26679},{"style":1202},[26680],{"type":259,"value":2309},{"type":254,"tag":622,"props":26682,"children":26683},{"style":629},[26684],{"type":259,"value":2350},{"type":254,"tag":427,"props":26686,"children":26687},{"icon":19646},[26688],{"type":254,"tag":255,"props":26689,"children":26690},{},[26691,26693,26700],{"type":259,"value":26692},"Please note that could also use ",{"type":254,"tag":262,"props":26694,"children":26697},{"href":26695,"rel":26696},"https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment",[266],[26698],{"type":259,"value":26699},"environments for deployment",{"type":259,"value":26701}," and their associated secrets and variables.",{"type":254,"tag":455,"props":26703,"children":26705},{"id":26704},"create-the-github-actions-workflow",[26706],{"type":259,"value":26707},"Create the GitHub Actions workflow",{"type":254,"tag":255,"props":26709,"children":26710},{},[26711],{"type":259,"value":26712},"Everything seems to be properly configured to provision Azure resources from a GitHub Actions workflow in this new repository, except for the workflow itself. The goal here is to have a properly configured pipeline in the repository to get started provisioning Azure infrastructure.",{"type":254,"tag":255,"props":26714,"children":26715},{},[26716],{"type":259,"value":26717},"Here is such a pipeline:",{"type":254,"tag":612,"props":26719,"children":26721},{"className":16381,"code":26720,"language":230,"meta":205,"style":205},"name: infra\n\non:\n  workflow_dispatch:\n\npermissions:\n      id-token: write\n      contents: read\njobs:\n  provision-infra:\n    runs-on: ubuntu-latest\n    steps:\n      - name: 'Az CLI login'\n        uses: azure/login@v1\n        with:\n          client-id: ${{ secrets.AZURE_CLIENT_ID }}\n          tenant-id: ${{ secrets.AZURE_TENANT_ID }}\n          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}\n\n      - name: 'Run az commands'\n        run: |\n          az account show\n          az group list\n",[26722],{"type":254,"tag":389,"props":26723,"children":26724},{"__ignoreMap":205},[26725,26741,26748,26759,26770,26777,26788,26804,26820,26831,26843,26858,26869,26896,26911,26922,26937,26952,26967,26974,27001,27016,27023],{"type":254,"tag":622,"props":26726,"children":26727},{"class":624,"line":625},[26728,26732,26736],{"type":254,"tag":622,"props":26729,"children":26730},{"style":768},[26731],{"type":259,"value":16394},{"type":254,"tag":622,"props":26733,"children":26734},{"style":629},[26735],{"type":259,"value":474},{"type":254,"tag":622,"props":26737,"children":26738},{"style":664},[26739],{"type":259,"value":26740}," infra\n",{"type":254,"tag":622,"props":26742,"children":26743},{"class":624,"line":635},[26744],{"type":254,"tag":622,"props":26745,"children":26746},{"emptyLinePlaceholder":1699},[26747],{"type":259,"value":1702},{"type":254,"tag":622,"props":26749,"children":26750},{"class":624,"line":679},[26751,26755],{"type":254,"tag":622,"props":26752,"children":26753},{"style":7715},[26754],{"type":259,"value":16411},{"type":254,"tag":622,"props":26756,"children":26757},{"style":629},[26758],{"type":259,"value":16416},{"type":254,"tag":622,"props":26760,"children":26761},{"class":624,"line":705},[26762,26766],{"type":254,"tag":622,"props":26763,"children":26764},{"style":768},[26765],{"type":259,"value":16424},{"type":254,"tag":622,"props":26767,"children":26768},{"style":629},[26769],{"type":259,"value":16416},{"type":254,"tag":622,"props":26771,"children":26772},{"class":624,"line":732},[26773],{"type":254,"tag":622,"props":26774,"children":26775},{"emptyLinePlaceholder":1699},[26776],{"type":259,"value":1702},{"type":254,"tag":622,"props":26778,"children":26779},{"class":624,"line":759},[26780,26784],{"type":254,"tag":622,"props":26781,"children":26782},{"style":768},[26783],{"type":259,"value":16443},{"type":254,"tag":622,"props":26785,"children":26786},{"style":629},[26787],{"type":259,"value":16416},{"type":254,"tag":622,"props":26789,"children":26790},{"class":624,"line":798},[26791,26796,26800],{"type":254,"tag":622,"props":26792,"children":26793},{"style":768},[26794],{"type":259,"value":26795},"      id-token",{"type":254,"tag":622,"props":26797,"children":26798},{"style":629},[26799],{"type":259,"value":474},{"type":254,"tag":622,"props":26801,"children":26802},{"style":664},[26803],{"type":259,"value":16464},{"type":254,"tag":622,"props":26805,"children":26806},{"class":624,"line":836},[26807,26812,26816],{"type":254,"tag":622,"props":26808,"children":26809},{"style":768},[26810],{"type":259,"value":26811},"      contents",{"type":254,"tag":622,"props":26813,"children":26814},{"style":629},[26815],{"type":259,"value":474},{"type":254,"tag":622,"props":26817,"children":26818},{"style":664},[26819],{"type":259,"value":16481},{"type":254,"tag":622,"props":26821,"children":26822},{"class":624,"line":862},[26823,26827],{"type":254,"tag":622,"props":26824,"children":26825},{"style":768},[26826],{"type":259,"value":16489},{"type":254,"tag":622,"props":26828,"children":26829},{"style":629},[26830],{"type":259,"value":16416},{"type":254,"tag":622,"props":26832,"children":26833},{"class":624,"line":900},[26834,26839],{"type":254,"tag":622,"props":26835,"children":26836},{"style":768},[26837],{"type":259,"value":26838},"  provision-infra",{"type":254,"tag":622,"props":26840,"children":26841},{"style":629},[26842],{"type":259,"value":16416},{"type":254,"tag":622,"props":26844,"children":26845},{"class":624,"line":938},[26846,26850,26854],{"type":254,"tag":622,"props":26847,"children":26848},{"style":768},[26849],{"type":259,"value":16513},{"type":254,"tag":622,"props":26851,"children":26852},{"style":629},[26853],{"type":259,"value":474},{"type":254,"tag":622,"props":26855,"children":26856},{"style":664},[26857],{"type":259,"value":16522},{"type":254,"tag":622,"props":26859,"children":26860},{"class":624,"line":976},[26861,26865],{"type":254,"tag":622,"props":26862,"children":26863},{"style":768},[26864],{"type":259,"value":16530},{"type":254,"tag":622,"props":26866,"children":26867},{"style":629},[26868],{"type":259,"value":16416},{"type":254,"tag":622,"props":26870,"children":26871},{"class":624,"line":1014},[26872,26876,26880,26884,26888,26892],{"type":254,"tag":622,"props":26873,"children":26874},{"style":629},[26875],{"type":259,"value":16542},{"type":254,"tag":622,"props":26877,"children":26878},{"style":768},[26879],{"type":259,"value":16547},{"type":254,"tag":622,"props":26881,"children":26882},{"style":629},[26883],{"type":259,"value":474},{"type":254,"tag":622,"props":26885,"children":26886},{"style":629},[26887],{"type":259,"value":16556},{"type":254,"tag":622,"props":26889,"children":26890},{"style":664},[26891],{"type":259,"value":16561},{"type":254,"tag":622,"props":26893,"children":26894},{"style":629},[26895],{"type":259,"value":16566},{"type":254,"tag":622,"props":26897,"children":26898},{"class":624,"line":1052},[26899,26903,26907],{"type":254,"tag":622,"props":26900,"children":26901},{"style":768},[26902],{"type":259,"value":16574},{"type":254,"tag":622,"props":26904,"children":26905},{"style":629},[26906],{"type":259,"value":474},{"type":254,"tag":622,"props":26908,"children":26909},{"style":664},[26910],{"type":259,"value":16583},{"type":254,"tag":622,"props":26912,"children":26913},{"class":624,"line":1078},[26914,26918],{"type":254,"tag":622,"props":26915,"children":26916},{"style":768},[26917],{"type":259,"value":16591},{"type":254,"tag":622,"props":26919,"children":26920},{"style":629},[26921],{"type":259,"value":16416},{"type":254,"tag":622,"props":26923,"children":26924},{"class":624,"line":1087},[26925,26929,26933],{"type":254,"tag":622,"props":26926,"children":26927},{"style":768},[26928],{"type":259,"value":16603},{"type":254,"tag":622,"props":26930,"children":26931},{"style":629},[26932],{"type":259,"value":474},{"type":254,"tag":622,"props":26934,"children":26935},{"style":664},[26936],{"type":259,"value":16612},{"type":254,"tag":622,"props":26938,"children":26939},{"class":624,"line":1096},[26940,26944,26948],{"type":254,"tag":622,"props":26941,"children":26942},{"style":768},[26943],{"type":259,"value":16620},{"type":254,"tag":622,"props":26945,"children":26946},{"style":629},[26947],{"type":259,"value":474},{"type":254,"tag":622,"props":26949,"children":26950},{"style":664},[26951],{"type":259,"value":16629},{"type":254,"tag":622,"props":26953,"children":26954},{"class":624,"line":1105},[26955,26959,26963],{"type":254,"tag":622,"props":26956,"children":26957},{"style":768},[26958],{"type":259,"value":16637},{"type":254,"tag":622,"props":26960,"children":26961},{"style":629},[26962],{"type":259,"value":474},{"type":254,"tag":622,"props":26964,"children":26965},{"style":664},[26966],{"type":259,"value":16646},{"type":254,"tag":622,"props":26968,"children":26969},{"class":624,"line":4131},[26970],{"type":254,"tag":622,"props":26971,"children":26972},{"emptyLinePlaceholder":1699},[26973],{"type":259,"value":1702},{"type":254,"tag":622,"props":26975,"children":26976},{"class":624,"line":4183},[26977,26981,26985,26989,26993,26997],{"type":254,"tag":622,"props":26978,"children":26979},{"style":629},[26980],{"type":259,"value":16542},{"type":254,"tag":622,"props":26982,"children":26983},{"style":768},[26984],{"type":259,"value":16547},{"type":254,"tag":622,"props":26986,"children":26987},{"style":629},[26988],{"type":259,"value":474},{"type":254,"tag":622,"props":26990,"children":26991},{"style":629},[26992],{"type":259,"value":16556},{"type":254,"tag":622,"props":26994,"children":26995},{"style":664},[26996],{"type":259,"value":16677},{"type":254,"tag":622,"props":26998,"children":26999},{"style":629},[27000],{"type":259,"value":16566},{"type":254,"tag":622,"props":27002,"children":27003},{"class":624,"line":4208},[27004,27008,27012],{"type":254,"tag":622,"props":27005,"children":27006},{"style":768},[27007],{"type":259,"value":16689},{"type":254,"tag":622,"props":27009,"children":27010},{"style":629},[27011],{"type":259,"value":474},{"type":254,"tag":622,"props":27013,"children":27014},{"style":1196},[27015],{"type":259,"value":16698},{"type":254,"tag":622,"props":27017,"children":27018},{"class":624,"line":4216},[27019],{"type":254,"tag":622,"props":27020,"children":27021},{"style":664},[27022],{"type":259,"value":16706},{"type":254,"tag":622,"props":27024,"children":27025},{"class":624,"line":4224},[27026],{"type":254,"tag":622,"props":27027,"children":27028},{"style":664},[27029],{"type":259,"value":16714},{"type":254,"tag":255,"props":27031,"children":27032},{},[27033,27035,27041],{"type":259,"value":27034},"This workflow first authenticates to Azure using OIDC with the ",{"type":254,"tag":389,"props":27036,"children":27038},{"className":27037},[],[27039],{"type":259,"value":27040},"azure/login",{"type":259,"value":27042}," action and then performs some Azure CLI commands to interact with Azure resources. That's fine and probably enough to get you started but you surely want to provision your infrastructure using a more declarative solution than an Azure CLI script. So let's see a more interesting pipeline still authenticating via Azure OIDC but using Pulumi to provision the Azure resources.",{"type":254,"tag":612,"props":27044,"children":27046},{"className":16381,"code":27045,"language":230,"meta":205,"style":205},"name: infra\n\non:\n  workflow_dispatch:\n\npermissions:\n  id-token: write   # required for OIDC auth\n  contents: read    # required to perform a checkout\n\njobs:\n  provision-infra:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n\n      - name: Install pnpm\n        uses: pnpm/action-setup@v2\n        with:\n          version: latest\n\n      - name: Set node version to 18\n        uses: actions/setup-node@v3\n        with:\n          node-version: 18\n          cache: 'pnpm'\n      \n      - name: Install dependencies\n        run: pnpm install\n      \n      - name: Provision infrastructure\n        uses: pulumi/actions@v4.4.0\n        id: pulumi\n        with:\n          command: up\n          stack-name: dev\n        env:\n          ARM_USE_OIDC: true\n          PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}\n          ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}\n          ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}\n          ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} \n",[27047],{"type":254,"tag":389,"props":27048,"children":27049},{"__ignoreMap":205},[27050,27065,27072,27083,27094,27101,27112,27133,27154,27161,27172,27183,27198,27209,27230,27237,27257,27273,27284,27301,27308,27328,27344,27355,27372,27396,27404,27424,27440,27447,27467,27483,27500,27511,27528,27545,27557,27573,27590,27607,27624],{"type":254,"tag":622,"props":27051,"children":27052},{"class":624,"line":625},[27053,27057,27061],{"type":254,"tag":622,"props":27054,"children":27055},{"style":768},[27056],{"type":259,"value":16394},{"type":254,"tag":622,"props":27058,"children":27059},{"style":629},[27060],{"type":259,"value":474},{"type":254,"tag":622,"props":27062,"children":27063},{"style":664},[27064],{"type":259,"value":26740},{"type":254,"tag":622,"props":27066,"children":27067},{"class":624,"line":635},[27068],{"type":254,"tag":622,"props":27069,"children":27070},{"emptyLinePlaceholder":1699},[27071],{"type":259,"value":1702},{"type":254,"tag":622,"props":27073,"children":27074},{"class":624,"line":679},[27075,27079],{"type":254,"tag":622,"props":27076,"children":27077},{"style":7715},[27078],{"type":259,"value":16411},{"type":254,"tag":622,"props":27080,"children":27081},{"style":629},[27082],{"type":259,"value":16416},{"type":254,"tag":622,"props":27084,"children":27085},{"class":624,"line":705},[27086,27090],{"type":254,"tag":622,"props":27087,"children":27088},{"style":768},[27089],{"type":259,"value":16424},{"type":254,"tag":622,"props":27091,"children":27092},{"style":629},[27093],{"type":259,"value":16416},{"type":254,"tag":622,"props":27095,"children":27096},{"class":624,"line":732},[27097],{"type":254,"tag":622,"props":27098,"children":27099},{"emptyLinePlaceholder":1699},[27100],{"type":259,"value":1702},{"type":254,"tag":622,"props":27102,"children":27103},{"class":624,"line":759},[27104,27108],{"type":254,"tag":622,"props":27105,"children":27106},{"style":768},[27107],{"type":259,"value":16443},{"type":254,"tag":622,"props":27109,"children":27110},{"style":629},[27111],{"type":259,"value":16416},{"type":254,"tag":622,"props":27113,"children":27114},{"class":624,"line":798},[27115,27119,27123,27128],{"type":254,"tag":622,"props":27116,"children":27117},{"style":768},[27118],{"type":259,"value":16455},{"type":254,"tag":622,"props":27120,"children":27121},{"style":629},[27122],{"type":259,"value":474},{"type":254,"tag":622,"props":27124,"children":27125},{"style":664},[27126],{"type":259,"value":27127}," write",{"type":254,"tag":622,"props":27129,"children":27130},{"style":3195},[27131],{"type":259,"value":27132},"   # required for OIDC auth\n",{"type":254,"tag":622,"props":27134,"children":27135},{"class":624,"line":836},[27136,27140,27144,27149],{"type":254,"tag":622,"props":27137,"children":27138},{"style":768},[27139],{"type":259,"value":16472},{"type":254,"tag":622,"props":27141,"children":27142},{"style":629},[27143],{"type":259,"value":474},{"type":254,"tag":622,"props":27145,"children":27146},{"style":664},[27147],{"type":259,"value":27148}," read",{"type":254,"tag":622,"props":27150,"children":27151},{"style":3195},[27152],{"type":259,"value":27153},"    # required to perform a checkout\n",{"type":254,"tag":622,"props":27155,"children":27156},{"class":624,"line":862},[27157],{"type":254,"tag":622,"props":27158,"children":27159},{"emptyLinePlaceholder":1699},[27160],{"type":259,"value":1702},{"type":254,"tag":622,"props":27162,"children":27163},{"class":624,"line":900},[27164,27168],{"type":254,"tag":622,"props":27165,"children":27166},{"style":768},[27167],{"type":259,"value":16489},{"type":254,"tag":622,"props":27169,"children":27170},{"style":629},[27171],{"type":259,"value":16416},{"type":254,"tag":622,"props":27173,"children":27174},{"class":624,"line":938},[27175,27179],{"type":254,"tag":622,"props":27176,"children":27177},{"style":768},[27178],{"type":259,"value":26838},{"type":254,"tag":622,"props":27180,"children":27181},{"style":629},[27182],{"type":259,"value":16416},{"type":254,"tag":622,"props":27184,"children":27185},{"class":624,"line":976},[27186,27190,27194],{"type":254,"tag":622,"props":27187,"children":27188},{"style":768},[27189],{"type":259,"value":16513},{"type":254,"tag":622,"props":27191,"children":27192},{"style":629},[27193],{"type":259,"value":474},{"type":254,"tag":622,"props":27195,"children":27196},{"style":664},[27197],{"type":259,"value":16522},{"type":254,"tag":622,"props":27199,"children":27200},{"class":624,"line":1014},[27201,27205],{"type":254,"tag":622,"props":27202,"children":27203},{"style":768},[27204],{"type":259,"value":16530},{"type":254,"tag":622,"props":27206,"children":27207},{"style":629},[27208],{"type":259,"value":16416},{"type":254,"tag":622,"props":27210,"children":27211},{"class":624,"line":1052},[27212,27216,27221,27225],{"type":254,"tag":622,"props":27213,"children":27214},{"style":629},[27215],{"type":259,"value":16542},{"type":254,"tag":622,"props":27217,"children":27218},{"style":768},[27219],{"type":259,"value":27220}," uses",{"type":254,"tag":622,"props":27222,"children":27223},{"style":629},[27224],{"type":259,"value":474},{"type":254,"tag":622,"props":27226,"children":27227},{"style":664},[27228],{"type":259,"value":27229}," actions/checkout@v3\n",{"type":254,"tag":622,"props":27231,"children":27232},{"class":624,"line":1078},[27233],{"type":254,"tag":622,"props":27234,"children":27235},{"emptyLinePlaceholder":1699},[27236],{"type":259,"value":1702},{"type":254,"tag":622,"props":27238,"children":27239},{"class":624,"line":1087},[27240,27244,27248,27252],{"type":254,"tag":622,"props":27241,"children":27242},{"style":629},[27243],{"type":259,"value":16542},{"type":254,"tag":622,"props":27245,"children":27246},{"style":768},[27247],{"type":259,"value":16547},{"type":254,"tag":622,"props":27249,"children":27250},{"style":629},[27251],{"type":259,"value":474},{"type":254,"tag":622,"props":27253,"children":27254},{"style":664},[27255],{"type":259,"value":27256}," Install pnpm\n",{"type":254,"tag":622,"props":27258,"children":27259},{"class":624,"line":1096},[27260,27264,27268],{"type":254,"tag":622,"props":27261,"children":27262},{"style":768},[27263],{"type":259,"value":16574},{"type":254,"tag":622,"props":27265,"children":27266},{"style":629},[27267],{"type":259,"value":474},{"type":254,"tag":622,"props":27269,"children":27270},{"style":664},[27271],{"type":259,"value":27272}," pnpm/action-setup@v2\n",{"type":254,"tag":622,"props":27274,"children":27275},{"class":624,"line":1105},[27276,27280],{"type":254,"tag":622,"props":27277,"children":27278},{"style":768},[27279],{"type":259,"value":16591},{"type":254,"tag":622,"props":27281,"children":27282},{"style":629},[27283],{"type":259,"value":16416},{"type":254,"tag":622,"props":27285,"children":27286},{"class":624,"line":4131},[27287,27292,27296],{"type":254,"tag":622,"props":27288,"children":27289},{"style":768},[27290],{"type":259,"value":27291},"          version",{"type":254,"tag":622,"props":27293,"children":27294},{"style":629},[27295],{"type":259,"value":474},{"type":254,"tag":622,"props":27297,"children":27298},{"style":664},[27299],{"type":259,"value":27300}," latest\n",{"type":254,"tag":622,"props":27302,"children":27303},{"class":624,"line":4183},[27304],{"type":254,"tag":622,"props":27305,"children":27306},{"emptyLinePlaceholder":1699},[27307],{"type":259,"value":1702},{"type":254,"tag":622,"props":27309,"children":27310},{"class":624,"line":4208},[27311,27315,27319,27323],{"type":254,"tag":622,"props":27312,"children":27313},{"style":629},[27314],{"type":259,"value":16542},{"type":254,"tag":622,"props":27316,"children":27317},{"style":768},[27318],{"type":259,"value":16547},{"type":254,"tag":622,"props":27320,"children":27321},{"style":629},[27322],{"type":259,"value":474},{"type":254,"tag":622,"props":27324,"children":27325},{"style":664},[27326],{"type":259,"value":27327}," Set node version to 18\n",{"type":254,"tag":622,"props":27329,"children":27330},{"class":624,"line":4216},[27331,27335,27339],{"type":254,"tag":622,"props":27332,"children":27333},{"style":768},[27334],{"type":259,"value":16574},{"type":254,"tag":622,"props":27336,"children":27337},{"style":629},[27338],{"type":259,"value":474},{"type":254,"tag":622,"props":27340,"children":27341},{"style":664},[27342],{"type":259,"value":27343}," actions/setup-node@v3\n",{"type":254,"tag":622,"props":27345,"children":27346},{"class":624,"line":4224},[27347,27351],{"type":254,"tag":622,"props":27348,"children":27349},{"style":768},[27350],{"type":259,"value":16591},{"type":254,"tag":622,"props":27352,"children":27353},{"style":629},[27354],{"type":259,"value":16416},{"type":254,"tag":622,"props":27356,"children":27357},{"class":624,"line":4268},[27358,27363,27367],{"type":254,"tag":622,"props":27359,"children":27360},{"style":768},[27361],{"type":259,"value":27362},"          node-version",{"type":254,"tag":622,"props":27364,"children":27365},{"style":629},[27366],{"type":259,"value":474},{"type":254,"tag":622,"props":27368,"children":27369},{"style":741},[27370],{"type":259,"value":27371}," 18\n",{"type":254,"tag":622,"props":27373,"children":27374},{"class":624,"line":4276},[27375,27380,27384,27388,27392],{"type":254,"tag":622,"props":27376,"children":27377},{"style":768},[27378],{"type":259,"value":27379},"          cache",{"type":254,"tag":622,"props":27381,"children":27382},{"style":629},[27383],{"type":259,"value":474},{"type":254,"tag":622,"props":27385,"children":27386},{"style":629},[27387],{"type":259,"value":16556},{"type":254,"tag":622,"props":27389,"children":27390},{"style":664},[27391],{"type":259,"value":24281},{"type":254,"tag":622,"props":27393,"children":27394},{"style":629},[27395],{"type":259,"value":16566},{"type":254,"tag":622,"props":27397,"children":27398},{"class":624,"line":4332},[27399],{"type":254,"tag":622,"props":27400,"children":27401},{"style":1202},[27402],{"type":259,"value":27403},"      \n",{"type":254,"tag":622,"props":27405,"children":27406},{"class":624,"line":4376},[27407,27411,27415,27419],{"type":254,"tag":622,"props":27408,"children":27409},{"style":629},[27410],{"type":259,"value":16542},{"type":254,"tag":622,"props":27412,"children":27413},{"style":768},[27414],{"type":259,"value":16547},{"type":254,"tag":622,"props":27416,"children":27417},{"style":629},[27418],{"type":259,"value":474},{"type":254,"tag":622,"props":27420,"children":27421},{"style":664},[27422],{"type":259,"value":27423}," Install dependencies\n",{"type":254,"tag":622,"props":27425,"children":27426},{"class":624,"line":4384},[27427,27431,27435],{"type":254,"tag":622,"props":27428,"children":27429},{"style":768},[27430],{"type":259,"value":16689},{"type":254,"tag":622,"props":27432,"children":27433},{"style":629},[27434],{"type":259,"value":474},{"type":254,"tag":622,"props":27436,"children":27437},{"style":664},[27438],{"type":259,"value":27439}," pnpm install\n",{"type":254,"tag":622,"props":27441,"children":27442},{"class":624,"line":4416},[27443],{"type":254,"tag":622,"props":27444,"children":27445},{"style":1202},[27446],{"type":259,"value":27403},{"type":254,"tag":622,"props":27448,"children":27449},{"class":624,"line":4456},[27450,27454,27458,27462],{"type":254,"tag":622,"props":27451,"children":27452},{"style":629},[27453],{"type":259,"value":16542},{"type":254,"tag":622,"props":27455,"children":27456},{"style":768},[27457],{"type":259,"value":16547},{"type":254,"tag":622,"props":27459,"children":27460},{"style":629},[27461],{"type":259,"value":474},{"type":254,"tag":622,"props":27463,"children":27464},{"style":664},[27465],{"type":259,"value":27466}," Provision infrastructure\n",{"type":254,"tag":622,"props":27468,"children":27469},{"class":624,"line":4488},[27470,27474,27478],{"type":254,"tag":622,"props":27471,"children":27472},{"style":768},[27473],{"type":259,"value":16574},{"type":254,"tag":622,"props":27475,"children":27476},{"style":629},[27477],{"type":259,"value":474},{"type":254,"tag":622,"props":27479,"children":27480},{"style":664},[27481],{"type":259,"value":27482}," pulumi/actions@v4.4.0\n",{"type":254,"tag":622,"props":27484,"children":27485},{"class":624,"line":4496},[27486,27491,27495],{"type":254,"tag":622,"props":27487,"children":27488},{"style":768},[27489],{"type":259,"value":27490},"        id",{"type":254,"tag":622,"props":27492,"children":27493},{"style":629},[27494],{"type":259,"value":474},{"type":254,"tag":622,"props":27496,"children":27497},{"style":664},[27498],{"type":259,"value":27499}," pulumi\n",{"type":254,"tag":622,"props":27501,"children":27502},{"class":624,"line":4564},[27503,27507],{"type":254,"tag":622,"props":27504,"children":27505},{"style":768},[27506],{"type":259,"value":16591},{"type":254,"tag":622,"props":27508,"children":27509},{"style":629},[27510],{"type":259,"value":16416},{"type":254,"tag":622,"props":27512,"children":27513},{"class":624,"line":4572},[27514,27519,27523],{"type":254,"tag":622,"props":27515,"children":27516},{"style":768},[27517],{"type":259,"value":27518},"          command",{"type":254,"tag":622,"props":27520,"children":27521},{"style":629},[27522],{"type":259,"value":474},{"type":254,"tag":622,"props":27524,"children":27525},{"style":664},[27526],{"type":259,"value":27527}," up\n",{"type":254,"tag":622,"props":27529,"children":27530},{"class":624,"line":4644},[27531,27536,27540],{"type":254,"tag":622,"props":27532,"children":27533},{"style":768},[27534],{"type":259,"value":27535},"          stack-name",{"type":254,"tag":622,"props":27537,"children":27538},{"style":629},[27539],{"type":259,"value":474},{"type":254,"tag":622,"props":27541,"children":27542},{"style":664},[27543],{"type":259,"value":27544}," dev\n",{"type":254,"tag":622,"props":27546,"children":27547},{"class":624,"line":4652},[27548,27553],{"type":254,"tag":622,"props":27549,"children":27550},{"style":768},[27551],{"type":259,"value":27552},"        env",{"type":254,"tag":622,"props":27554,"children":27555},{"style":629},[27556],{"type":259,"value":16416},{"type":254,"tag":622,"props":27558,"children":27559},{"class":624,"line":4660},[27560,27565,27569],{"type":254,"tag":622,"props":27561,"children":27562},{"style":768},[27563],{"type":259,"value":27564},"          ARM_USE_OIDC",{"type":254,"tag":622,"props":27566,"children":27567},{"style":629},[27568],{"type":259,"value":474},{"type":254,"tag":622,"props":27570,"children":27571},{"style":7715},[27572],{"type":259,"value":1075},{"type":254,"tag":622,"props":27574,"children":27575},{"class":624,"line":4692},[27576,27581,27585],{"type":254,"tag":622,"props":27577,"children":27578},{"style":768},[27579],{"type":259,"value":27580},"          PULUMI_ACCESS_TOKEN",{"type":254,"tag":622,"props":27582,"children":27583},{"style":629},[27584],{"type":259,"value":474},{"type":254,"tag":622,"props":27586,"children":27587},{"style":664},[27588],{"type":259,"value":27589}," ${{ secrets.PULUMI_ACCESS_TOKEN }}\n",{"type":254,"tag":622,"props":27591,"children":27592},{"class":624,"line":4700},[27593,27598,27602],{"type":254,"tag":622,"props":27594,"children":27595},{"style":768},[27596],{"type":259,"value":27597},"          ARM_CLIENT_ID",{"type":254,"tag":622,"props":27599,"children":27600},{"style":629},[27601],{"type":259,"value":474},{"type":254,"tag":622,"props":27603,"children":27604},{"style":664},[27605],{"type":259,"value":27606}," ${{ secrets.ARM_CLIENT_ID }}\n",{"type":254,"tag":622,"props":27608,"children":27609},{"class":624,"line":4756},[27610,27615,27619],{"type":254,"tag":622,"props":27611,"children":27612},{"style":768},[27613],{"type":259,"value":27614},"          ARM_TENANT_ID",{"type":254,"tag":622,"props":27616,"children":27617},{"style":629},[27618],{"type":259,"value":474},{"type":254,"tag":622,"props":27620,"children":27621},{"style":664},[27622],{"type":259,"value":27623}," ${{ secrets.ARM_TENANT_ID }}\n",{"type":254,"tag":622,"props":27625,"children":27626},{"class":624,"line":4808},[27627,27632,27636],{"type":254,"tag":622,"props":27628,"children":27629},{"style":768},[27630],{"type":259,"value":27631},"          ARM_SUBSCRIPTION_ID",{"type":254,"tag":622,"props":27633,"children":27634},{"style":629},[27635],{"type":259,"value":474},{"type":254,"tag":622,"props":27637,"children":27638},{"style":664},[27639],{"type":259,"value":27640}," ${{ secrets.ARM_SUBSCRIPTION_ID }}\n",{"type":254,"tag":255,"props":27642,"children":27643},{},[27644,27646,27652],{"type":259,"value":27645},"A permission section is required with 2 settings (more details ",{"type":254,"tag":262,"props":27647,"children":27650},{"href":27648,"rel":27649},"https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings",[266],[27651],{"type":259,"value":1414},{"type":259,"value":27653},"):",{"type":254,"tag":332,"props":27655,"children":27656},{},[27657,27668],{"type":254,"tag":336,"props":27658,"children":27659},{},[27660,27666],{"type":254,"tag":389,"props":27661,"children":27663},{"className":27662},[],[27664],{"type":259,"value":27665},"id-token: write",{"type":259,"value":27667}," ➡️ needed to request the OIDC token",{"type":254,"tag":336,"props":27669,"children":27670},{},[27671,27677],{"type":254,"tag":389,"props":27672,"children":27674},{"className":27673},[],[27675],{"type":259,"value":27676},"contents: read",{"type":259,"value":27678}," ➡️ needed to perform checkout action",{"type":254,"tag":427,"props":27680,"children":27681},{"icon":19646},[27682],{"type":254,"tag":255,"props":27683,"children":27684},{},[27685],{"type":259,"value":27686},"When you start to specify specific permissions, you have to specify all the permissions you need for the job because the default permissions won't apply anymore.",{"type":254,"tag":255,"props":27688,"children":27689},{},[27690,27692,27697,27699,27705,27707,27713],{"type":259,"value":27691},"The 3 steps following the checkout step are actions to specify the Node.js version to use, install and correctly configure ",{"type":254,"tag":262,"props":27693,"children":27695},{"href":10371,"rel":27694},[266],[27696],{"type":259,"value":24281},{"type":259,"value":27698},". We assume here the infrastructure will be provisioned using TypeScript (and Pulumi of course) but there would have been similar steps with other runtimes/languages (a ",{"type":254,"tag":389,"props":27700,"children":27702},{"className":27701},[],[27703],{"type":259,"value":27704},"setup-dotnet",{"type":259,"value":27706}," and a ",{"type":254,"tag":389,"props":27708,"children":27710},{"className":27709},[],[27711],{"type":259,"value":27712},"dotnet retore",{"type":259,"value":27714}," action for .NET for instance).",{"type":254,"tag":255,"props":27716,"children":27717},{},[27718,27720,27725,27727,27732,27734,27740,27742,27747],{"type":259,"value":27719},"The last action is the Pulumi action to provision the infrastructure by running the ",{"type":254,"tag":389,"props":27721,"children":27723},{"className":27722},[],[27724],{"type":259,"value":23629},{"type":259,"value":27726}," on the ",{"type":254,"tag":389,"props":27728,"children":27730},{"className":27729},[],[27731],{"type":259,"value":19878},{"type":259,"value":27733}," stack. We can see that this action uses environment variables whose values are based on the GitHub Actions secrets we defined earlier. To tell Pulumi to use OIDC, we just have to set the ",{"type":254,"tag":389,"props":27735,"children":27737},{"className":27736},[],[27738],{"type":259,"value":27739},"ARM_USE_OIDC",{"type":259,"value":27741}," environment variable to ",{"type":254,"tag":389,"props":27743,"children":27745},{"className":27744},[],[27746],{"type":259,"value":24747},{"type":259,"value":594},{"type":254,"tag":612,"props":27749,"children":27751},{"className":16381,"code":27750,"language":230,"meta":205,"style":205},"        env:\n          ARM_USE_OIDC: true\n          PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}\n          ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}\n          ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}\n          ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} \n",[27752],{"type":254,"tag":389,"props":27753,"children":27754},{"__ignoreMap":205},[27755,27766,27781,27796,27811,27826],{"type":254,"tag":622,"props":27756,"children":27757},{"class":624,"line":625},[27758,27762],{"type":254,"tag":622,"props":27759,"children":27760},{"style":768},[27761],{"type":259,"value":27552},{"type":254,"tag":622,"props":27763,"children":27764},{"style":629},[27765],{"type":259,"value":16416},{"type":254,"tag":622,"props":27767,"children":27768},{"class":624,"line":635},[27769,27773,27777],{"type":254,"tag":622,"props":27770,"children":27771},{"style":768},[27772],{"type":259,"value":27564},{"type":254,"tag":622,"props":27774,"children":27775},{"style":629},[27776],{"type":259,"value":474},{"type":254,"tag":622,"props":27778,"children":27779},{"style":7715},[27780],{"type":259,"value":1075},{"type":254,"tag":622,"props":27782,"children":27783},{"class":624,"line":679},[27784,27788,27792],{"type":254,"tag":622,"props":27785,"children":27786},{"style":768},[27787],{"type":259,"value":27580},{"type":254,"tag":622,"props":27789,"children":27790},{"style":629},[27791],{"type":259,"value":474},{"type":254,"tag":622,"props":27793,"children":27794},{"style":664},[27795],{"type":259,"value":27589},{"type":254,"tag":622,"props":27797,"children":27798},{"class":624,"line":705},[27799,27803,27807],{"type":254,"tag":622,"props":27800,"children":27801},{"style":768},[27802],{"type":259,"value":27597},{"type":254,"tag":622,"props":27804,"children":27805},{"style":629},[27806],{"type":259,"value":474},{"type":254,"tag":622,"props":27808,"children":27809},{"style":664},[27810],{"type":259,"value":27606},{"type":254,"tag":622,"props":27812,"children":27813},{"class":624,"line":732},[27814,27818,27822],{"type":254,"tag":622,"props":27815,"children":27816},{"style":768},[27817],{"type":259,"value":27614},{"type":254,"tag":622,"props":27819,"children":27820},{"style":629},[27821],{"type":259,"value":474},{"type":254,"tag":622,"props":27823,"children":27824},{"style":664},[27825],{"type":259,"value":27623},{"type":254,"tag":622,"props":27827,"children":27828},{"class":624,"line":759},[27829,27833,27837],{"type":254,"tag":622,"props":27830,"children":27831},{"style":768},[27832],{"type":259,"value":27631},{"type":254,"tag":622,"props":27834,"children":27835},{"style":629},[27836],{"type":259,"value":474},{"type":254,"tag":622,"props":27838,"children":27839},{"style":664},[27840],{"type":259,"value":27640},{"type":254,"tag":255,"props":27842,"children":27843},{},[27844,27846,27852,27854,27861],{"type":259,"value":27845},"A GitHub Actions secret we did not talk about is ",{"type":254,"tag":389,"props":27847,"children":27849},{"className":27848},[],[27850],{"type":259,"value":27851},"PULUMI_ACCESS_TOKEN",{"type":259,"value":27853}," that is a ",{"type":254,"tag":262,"props":27855,"children":27858},{"href":27856,"rel":27857},"https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/",[266],[27859],{"type":259,"value":27860},"Pulumi access token",{"type":259,"value":27862}," to use Pulumi Cloud as our backend to store the infrastructure state and encrypt secrets. This token should be:",{"type":254,"tag":476,"props":27864,"children":27865},{},[27866,27878,27889],{"type":254,"tag":336,"props":27867,"children":27868},{},[27869,27871,27877],{"type":259,"value":27870},"Created from Pulumi Cloud (following the documentation ",{"type":254,"tag":262,"props":27872,"children":27875},{"href":27873,"rel":27874},"https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/#personal-access-tokens",[266],[27876],{"type":259,"value":1414},{"type":259,"value":2309},{"type":254,"tag":336,"props":27879,"children":27880},{},[27881,27883],{"type":259,"value":27882},"Stored in the stack configuration using the following command ",{"type":254,"tag":389,"props":27884,"children":27886},{"className":27885},[],[27887],{"type":259,"value":27888},"pulumi config set pulumiTokenForRepository ******* --secret",{"type":254,"tag":336,"props":27890,"children":27891},{},[27892,27894],{"type":259,"value":27893},"Stored in a GitHub Actions secret using this code",{"type":254,"tag":612,"props":27895,"children":27897},{"className":24432,"code":27896,"language":24434,"meta":205,"style":205},"new github.ActionsSecret(\"pulumiAccessToken\", {\n  repository: repository.name,\n  secretName: \"PULUMI_ACCESS_TOKEN\",\n  plaintextValue: config.requireSecret(\"pulumiTokenForRepository\"),\n});\n",[27898],{"type":254,"tag":389,"props":27899,"children":27900},{"__ignoreMap":205},[27901,27945,27972,27999,28048],{"type":254,"tag":622,"props":27902,"children":27903},{"class":624,"line":625},[27904,27908,27912,27916,27920,27924,27928,27933,27937,27941],{"type":254,"tag":622,"props":27905,"children":27906},{"style":629},[27907],{"type":259,"value":21937},{"type":254,"tag":622,"props":27909,"children":27910},{"style":1202},[27911],{"type":259,"value":24515},{"type":254,"tag":622,"props":27913,"children":27914},{"style":629},[27915],{"type":259,"value":594},{"type":254,"tag":622,"props":27917,"children":27918},{"style":1735},[27919],{"type":259,"value":26268},{"type":254,"tag":622,"props":27921,"children":27922},{"style":1202},[27923],{"type":259,"value":1668},{"type":254,"tag":622,"props":27925,"children":27926},{"style":629},[27927],{"type":259,"value":652},{"type":254,"tag":622,"props":27929,"children":27930},{"style":664},[27931],{"type":259,"value":27932},"pulumiAccessToken",{"type":254,"tag":622,"props":27934,"children":27935},{"style":629},[27936],{"type":259,"value":652},{"type":254,"tag":622,"props":27938,"children":27939},{"style":629},[27940],{"type":259,"value":3149},{"type":254,"tag":622,"props":27942,"children":27943},{"style":629},[27944],{"type":259,"value":702},{"type":254,"tag":622,"props":27946,"children":27947},{"class":624,"line":635},[27948,27952,27956,27960,27964,27968],{"type":254,"tag":622,"props":27949,"children":27950},{"style":768},[27951],{"type":259,"value":26090},{"type":254,"tag":622,"props":27953,"children":27954},{"style":629},[27955],{"type":259,"value":474},{"type":254,"tag":622,"props":27957,"children":27958},{"style":1202},[27959],{"type":259,"value":20498},{"type":254,"tag":622,"props":27961,"children":27962},{"style":629},[27963],{"type":259,"value":594},{"type":254,"tag":622,"props":27965,"children":27966},{"style":1202},[27967],{"type":259,"value":16394},{"type":254,"tag":622,"props":27969,"children":27970},{"style":629},[27971],{"type":259,"value":676},{"type":254,"tag":622,"props":27973,"children":27974},{"class":624,"line":679},[27975,27979,27983,27987,27991,27995],{"type":254,"tag":622,"props":27976,"children":27977},{"style":768},[27978],{"type":259,"value":26327},{"type":254,"tag":622,"props":27980,"children":27981},{"style":629},[27982],{"type":259,"value":474},{"type":254,"tag":622,"props":27984,"children":27985},{"style":629},[27986],{"type":259,"value":661},{"type":254,"tag":622,"props":27988,"children":27989},{"style":664},[27990],{"type":259,"value":27851},{"type":254,"tag":622,"props":27992,"children":27993},{"style":629},[27994],{"type":259,"value":652},{"type":254,"tag":622,"props":27996,"children":27997},{"style":629},[27998],{"type":259,"value":676},{"type":254,"tag":622,"props":28000,"children":28001},{"class":624,"line":705},[28002,28006,28010,28014,28018,28023,28027,28031,28036,28040,28044],{"type":254,"tag":622,"props":28003,"children":28004},{"style":768},[28005],{"type":259,"value":26355},{"type":254,"tag":622,"props":28007,"children":28008},{"style":629},[28009],{"type":259,"value":474},{"type":254,"tag":622,"props":28011,"children":28012},{"style":1202},[28013],{"type":259,"value":20010},{"type":254,"tag":622,"props":28015,"children":28016},{"style":629},[28017],{"type":259,"value":594},{"type":254,"tag":622,"props":28019,"children":28020},{"style":1735},[28021],{"type":259,"value":28022},"requireSecret",{"type":254,"tag":622,"props":28024,"children":28025},{"style":1202},[28026],{"type":259,"value":1668},{"type":254,"tag":622,"props":28028,"children":28029},{"style":629},[28030],{"type":259,"value":652},{"type":254,"tag":622,"props":28032,"children":28033},{"style":664},[28034],{"type":259,"value":28035},"pulumiTokenForRepository",{"type":254,"tag":622,"props":28037,"children":28038},{"style":629},[28039],{"type":259,"value":652},{"type":254,"tag":622,"props":28041,"children":28042},{"style":1202},[28043],{"type":259,"value":2309},{"type":254,"tag":622,"props":28045,"children":28046},{"style":629},[28047],{"type":259,"value":676},{"type":254,"tag":622,"props":28049,"children":28050},{"class":624,"line":732},[28051,28055,28059],{"type":254,"tag":622,"props":28052,"children":28053},{"style":629},[28054],{"type":259,"value":2421},{"type":254,"tag":622,"props":28056,"children":28057},{"style":1202},[28058],{"type":259,"value":2309},{"type":254,"tag":622,"props":28060,"children":28061},{"style":629},[28062],{"type":259,"value":2350},{"type":254,"tag":255,"props":28064,"children":28065},{},[28066],{"type":259,"value":28067},"The last thing to do is to add this workflow file to the GitHub repository:",{"type":254,"tag":612,"props":28069,"children":28071},{"className":24432,"code":28070,"language":24434,"meta":205,"style":205},"import { readFileSync } from \"fs\";\n\nconst pipelineContent = readFileSync(\"main.yml\", \"utf-8\");\nnew github.RepositoryFile(\"pipelineRepositoryFile\", {\n  repository: repository.name,\n  branch: \"main\",\n  file: \".github/workflows/main.yml\",\n  content: pipelineContent,\n  commitMessage: \"Add preconfigured pipeline file\",\n  commitAuthor: \"Alexandre Nédélec\",\n  commitEmail: \"15186176+TechWatching@users.noreply.github.com\",\n  overwriteOnCreate: true,\n});\n",[28072],{"type":254,"tag":389,"props":28073,"children":28074},{"__ignoreMap":205},[28075,28116,28123,28185,28230,28257,28285,28314,28335,28363,28392,28421,28441],{"type":254,"tag":622,"props":28076,"children":28077},{"class":624,"line":625},[28078,28082,28086,28091,28095,28099,28103,28108,28112],{"type":254,"tag":622,"props":28079,"children":28080},{"style":1196},[28081],{"type":259,"value":24446},{"type":254,"tag":622,"props":28083,"children":28084},{"style":629},[28085],{"type":259,"value":1990},{"type":254,"tag":622,"props":28087,"children":28088},{"style":1202},[28089],{"type":259,"value":28090}," readFileSync",{"type":254,"tag":622,"props":28092,"children":28093},{"style":629},[28094],{"type":259,"value":14256},{"type":254,"tag":622,"props":28096,"children":28097},{"style":1196},[28098],{"type":259,"value":25470},{"type":254,"tag":622,"props":28100,"children":28101},{"style":629},[28102],{"type":259,"value":661},{"type":254,"tag":622,"props":28104,"children":28105},{"style":664},[28106],{"type":259,"value":28107},"fs",{"type":254,"tag":622,"props":28109,"children":28110},{"style":629},[28111],{"type":259,"value":652},{"type":254,"tag":622,"props":28113,"children":28114},{"style":629},[28115],{"type":259,"value":2350},{"type":254,"tag":622,"props":28117,"children":28118},{"class":624,"line":635},[28119],{"type":254,"tag":622,"props":28120,"children":28121},{"emptyLinePlaceholder":1699},[28122],{"type":259,"value":1702},{"type":254,"tag":622,"props":28124,"children":28125},{"class":624,"line":679},[28126,28130,28135,28139,28143,28147,28151,28156,28160,28164,28168,28173,28177,28181],{"type":254,"tag":622,"props":28127,"children":28128},{"style":644},[28129],{"type":259,"value":24497},{"type":254,"tag":622,"props":28131,"children":28132},{"style":1202},[28133],{"type":259,"value":28134}," pipelineContent ",{"type":254,"tag":622,"props":28136,"children":28137},{"style":629},[28138],{"type":259,"value":1654},{"type":254,"tag":622,"props":28140,"children":28141},{"style":1735},[28142],{"type":259,"value":28090},{"type":254,"tag":622,"props":28144,"children":28145},{"style":1202},[28146],{"type":259,"value":1668},{"type":254,"tag":622,"props":28148,"children":28149},{"style":629},[28150],{"type":259,"value":652},{"type":254,"tag":622,"props":28152,"children":28153},{"style":664},[28154],{"type":259,"value":28155},"main.yml",{"type":254,"tag":622,"props":28157,"children":28158},{"style":629},[28159],{"type":259,"value":652},{"type":254,"tag":622,"props":28161,"children":28162},{"style":629},[28163],{"type":259,"value":3149},{"type":254,"tag":622,"props":28165,"children":28166},{"style":629},[28167],{"type":259,"value":661},{"type":254,"tag":622,"props":28169,"children":28170},{"style":664},[28171],{"type":259,"value":28172},"utf-8",{"type":254,"tag":622,"props":28174,"children":28175},{"style":629},[28176],{"type":259,"value":652},{"type":254,"tag":622,"props":28178,"children":28179},{"style":1202},[28180],{"type":259,"value":2309},{"type":254,"tag":622,"props":28182,"children":28183},{"style":629},[28184],{"type":259,"value":2350},{"type":254,"tag":622,"props":28186,"children":28187},{"class":624,"line":705},[28188,28192,28196,28200,28205,28209,28213,28218,28222,28226],{"type":254,"tag":622,"props":28189,"children":28190},{"style":629},[28191],{"type":259,"value":21937},{"type":254,"tag":622,"props":28193,"children":28194},{"style":1202},[28195],{"type":259,"value":24515},{"type":254,"tag":622,"props":28197,"children":28198},{"style":629},[28199],{"type":259,"value":594},{"type":254,"tag":622,"props":28201,"children":28202},{"style":1735},[28203],{"type":259,"value":28204},"RepositoryFile",{"type":254,"tag":622,"props":28206,"children":28207},{"style":1202},[28208],{"type":259,"value":1668},{"type":254,"tag":622,"props":28210,"children":28211},{"style":629},[28212],{"type":259,"value":652},{"type":254,"tag":622,"props":28214,"children":28215},{"style":664},[28216],{"type":259,"value":28217},"pipelineRepositoryFile",{"type":254,"tag":622,"props":28219,"children":28220},{"style":629},[28221],{"type":259,"value":652},{"type":254,"tag":622,"props":28223,"children":28224},{"style":629},[28225],{"type":259,"value":3149},{"type":254,"tag":622,"props":28227,"children":28228},{"style":629},[28229],{"type":259,"value":702},{"type":254,"tag":622,"props":28231,"children":28232},{"class":624,"line":732},[28233,28237,28241,28245,28249,28253],{"type":254,"tag":622,"props":28234,"children":28235},{"style":768},[28236],{"type":259,"value":26090},{"type":254,"tag":622,"props":28238,"children":28239},{"style":629},[28240],{"type":259,"value":474},{"type":254,"tag":622,"props":28242,"children":28243},{"style":1202},[28244],{"type":259,"value":20498},{"type":254,"tag":622,"props":28246,"children":28247},{"style":629},[28248],{"type":259,"value":594},{"type":254,"tag":622,"props":28250,"children":28251},{"style":1202},[28252],{"type":259,"value":16394},{"type":254,"tag":622,"props":28254,"children":28255},{"style":629},[28256],{"type":259,"value":676},{"type":254,"tag":622,"props":28258,"children":28259},{"class":624,"line":759},[28260,28265,28269,28273,28277,28281],{"type":254,"tag":622,"props":28261,"children":28262},{"style":768},[28263],{"type":259,"value":28264},"  branch",{"type":254,"tag":622,"props":28266,"children":28267},{"style":629},[28268],{"type":259,"value":474},{"type":254,"tag":622,"props":28270,"children":28271},{"style":629},[28272],{"type":259,"value":661},{"type":254,"tag":622,"props":28274,"children":28275},{"style":664},[28276],{"type":259,"value":23830},{"type":254,"tag":622,"props":28278,"children":28279},{"style":629},[28280],{"type":259,"value":652},{"type":254,"tag":622,"props":28282,"children":28283},{"style":629},[28284],{"type":259,"value":676},{"type":254,"tag":622,"props":28286,"children":28287},{"class":624,"line":798},[28288,28293,28297,28301,28306,28310],{"type":254,"tag":622,"props":28289,"children":28290},{"style":768},[28291],{"type":259,"value":28292},"  file",{"type":254,"tag":622,"props":28294,"children":28295},{"style":629},[28296],{"type":259,"value":474},{"type":254,"tag":622,"props":28298,"children":28299},{"style":629},[28300],{"type":259,"value":661},{"type":254,"tag":622,"props":28302,"children":28303},{"style":664},[28304],{"type":259,"value":28305},".github/workflows/main.yml",{"type":254,"tag":622,"props":28307,"children":28308},{"style":629},[28309],{"type":259,"value":652},{"type":254,"tag":622,"props":28311,"children":28312},{"style":629},[28313],{"type":259,"value":676},{"type":254,"tag":622,"props":28315,"children":28316},{"class":624,"line":836},[28317,28322,28326,28331],{"type":254,"tag":622,"props":28318,"children":28319},{"style":768},[28320],{"type":259,"value":28321},"  content",{"type":254,"tag":622,"props":28323,"children":28324},{"style":629},[28325],{"type":259,"value":474},{"type":254,"tag":622,"props":28327,"children":28328},{"style":1202},[28329],{"type":259,"value":28330}," pipelineContent",{"type":254,"tag":622,"props":28332,"children":28333},{"style":629},[28334],{"type":259,"value":676},{"type":254,"tag":622,"props":28336,"children":28337},{"class":624,"line":862},[28338,28343,28347,28351,28355,28359],{"type":254,"tag":622,"props":28339,"children":28340},{"style":768},[28341],{"type":259,"value":28342},"  commitMessage",{"type":254,"tag":622,"props":28344,"children":28345},{"style":629},[28346],{"type":259,"value":474},{"type":254,"tag":622,"props":28348,"children":28349},{"style":629},[28350],{"type":259,"value":661},{"type":254,"tag":622,"props":28352,"children":28353},{"style":664},[28354],{"type":259,"value":22902},{"type":254,"tag":622,"props":28356,"children":28357},{"style":629},[28358],{"type":259,"value":652},{"type":254,"tag":622,"props":28360,"children":28361},{"style":629},[28362],{"type":259,"value":676},{"type":254,"tag":622,"props":28364,"children":28365},{"class":624,"line":900},[28366,28371,28375,28379,28384,28388],{"type":254,"tag":622,"props":28367,"children":28368},{"style":768},[28369],{"type":259,"value":28370},"  commitAuthor",{"type":254,"tag":622,"props":28372,"children":28373},{"style":629},[28374],{"type":259,"value":474},{"type":254,"tag":622,"props":28376,"children":28377},{"style":629},[28378],{"type":259,"value":661},{"type":254,"tag":622,"props":28380,"children":28381},{"style":664},[28382],{"type":259,"value":28383},"Alexandre Nédélec",{"type":254,"tag":622,"props":28385,"children":28386},{"style":629},[28387],{"type":259,"value":652},{"type":254,"tag":622,"props":28389,"children":28390},{"style":629},[28391],{"type":259,"value":676},{"type":254,"tag":622,"props":28393,"children":28394},{"class":624,"line":938},[28395,28400,28404,28408,28413,28417],{"type":254,"tag":622,"props":28396,"children":28397},{"style":768},[28398],{"type":259,"value":28399},"  commitEmail",{"type":254,"tag":622,"props":28401,"children":28402},{"style":629},[28403],{"type":259,"value":474},{"type":254,"tag":622,"props":28405,"children":28406},{"style":629},[28407],{"type":259,"value":661},{"type":254,"tag":622,"props":28409,"children":28410},{"style":664},[28411],{"type":259,"value":28412},"15186176+TechWatching@users.noreply.github.com",{"type":254,"tag":622,"props":28414,"children":28415},{"style":629},[28416],{"type":259,"value":652},{"type":254,"tag":622,"props":28418,"children":28419},{"style":629},[28420],{"type":259,"value":676},{"type":254,"tag":622,"props":28422,"children":28423},{"class":624,"line":976},[28424,28429,28433,28437],{"type":254,"tag":622,"props":28425,"children":28426},{"style":768},[28427],{"type":259,"value":28428},"  overwriteOnCreate",{"type":254,"tag":622,"props":28430,"children":28431},{"style":629},[28432],{"type":259,"value":474},{"type":254,"tag":622,"props":28434,"children":28435},{"style":7715},[28436],{"type":259,"value":7718},{"type":254,"tag":622,"props":28438,"children":28439},{"style":629},[28440],{"type":259,"value":676},{"type":254,"tag":622,"props":28442,"children":28443},{"class":624,"line":1014},[28444,28448,28452],{"type":254,"tag":622,"props":28445,"children":28446},{"style":629},[28447],{"type":259,"value":2421},{"type":254,"tag":622,"props":28449,"children":28450},{"style":1202},[28451],{"type":259,"value":2309},{"type":254,"tag":622,"props":28453,"children":28454},{"style":629},[28455],{"type":259,"value":2350},{"type":254,"tag":255,"props":28457,"children":28458},{},[28459],{"type":259,"value":28460},"This code:",{"type":254,"tag":476,"props":28462,"children":28463},{},[28464,28476,28488],{"type":254,"tag":336,"props":28465,"children":28466},{},[28467,28469,28474],{"type":259,"value":28468},"reads the ",{"type":254,"tag":389,"props":28470,"children":28472},{"className":28471},[],[28473],{"type":259,"value":28155},{"type":259,"value":28475}," file that contains the workflow we saw previously",{"type":254,"tag":336,"props":28477,"children":28478},{},[28479,28481,28486],{"type":259,"value":28480},"creates a file with this content in the repository in the ",{"type":254,"tag":389,"props":28482,"children":28484},{"className":28483},[],[28485],{"type":259,"value":23848},{"type":259,"value":28487}," folder for the GitHub Actions workflows",{"type":254,"tag":336,"props":28489,"children":28490},{},[28491],{"type":259,"value":28492},"makes a commit when creating the file (or modifying it)",{"type":254,"tag":427,"props":28494,"children":28495},{"icon":429},[28496],{"type":254,"tag":255,"props":28497,"children":28498},{},[28499,28501,28507,28509,28514],{"type":259,"value":28500},"To read the YAML file, I use the ",{"type":254,"tag":389,"props":28502,"children":28504},{"className":28503},[],[28505],{"type":259,"value":28506},"readFileSync",{"type":259,"value":28508}," method from the File System API ",{"type":254,"tag":389,"props":28510,"children":28512},{"className":28511},[],[28513],{"type":259,"value":28107},{"type":259,"value":28515},". That's one of the things I love about Pulumi: you use the things you already know and that already exist in your ecosystem. No need to look for a module or wait for someone to write one, there is probably something standard or a popular community library you can use.",{"type":254,"tag":282,"props":28517,"children":28519},{"id":28518},"test-the-azure-ready-github-repository",[28520],{"type":259,"value":28521},"Test the Azure-Ready GitHub Repository",{"type":254,"tag":255,"props":28523,"children":28524},{},[28525,28527,28532],{"type":259,"value":28526},"Now that the infrastructure code to provision the Azure-Ready GitHub repository is written, let's run it with the ",{"type":254,"tag":389,"props":28528,"children":28530},{"className":28529},[],[28531],{"type":259,"value":23629},{"type":259,"value":28533}," command and see if it works!",{"type":254,"tag":255,"props":28535,"children":28536},{},[28537],{"type":254,"tag":1161,"props":28538,"children":28542},{"alt":28539,"className":28540,"src":28541,"width":19735},"Ouput of the pulumi up command with all the resources created.",[1165,1166],"/posts/images/azurereadygithub_pulumi_1.webp",[],{"type":254,"tag":255,"props":28544,"children":28545},{},[28546],{"type":259,"value":28547},"All the resources are correctly created and our new GitHub repository is ready to be used.",{"type":254,"tag":255,"props":28549,"children":28550},{},[28551],{"type":254,"tag":1161,"props":28552,"children":28556},{"alt":28553,"className":28554,"src":28555},"Picture of the Azure Ready GitHub repository",[1165,1166],"/posts/images/azurereadygithub_github_2.webp",[],{"type":254,"tag":255,"props":28558,"children":28559},{},[28560],{"type":259,"value":28561},"Let's clone it.",{"type":254,"tag":612,"props":28563,"children":28565},{"className":16836,"code":28564,"language":16838,"meta":205,"style":205},"git clone https://github.com/TechWatching/azure-ready-repository; cd azure-ready-repository\n",[28566],{"type":254,"tag":389,"props":28567,"children":28568},{"__ignoreMap":205},[28569],{"type":254,"tag":622,"props":28570,"children":28571},{"class":624,"line":625},[28572,28576,28581,28586,28590,28595],{"type":254,"tag":622,"props":28573,"children":28574},{"style":714},[28575],{"type":259,"value":16850},{"type":254,"tag":622,"props":28577,"children":28578},{"style":664},[28579],{"type":259,"value":28580}," clone",{"type":254,"tag":622,"props":28582,"children":28583},{"style":664},[28584],{"type":259,"value":28585}," https://github.com/TechWatching/azure-ready-repository",{"type":254,"tag":622,"props":28587,"children":28588},{"style":629},[28589],{"type":259,"value":2000},{"type":254,"tag":622,"props":28591,"children":28592},{"style":1735},[28593],{"type":259,"value":28594}," cd",{"type":254,"tag":622,"props":28596,"children":28597},{"style":664},[28598],{"type":259,"value":28599}," azure-ready-repository\n",{"type":254,"tag":255,"props":28601,"children":28602},{},[28603],{"type":259,"value":28604},"We want to verify that the GitHub project is properly configured and can provision Azure resources from its GitHub Actions workflow.",{"type":254,"tag":255,"props":28606,"children":28607},{},[28608],{"type":259,"value":28609},"Let's add some infrastructure code that provisions a few Azure resources to check that:",{"type":254,"tag":612,"props":28611,"children":28613},{"className":16836,"code":28612,"language":16838,"meta":205,"style":205},"pulumi new azure-typescript -n \"AzureReadyGitHuRepository\" -y --force\n",[28614],{"type":254,"tag":389,"props":28615,"children":28616},{"__ignoreMap":205},[28617],{"type":254,"tag":622,"props":28618,"children":28619},{"class":624,"line":625},[28620,28624,28628,28633,28637,28641,28646,28650,28655],{"type":254,"tag":622,"props":28621,"children":28622},{"style":714},[28623],{"type":259,"value":19768},{"type":254,"tag":622,"props":28625,"children":28626},{"style":664},[28627],{"type":259,"value":1624},{"type":254,"tag":622,"props":28629,"children":28630},{"style":664},[28631],{"type":259,"value":28632}," azure-typescript",{"type":254,"tag":622,"props":28634,"children":28635},{"style":664},[28636],{"type":259,"value":19782},{"type":254,"tag":622,"props":28638,"children":28639},{"style":629},[28640],{"type":259,"value":661},{"type":254,"tag":622,"props":28642,"children":28643},{"style":664},[28644],{"type":259,"value":28645},"AzureReadyGitHuRepository",{"type":254,"tag":622,"props":28647,"children":28648},{"style":629},[28649],{"type":259,"value":652},{"type":254,"tag":622,"props":28651,"children":28652},{"style":664},[28653],{"type":259,"value":28654}," -y",{"type":254,"tag":622,"props":28656,"children":28657},{"style":664},[28658],{"type":259,"value":28659}," --force\n",{"type":254,"tag":255,"props":28661,"children":28662},{},[28663,28664,28670],{"type":259,"value":10970},{"type":254,"tag":389,"props":28665,"children":28667},{"className":28666},[],[28668],{"type":259,"value":28669},"--force",{"type":259,"value":28671}," option allows us to create the code within a non-empty directory.",{"type":254,"tag":255,"props":28673,"children":28674},{},[28675,28677,28683],{"type":259,"value":28676},"I used the ",{"type":254,"tag":389,"props":28678,"children":28680},{"className":28679},[],[28681],{"type":259,"value":28682},"azure-typescript",{"type":259,"value":28684}," template that creates a storage account and outputs retrieve its primary access key.",{"type":254,"tag":427,"props":28686,"children":28687},{"icon":20082},[28688],{"type":254,"tag":255,"props":28689,"children":28690},{},[28691,28693,28700],{"type":259,"value":28692},"In the SDK, the outputs of the function that lists the storage access keys are not currently marked as secrets. There is currently an ",{"type":254,"tag":262,"props":28694,"children":28697},{"href":28695,"rel":28696},"https://github.com/pulumi/pulumi-azure-native/issues/2408",[266],[28698],{"type":259,"value":28699},"open issue",{"type":259,"value":28701}," to change that but in the meantime, I have just modified the code to label the stack output as secret ensuring its encryption.",{"type":254,"tag":255,"props":28703,"children":28704},{},[28705,28707,28713,28715,28721],{"type":259,"value":28706},"Let's run a ",{"type":254,"tag":389,"props":28708,"children":28710},{"className":28709},[],[28711],{"type":259,"value":28712},"pnpm install",{"type":259,"value":28714}," to install the dependencies and generate the ",{"type":254,"tag":389,"props":28716,"children":28718},{"className":28717},[],[28719],{"type":259,"value":28720},"pnpm-lock.yaml",{"type":259,"value":28722}," file. Then, we can push the code to GitHub and run the pipeline to see how it goes.",{"type":254,"tag":255,"props":28724,"children":28725},{},[28726],{"type":254,"tag":1161,"props":28727,"children":28731},{"alt":28728,"className":28729,"src":28730},"Logs of the pipeline run showing that the workflow successfully created a storage account.",[1165,1166],"/posts/images/azurereadygithub_github_3.webp",[],{"type":254,"tag":255,"props":28733,"children":28734},{},[28735],{"type":259,"value":28736},"That's it, we succeeded to provision a storage account from our new GitHub repository whose creation and configuration were entirely automated using Pulumi.",{"type":254,"tag":282,"props":28738,"children":28739},{"id":10572},[28740],{"type":259,"value":10575},{"type":254,"tag":455,"props":28742,"children":28744},{"id":28743},"additional-information",[28745],{"type":259,"value":28746},"Additional information",{"type":254,"tag":255,"props":28748,"children":28749},{},[28750],{"type":259,"value":28751},"There are different platforms you can use to host your Git repositories: GitHub, GitLab, and Azure DevOps to name a few. We use GitHub in this article but you can easily apply the same logic with other platforms (Pulumi has providers for GitLab and Azure DevOps as well).",{"type":254,"tag":255,"props":28753,"children":28754},{},[28755],{"type":259,"value":28756},"Even though the Azure-Ready GitHub repository is provisioned using Pulumi, there's nothing stopping you from using another Infrastructure as Code solution that supports Azure OIDC (such as Azure CLI, which was mentioned in the article, Azure Bicep, or even Terraform) in the GitHub Actions workflow of the created repository. You don't even have to provision infrastructure; you can use this workflow to simply deploy an application to an existing Azure resource.",{"type":254,"tag":455,"props":28758,"children":28760},{"id":28759},"potential-enhancements",[28761],{"type":259,"value":28762},"Potential Enhancements",{"type":254,"tag":255,"props":28764,"children":28765},{},[28766],{"type":259,"value":28767},"There are many aspects that could be improved in the infrastructure code provisioning the Azure-Ready GitHub repository, but I believe the current solution serves as a good starting point. Nevertheless, here are some ideas for potential enhancements:",{"type":254,"tag":332,"props":28769,"children":28770},{},[28771,28776,28781],{"type":254,"tag":336,"props":28772,"children":28773},{},[28774],{"type":259,"value":28775},"make additional items, such as the commit author, configurable",{"type":254,"tag":336,"props":28777,"children":28778},{},[28779],{"type":259,"value":28780},"authorize an environment and not only a branch to retrieve an Azure token",{"type":254,"tag":336,"props":28782,"children":28783},{},[28784],{"type":259,"value":28785},"use environment variables/secrets instead of variable/secrets at the repository scope",{"type":254,"tag":255,"props":28787,"children":28788},{},[28789],{"type":259,"value":28790},"I think it would be interesting as well to put that code behind an API or a Web application using Pulumi Automation API to have a self-service solution to create Azure-Ready GitHub repository on the fly.",{"type":254,"tag":455,"props":28792,"children":28794},{"id":28793},"related-articles",[28795],{"type":259,"value":28796},"Related articles",{"type":254,"tag":255,"props":28798,"children":28799},{},[28800],{"type":259,"value":28801},"Here are some articles on the same topic I wanted to mention:",{"type":254,"tag":332,"props":28803,"children":28804},{},[28805,28836,28864],{"type":254,"tag":336,"props":28806,"children":28807},{},[28808,28818,28820,28825,28829,28834],{"type":254,"tag":262,"props":28809,"children":28812},{"href":28810,"rel":28811},"https://leebriggs.co.uk/blog/2022/01/23/gha-cloud-credentials",[266],[28813],{"type":254,"tag":9718,"props":28814,"children":28815},{},[28816],{"type":259,"value":28817},"Stop using static cloud credentials in GitHub Actions",{"type":259,"value":28819}," ",{"type":254,"tag":9718,"props":28821,"children":28822},{},[28823],{"type":259,"value":28824},"by Lee Briggs",{"type":254,"tag":28826,"props":28827,"children":28828},"br",{},[],{"type":254,"tag":9718,"props":28830,"children":28831},{},[28832],{"type":259,"value":28833},"➡️",{"type":259,"value":28835}," This post provides examples for configuring OIDC authentication with GitHub Actions for AWS, Azure, and GCP. The code for Azure is quite similar to the code I showed here. Yet, it doesn't go so far as to initialize a pipeline ready to deploy resources with Pulumi. Anyway, it's awesome to have the code for all 3 major providers.",{"type":254,"tag":336,"props":28837,"children":28838},{},[28839,28849,28850,28855,28858,28862],{"type":254,"tag":262,"props":28840,"children":28843},{"href":28841,"rel":28842},"https://xaviergeerinck.com/2023/05/16/configuring-github-actions-to-azure-authentication-with-oidc/",[266],[28844],{"type":254,"tag":9718,"props":28845,"children":28846},{},[28847],{"type":259,"value":28848},"Configuring GitHub Actions to Azure authentication with OIDC",{"type":259,"value":28819},{"type":254,"tag":9718,"props":28851,"children":28852},{},[28853],{"type":259,"value":28854},"by Xavier Geerinck",{"type":254,"tag":28826,"props":28856,"children":28857},{},[],{"type":254,"tag":9718,"props":28859,"children":28860},{},[28861],{"type":259,"value":28833},{"type":259,"value":28863},"This post also shows how to configure OIDC authentication with GitHub Actions and Azure but using an Azure CLI script. Although the GitHub repository creation and configuration are done manually, automating the Azure part with a few lines of script is nice.",{"type":254,"tag":336,"props":28865,"children":28866},{},[28867,28877,28878,28883,28886],{"type":254,"tag":262,"props":28868,"children":28871},{"href":28869,"rel":28870},"https://samcogan.com/getting-rid-of-passwords-for-deployment-with-pulumi-oidc-support/",[266],[28872],{"type":254,"tag":9718,"props":28873,"children":28874},{},[28875],{"type":259,"value":28876},"Getting Rid of Passwords for Deployment with Pulumi OIDC Support",{"type":259,"value":28819},{"type":254,"tag":9718,"props":28879,"children":28880},{},[28881],{"type":259,"value":28882},"by Sam Cogan",{"type":254,"tag":28826,"props":28884,"children":28885},{},[],{"type":259,"value":28887},"\n➡️ If you don't care about automating everything and simply want to configure OIDC authentication through the Azure portal, that's the post you will want to read. There is also an example of a pipeline to provision Azure infrastructure using a .NET Pulumi program.",{"type":254,"tag":455,"props":28889,"children":28891},{"id":28890},"complete-code-solution",[28892],{"type":259,"value":28893},"Complete code solution",{"type":254,"tag":255,"props":28895,"children":28896},{},[28897],{"type":259,"value":28898},"In this article, I aimed to provide a step-by-step explanation of how to automate the creation of a GitHub repository with a properly configured workflow to interact with Azure using OpenID Connect. Consequently, the article turned out to be quite lengthy. I apologize for that, but I didn't want to present the code without adequate explanation.",{"type":254,"tag":255,"props":28900,"children":28901},{},[28902],{"type":259,"value":28903},"Anyway, now that we've covered everything, here is the complete code, which is just 75 lines long:",{"type":254,"tag":612,"props":28905,"children":28907},{"className":24432,"code":28906,"language":24434,"meta":205,"style":205},"import * as pulumi from \"@pulumi/pulumi\";\nimport * as github from \"@pulumi/github\";\nimport * as azuread from \"@pulumi/azuread\";\nimport * as authorization from \"@pulumi/azure-native/authorization\";\nimport { azureBuiltInRoles } from \"./builtInRoles\";\nimport { readFileSync } from \"fs\";\n\nconst config = new pulumi.Config();\n\nconst repository = new github.Repository(\"azure-ready-repository\", {\n  name: \"azure-ready-repository\",\n  visibility: \"public\",\n  autoInit: true\n});\n\nexport const repositoryCloneUrl = repository.httpCloneUrl;\n\nconst aadApplication = new azuread.Application(\"AzureReadyApp\", { displayName: \"Azure Ready App\" });\nconst servicePrincipal = new azuread.ServicePrincipal(\"AzureReadyServicePrincipal\", {\n  applicationId: aadApplication.applicationId,\n});\nnew azuread.ApplicationFederatedIdentityCredential(\"AzureReadyAppFederatedIdentityCredential\", {\n  applicationObjectId: aadApplication.objectId,\n  displayName: \"AzureReadyDeploys\",\n  description: \"Deployments for azure-ready-repository\",\n  audiences: [\"api://AzureADTokenExchange\"],\n  issuer: \"https://token.actions.githubusercontent.com\",\n  subject: pulumi.interpolate`repo:${repository.fullName}:ref:refs/heads/main`,\n});\n\nconst azureConfig = pulumi.output(authorization.getClientConfig());\nconst subscriptionId = azureConfig.subscriptionId;\n\nnew authorization.RoleAssignment(\"contributor\", {\n  principalId: servicePrincipal.id,\n  principalType: authorization.PrincipalType.ServicePrincipal,\n  roleDefinitionId: azureBuiltInRoles.contributor,\n  scope: pulumi.interpolate`/subscriptions/${subscriptionId}`,\n});\n\nnew github.ActionsSecret(\"tenantId\", {\n  repository: repository.name,\n  secretName: \"ARM_TENANT_ID\",\n  plaintextValue: azureConfig.tenantId,\n});\n\nnew github.ActionsSecret(\"subscriptionId\", {\n  repository: repository.name,\n  secretName: \"ARM_SUBSCRIPTION_ID\",\n  plaintextValue: azureConfig.subscriptionId,\n});\n\nnew github.ActionsSecret(\"clientId\", {\n  repository: repository.name,\n  secretName: \"ARM_CLIENT_ID\",\n  plaintextValue: aadApplication.applicationId,\n});\n\nnew github.ActionsSecret(\"pulumiAccessToken\", {\n  repository: repository.name,\n  secretName: \"PULUMI_ACCESS_TOKEN\",\n  plaintextValue: config.requireSecret(\"pulumiTokenForRepository\"),\n});\n\nconst pipelineContent = readFileSync(\"main.yml\", \"utf-8\");\nnew github.RepositoryFile(\"pipelineRepositoryFile\", {\n  repository: repository.name,\n  branch: \"main\",\n  file: \".github/workflows/main.yml\",\n  content: pipelineContent,\n  commitMessage: \"Add preconfigured pipeline file\",\n  commitAuthor: \"Alexandre Nédélec\",\n  commitEmail: \"15186176+TechWatching@users.noreply.github.com\",\n  overwriteOnCreate: true,\n});\n",[28908],{"type":254,"tag":389,"props":28909,"children":28910},{"__ignoreMap":205},[28911,28952,28991,29030,29069,29108,29147,29154,29193,29200,29255,29282,29309,29324,29339,29346,29381,29388,29475,29530,29557,29572,29615,29642,29669,29696,29731,29758,29821,29836,29843,29890,29921,29928,29971,29998,30033,30060,30107,30122,30129,30172,30199,30226,30253,30268,30275,30318,30345,30372,30399,30414,30421,30464,30491,30518,30545,30560,30567,30610,30637,30664,30711,30726,30733,30792,30835,30862,30889,30916,30935,30962,30989,31016,31035],{"type":254,"tag":622,"props":28912,"children":28913},{"class":624,"line":625},[28914,28918,28922,28926,28931,28935,28939,28944,28948],{"type":254,"tag":622,"props":28915,"children":28916},{"style":1196},[28917],{"type":259,"value":24446},{"type":254,"tag":622,"props":28919,"children":28920},{"style":629},[28921],{"type":259,"value":24451},{"type":254,"tag":622,"props":28923,"children":28924},{"style":1196},[28925],{"type":259,"value":24456},{"type":254,"tag":622,"props":28927,"children":28928},{"style":1202},[28929],{"type":259,"value":28930}," pulumi ",{"type":254,"tag":622,"props":28932,"children":28933},{"style":1196},[28934],{"type":259,"value":24466},{"type":254,"tag":622,"props":28936,"children":28937},{"style":629},[28938],{"type":259,"value":661},{"type":254,"tag":622,"props":28940,"children":28941},{"style":664},[28942],{"type":259,"value":28943},"@pulumi/pulumi",{"type":254,"tag":622,"props":28945,"children":28946},{"style":629},[28947],{"type":259,"value":652},{"type":254,"tag":622,"props":28949,"children":28950},{"style":629},[28951],{"type":259,"value":2350},{"type":254,"tag":622,"props":28953,"children":28954},{"class":624,"line":635},[28955,28959,28963,28967,28971,28975,28979,28983,28987],{"type":254,"tag":622,"props":28956,"children":28957},{"style":1196},[28958],{"type":259,"value":24446},{"type":254,"tag":622,"props":28960,"children":28961},{"style":629},[28962],{"type":259,"value":24451},{"type":254,"tag":622,"props":28964,"children":28965},{"style":1196},[28966],{"type":259,"value":24456},{"type":254,"tag":622,"props":28968,"children":28969},{"style":1202},[28970],{"type":259,"value":24461},{"type":254,"tag":622,"props":28972,"children":28973},{"style":1196},[28974],{"type":259,"value":24466},{"type":254,"tag":622,"props":28976,"children":28977},{"style":629},[28978],{"type":259,"value":661},{"type":254,"tag":622,"props":28980,"children":28981},{"style":664},[28982],{"type":259,"value":24358},{"type":254,"tag":622,"props":28984,"children":28985},{"style":629},[28986],{"type":259,"value":652},{"type":254,"tag":622,"props":28988,"children":28989},{"style":629},[28990],{"type":259,"value":2350},{"type":254,"tag":622,"props":28992,"children":28993},{"class":624,"line":679},[28994,28998,29002,29006,29010,29014,29018,29022,29026],{"type":254,"tag":622,"props":28995,"children":28996},{"style":1196},[28997],{"type":259,"value":24446},{"type":254,"tag":622,"props":28999,"children":29000},{"style":629},[29001],{"type":259,"value":24451},{"type":254,"tag":622,"props":29003,"children":29004},{"style":1196},[29005],{"type":259,"value":24456},{"type":254,"tag":622,"props":29007,"children":29008},{"style":1202},[29009],{"type":259,"value":24801},{"type":254,"tag":622,"props":29011,"children":29012},{"style":1196},[29013],{"type":259,"value":24466},{"type":254,"tag":622,"props":29015,"children":29016},{"style":629},[29017],{"type":259,"value":661},{"type":254,"tag":622,"props":29019,"children":29020},{"style":664},[29021],{"type":259,"value":24353},{"type":254,"tag":622,"props":29023,"children":29024},{"style":629},[29025],{"type":259,"value":652},{"type":254,"tag":622,"props":29027,"children":29028},{"style":629},[29029],{"type":259,"value":2350},{"type":254,"tag":622,"props":29031,"children":29032},{"class":624,"line":705},[29033,29037,29041,29045,29049,29053,29057,29061,29065],{"type":254,"tag":622,"props":29034,"children":29035},{"style":1196},[29036],{"type":259,"value":24446},{"type":254,"tag":622,"props":29038,"children":29039},{"style":629},[29040],{"type":259,"value":24451},{"type":254,"tag":622,"props":29042,"children":29043},{"style":1196},[29044],{"type":259,"value":24456},{"type":254,"tag":622,"props":29046,"children":29047},{"style":1202},[29048],{"type":259,"value":25424},{"type":254,"tag":622,"props":29050,"children":29051},{"style":1196},[29052],{"type":259,"value":24466},{"type":254,"tag":622,"props":29054,"children":29055},{"style":629},[29056],{"type":259,"value":661},{"type":254,"tag":622,"props":29058,"children":29059},{"style":664},[29060],{"type":259,"value":25437},{"type":254,"tag":622,"props":29062,"children":29063},{"style":629},[29064],{"type":259,"value":652},{"type":254,"tag":622,"props":29066,"children":29067},{"style":629},[29068],{"type":259,"value":2350},{"type":254,"tag":622,"props":29070,"children":29071},{"class":624,"line":732},[29072,29076,29080,29084,29088,29092,29096,29100,29104],{"type":254,"tag":622,"props":29073,"children":29074},{"style":1196},[29075],{"type":259,"value":24446},{"type":254,"tag":622,"props":29077,"children":29078},{"style":629},[29079],{"type":259,"value":1990},{"type":254,"tag":622,"props":29081,"children":29082},{"style":1202},[29083],{"type":259,"value":25461},{"type":254,"tag":622,"props":29085,"children":29086},{"style":629},[29087],{"type":259,"value":14256},{"type":254,"tag":622,"props":29089,"children":29090},{"style":1196},[29091],{"type":259,"value":25470},{"type":254,"tag":622,"props":29093,"children":29094},{"style":629},[29095],{"type":259,"value":661},{"type":254,"tag":622,"props":29097,"children":29098},{"style":664},[29099],{"type":259,"value":25479},{"type":254,"tag":622,"props":29101,"children":29102},{"style":629},[29103],{"type":259,"value":652},{"type":254,"tag":622,"props":29105,"children":29106},{"style":629},[29107],{"type":259,"value":2350},{"type":254,"tag":622,"props":29109,"children":29110},{"class":624,"line":759},[29111,29115,29119,29123,29127,29131,29135,29139,29143],{"type":254,"tag":622,"props":29112,"children":29113},{"style":1196},[29114],{"type":259,"value":24446},{"type":254,"tag":622,"props":29116,"children":29117},{"style":629},[29118],{"type":259,"value":1990},{"type":254,"tag":622,"props":29120,"children":29121},{"style":1202},[29122],{"type":259,"value":28090},{"type":254,"tag":622,"props":29124,"children":29125},{"style":629},[29126],{"type":259,"value":14256},{"type":254,"tag":622,"props":29128,"children":29129},{"style":1196},[29130],{"type":259,"value":25470},{"type":254,"tag":622,"props":29132,"children":29133},{"style":629},[29134],{"type":259,"value":661},{"type":254,"tag":622,"props":29136,"children":29137},{"style":664},[29138],{"type":259,"value":28107},{"type":254,"tag":622,"props":29140,"children":29141},{"style":629},[29142],{"type":259,"value":652},{"type":254,"tag":622,"props":29144,"children":29145},{"style":629},[29146],{"type":259,"value":2350},{"type":254,"tag":622,"props":29148,"children":29149},{"class":624,"line":798},[29150],{"type":254,"tag":622,"props":29151,"children":29152},{"emptyLinePlaceholder":1699},[29153],{"type":259,"value":1702},{"type":254,"tag":622,"props":29155,"children":29156},{"class":624,"line":836},[29157,29161,29165,29169,29173,29177,29181,29185,29189],{"type":254,"tag":622,"props":29158,"children":29159},{"style":644},[29160],{"type":259,"value":24497},{"type":254,"tag":622,"props":29162,"children":29163},{"style":1202},[29164],{"type":259,"value":25727},{"type":254,"tag":622,"props":29166,"children":29167},{"style":629},[29168],{"type":259,"value":1654},{"type":254,"tag":622,"props":29170,"children":29171},{"style":629},[29172],{"type":259,"value":1624},{"type":254,"tag":622,"props":29174,"children":29175},{"style":1202},[29176],{"type":259,"value":25261},{"type":254,"tag":622,"props":29178,"children":29179},{"style":629},[29180],{"type":259,"value":594},{"type":254,"tag":622,"props":29182,"children":29183},{"style":1735},[29184],{"type":259,"value":25748},{"type":254,"tag":622,"props":29186,"children":29187},{"style":1202},[29188],{"type":259,"value":2724},{"type":254,"tag":622,"props":29190,"children":29191},{"style":629},[29192],{"type":259,"value":2350},{"type":254,"tag":622,"props":29194,"children":29195},{"class":624,"line":862},[29196],{"type":254,"tag":622,"props":29197,"children":29198},{"emptyLinePlaceholder":1699},[29199],{"type":259,"value":1702},{"type":254,"tag":622,"props":29201,"children":29202},{"class":624,"line":900},[29203,29207,29211,29215,29219,29223,29227,29231,29235,29239,29243,29247,29251],{"type":254,"tag":622,"props":29204,"children":29205},{"style":644},[29206],{"type":259,"value":24497},{"type":254,"tag":622,"props":29208,"children":29209},{"style":1202},[29210],{"type":259,"value":24502},{"type":254,"tag":622,"props":29212,"children":29213},{"style":629},[29214],{"type":259,"value":1654},{"type":254,"tag":622,"props":29216,"children":29217},{"style":629},[29218],{"type":259,"value":1624},{"type":254,"tag":622,"props":29220,"children":29221},{"style":1202},[29222],{"type":259,"value":24515},{"type":254,"tag":622,"props":29224,"children":29225},{"style":629},[29226],{"type":259,"value":594},{"type":254,"tag":622,"props":29228,"children":29229},{"style":1735},[29230],{"type":259,"value":24524},{"type":254,"tag":622,"props":29232,"children":29233},{"style":1202},[29234],{"type":259,"value":1668},{"type":254,"tag":622,"props":29236,"children":29237},{"style":629},[29238],{"type":259,"value":652},{"type":254,"tag":622,"props":29240,"children":29241},{"style":664},[29242],{"type":259,"value":24537},{"type":254,"tag":622,"props":29244,"children":29245},{"style":629},[29246],{"type":259,"value":652},{"type":254,"tag":622,"props":29248,"children":29249},{"style":629},[29250],{"type":259,"value":3149},{"type":254,"tag":622,"props":29252,"children":29253},{"style":629},[29254],{"type":259,"value":702},{"type":254,"tag":622,"props":29256,"children":29257},{"class":624,"line":938},[29258,29262,29266,29270,29274,29278],{"type":254,"tag":622,"props":29259,"children":29260},{"style":768},[29261],{"type":259,"value":24557},{"type":254,"tag":622,"props":29263,"children":29264},{"style":629},[29265],{"type":259,"value":474},{"type":254,"tag":622,"props":29267,"children":29268},{"style":629},[29269],{"type":259,"value":661},{"type":254,"tag":622,"props":29271,"children":29272},{"style":664},[29273],{"type":259,"value":24537},{"type":254,"tag":622,"props":29275,"children":29276},{"style":629},[29277],{"type":259,"value":652},{"type":254,"tag":622,"props":29279,"children":29280},{"style":629},[29281],{"type":259,"value":676},{"type":254,"tag":622,"props":29283,"children":29284},{"class":624,"line":976},[29285,29289,29293,29297,29301,29305],{"type":254,"tag":622,"props":29286,"children":29287},{"style":768},[29288],{"type":259,"value":24585},{"type":254,"tag":622,"props":29290,"children":29291},{"style":629},[29292],{"type":259,"value":474},{"type":254,"tag":622,"props":29294,"children":29295},{"style":629},[29296],{"type":259,"value":661},{"type":254,"tag":622,"props":29298,"children":29299},{"style":664},[29300],{"type":259,"value":1437},{"type":254,"tag":622,"props":29302,"children":29303},{"style":629},[29304],{"type":259,"value":652},{"type":254,"tag":622,"props":29306,"children":29307},{"style":629},[29308],{"type":259,"value":676},{"type":254,"tag":622,"props":29310,"children":29311},{"class":624,"line":1014},[29312,29316,29320],{"type":254,"tag":622,"props":29313,"children":29314},{"style":768},[29315],{"type":259,"value":24613},{"type":254,"tag":622,"props":29317,"children":29318},{"style":629},[29319],{"type":259,"value":474},{"type":254,"tag":622,"props":29321,"children":29322},{"style":7715},[29323],{"type":259,"value":1075},{"type":254,"tag":622,"props":29325,"children":29326},{"class":624,"line":1052},[29327,29331,29335],{"type":254,"tag":622,"props":29328,"children":29329},{"style":629},[29330],{"type":259,"value":2421},{"type":254,"tag":622,"props":29332,"children":29333},{"style":1202},[29334],{"type":259,"value":2309},{"type":254,"tag":622,"props":29336,"children":29337},{"style":629},[29338],{"type":259,"value":2350},{"type":254,"tag":622,"props":29340,"children":29341},{"class":624,"line":1078},[29342],{"type":254,"tag":622,"props":29343,"children":29344},{"emptyLinePlaceholder":1699},[29345],{"type":259,"value":1702},{"type":254,"tag":622,"props":29347,"children":29348},{"class":624,"line":1087},[29349,29353,29357,29361,29365,29369,29373,29377],{"type":254,"tag":622,"props":29350,"children":29351},{"style":1196},[29352],{"type":259,"value":24651},{"type":254,"tag":622,"props":29354,"children":29355},{"style":644},[29356],{"type":259,"value":24656},{"type":254,"tag":622,"props":29358,"children":29359},{"style":1202},[29360],{"type":259,"value":24661},{"type":254,"tag":622,"props":29362,"children":29363},{"style":629},[29364],{"type":259,"value":1654},{"type":254,"tag":622,"props":29366,"children":29367},{"style":1202},[29368],{"type":259,"value":20498},{"type":254,"tag":622,"props":29370,"children":29371},{"style":629},[29372],{"type":259,"value":594},{"type":254,"tag":622,"props":29374,"children":29375},{"style":1202},[29376],{"type":259,"value":24678},{"type":254,"tag":622,"props":29378,"children":29379},{"style":629},[29380],{"type":259,"value":2350},{"type":254,"tag":622,"props":29382,"children":29383},{"class":624,"line":1096},[29384],{"type":254,"tag":622,"props":29385,"children":29386},{"emptyLinePlaceholder":1699},[29387],{"type":259,"value":1702},{"type":254,"tag":622,"props":29389,"children":29390},{"class":624,"line":1105},[29391,29395,29399,29403,29407,29411,29415,29419,29423,29427,29431,29435,29439,29443,29447,29451,29455,29459,29463,29467,29471],{"type":254,"tag":622,"props":29392,"children":29393},{"style":644},[29394],{"type":259,"value":24497},{"type":254,"tag":622,"props":29396,"children":29397},{"style":1202},[29398],{"type":259,"value":24840},{"type":254,"tag":622,"props":29400,"children":29401},{"style":629},[29402],{"type":259,"value":1654},{"type":254,"tag":622,"props":29404,"children":29405},{"style":629},[29406],{"type":259,"value":1624},{"type":254,"tag":622,"props":29408,"children":29409},{"style":1202},[29410],{"type":259,"value":24853},{"type":254,"tag":622,"props":29412,"children":29413},{"style":629},[29414],{"type":259,"value":594},{"type":254,"tag":622,"props":29416,"children":29417},{"style":1735},[29418],{"type":259,"value":24862},{"type":254,"tag":622,"props":29420,"children":29421},{"style":1202},[29422],{"type":259,"value":1668},{"type":254,"tag":622,"props":29424,"children":29425},{"style":629},[29426],{"type":259,"value":652},{"type":254,"tag":622,"props":29428,"children":29429},{"style":664},[29430],{"type":259,"value":24875},{"type":254,"tag":622,"props":29432,"children":29433},{"style":629},[29434],{"type":259,"value":652},{"type":254,"tag":622,"props":29436,"children":29437},{"style":629},[29438],{"type":259,"value":3149},{"type":254,"tag":622,"props":29440,"children":29441},{"style":629},[29442],{"type":259,"value":1990},{"type":254,"tag":622,"props":29444,"children":29445},{"style":768},[29446],{"type":259,"value":24892},{"type":254,"tag":622,"props":29448,"children":29449},{"style":629},[29450],{"type":259,"value":474},{"type":254,"tag":622,"props":29452,"children":29453},{"style":629},[29454],{"type":259,"value":661},{"type":254,"tag":622,"props":29456,"children":29457},{"style":664},[29458],{"type":259,"value":24905},{"type":254,"tag":622,"props":29460,"children":29461},{"style":629},[29462],{"type":259,"value":652},{"type":254,"tag":622,"props":29464,"children":29465},{"style":629},[29466],{"type":259,"value":14256},{"type":254,"tag":622,"props":29468,"children":29469},{"style":1202},[29470],{"type":259,"value":2309},{"type":254,"tag":622,"props":29472,"children":29473},{"style":629},[29474],{"type":259,"value":2350},{"type":254,"tag":622,"props":29476,"children":29477},{"class":624,"line":4131},[29478,29482,29486,29490,29494,29498,29502,29506,29510,29514,29518,29522,29526],{"type":254,"tag":622,"props":29479,"children":29480},{"style":644},[29481],{"type":259,"value":24497},{"type":254,"tag":622,"props":29483,"children":29484},{"style":1202},[29485],{"type":259,"value":24933},{"type":254,"tag":622,"props":29487,"children":29488},{"style":629},[29489],{"type":259,"value":1654},{"type":254,"tag":622,"props":29491,"children":29492},{"style":629},[29493],{"type":259,"value":1624},{"type":254,"tag":622,"props":29495,"children":29496},{"style":1202},[29497],{"type":259,"value":24853},{"type":254,"tag":622,"props":29499,"children":29500},{"style":629},[29501],{"type":259,"value":594},{"type":254,"tag":622,"props":29503,"children":29504},{"style":1735},[29505],{"type":259,"value":22028},{"type":254,"tag":622,"props":29507,"children":29508},{"style":1202},[29509],{"type":259,"value":1668},{"type":254,"tag":622,"props":29511,"children":29512},{"style":629},[29513],{"type":259,"value":652},{"type":254,"tag":622,"props":29515,"children":29516},{"style":664},[29517],{"type":259,"value":21813},{"type":254,"tag":622,"props":29519,"children":29520},{"style":629},[29521],{"type":259,"value":652},{"type":254,"tag":622,"props":29523,"children":29524},{"style":629},[29525],{"type":259,"value":3149},{"type":254,"tag":622,"props":29527,"children":29528},{"style":629},[29529],{"type":259,"value":702},{"type":254,"tag":622,"props":29531,"children":29532},{"class":624,"line":4183},[29533,29537,29541,29545,29549,29553],{"type":254,"tag":622,"props":29534,"children":29535},{"style":768},[29536],{"type":259,"value":24986},{"type":254,"tag":622,"props":29538,"children":29539},{"style":629},[29540],{"type":259,"value":474},{"type":254,"tag":622,"props":29542,"children":29543},{"style":1202},[29544],{"type":259,"value":21698},{"type":254,"tag":622,"props":29546,"children":29547},{"style":629},[29548],{"type":259,"value":594},{"type":254,"tag":622,"props":29550,"children":29551},{"style":1202},[29552],{"type":259,"value":25003},{"type":254,"tag":622,"props":29554,"children":29555},{"style":629},[29556],{"type":259,"value":676},{"type":254,"tag":622,"props":29558,"children":29559},{"class":624,"line":4208},[29560,29564,29568],{"type":254,"tag":622,"props":29561,"children":29562},{"style":629},[29563],{"type":259,"value":2421},{"type":254,"tag":622,"props":29565,"children":29566},{"style":1202},[29567],{"type":259,"value":2309},{"type":254,"tag":622,"props":29569,"children":29570},{"style":629},[29571],{"type":259,"value":2350},{"type":254,"tag":622,"props":29573,"children":29574},{"class":624,"line":4216},[29575,29579,29583,29587,29591,29595,29599,29603,29607,29611],{"type":254,"tag":622,"props":29576,"children":29577},{"style":629},[29578],{"type":259,"value":21937},{"type":254,"tag":622,"props":29580,"children":29581},{"style":1202},[29582],{"type":259,"value":24853},{"type":254,"tag":622,"props":29584,"children":29585},{"style":629},[29586],{"type":259,"value":594},{"type":254,"tag":622,"props":29588,"children":29589},{"style":1735},[29590],{"type":259,"value":25070},{"type":254,"tag":622,"props":29592,"children":29593},{"style":1202},[29594],{"type":259,"value":1668},{"type":254,"tag":622,"props":29596,"children":29597},{"style":629},[29598],{"type":259,"value":652},{"type":254,"tag":622,"props":29600,"children":29601},{"style":664},[29602],{"type":259,"value":25083},{"type":254,"tag":622,"props":29604,"children":29605},{"style":629},[29606],{"type":259,"value":652},{"type":254,"tag":622,"props":29608,"children":29609},{"style":629},[29610],{"type":259,"value":3149},{"type":254,"tag":622,"props":29612,"children":29613},{"style":629},[29614],{"type":259,"value":702},{"type":254,"tag":622,"props":29616,"children":29617},{"class":624,"line":4224},[29618,29622,29626,29630,29634,29638],{"type":254,"tag":622,"props":29619,"children":29620},{"style":768},[29621],{"type":259,"value":25103},{"type":254,"tag":622,"props":29623,"children":29624},{"style":629},[29625],{"type":259,"value":474},{"type":254,"tag":622,"props":29627,"children":29628},{"style":1202},[29629],{"type":259,"value":21698},{"type":254,"tag":622,"props":29631,"children":29632},{"style":629},[29633],{"type":259,"value":594},{"type":254,"tag":622,"props":29635,"children":29636},{"style":1202},[29637],{"type":259,"value":25120},{"type":254,"tag":622,"props":29639,"children":29640},{"style":629},[29641],{"type":259,"value":676},{"type":254,"tag":622,"props":29643,"children":29644},{"class":624,"line":4268},[29645,29649,29653,29657,29661,29665],{"type":254,"tag":622,"props":29646,"children":29647},{"style":768},[29648],{"type":259,"value":25132},{"type":254,"tag":622,"props":29650,"children":29651},{"style":629},[29652],{"type":259,"value":474},{"type":254,"tag":622,"props":29654,"children":29655},{"style":629},[29656],{"type":259,"value":661},{"type":254,"tag":622,"props":29658,"children":29659},{"style":664},[29660],{"type":259,"value":22248},{"type":254,"tag":622,"props":29662,"children":29663},{"style":629},[29664],{"type":259,"value":652},{"type":254,"tag":622,"props":29666,"children":29667},{"style":629},[29668],{"type":259,"value":676},{"type":254,"tag":622,"props":29670,"children":29671},{"class":624,"line":4276},[29672,29676,29680,29684,29688,29692],{"type":254,"tag":622,"props":29673,"children":29674},{"style":768},[29675],{"type":259,"value":25160},{"type":254,"tag":622,"props":29677,"children":29678},{"style":629},[29679],{"type":259,"value":474},{"type":254,"tag":622,"props":29681,"children":29682},{"style":629},[29683],{"type":259,"value":661},{"type":254,"tag":622,"props":29685,"children":29686},{"style":664},[29687],{"type":259,"value":22276},{"type":254,"tag":622,"props":29689,"children":29690},{"style":629},[29691],{"type":259,"value":652},{"type":254,"tag":622,"props":29693,"children":29694},{"style":629},[29695],{"type":259,"value":676},{"type":254,"tag":622,"props":29697,"children":29698},{"class":624,"line":4332},[29699,29703,29707,29711,29715,29719,29723,29727],{"type":254,"tag":622,"props":29700,"children":29701},{"style":768},[29702],{"type":259,"value":25188},{"type":254,"tag":622,"props":29704,"children":29705},{"style":629},[29706],{"type":259,"value":474},{"type":254,"tag":622,"props":29708,"children":29709},{"style":1202},[29710],{"type":259,"value":13676},{"type":254,"tag":622,"props":29712,"children":29713},{"style":629},[29714],{"type":259,"value":652},{"type":254,"tag":622,"props":29716,"children":29717},{"style":664},[29718],{"type":259,"value":17783},{"type":254,"tag":622,"props":29720,"children":29721},{"style":629},[29722],{"type":259,"value":652},{"type":254,"tag":622,"props":29724,"children":29725},{"style":1202},[29726],{"type":259,"value":20251},{"type":254,"tag":622,"props":29728,"children":29729},{"style":629},[29730],{"type":259,"value":676},{"type":254,"tag":622,"props":29732,"children":29733},{"class":624,"line":4376},[29734,29738,29742,29746,29750,29754],{"type":254,"tag":622,"props":29735,"children":29736},{"style":768},[29737],{"type":259,"value":25224},{"type":254,"tag":622,"props":29739,"children":29740},{"style":629},[29741],{"type":259,"value":474},{"type":254,"tag":622,"props":29743,"children":29744},{"style":629},[29745],{"type":259,"value":661},{"type":254,"tag":622,"props":29747,"children":29748},{"style":664},[29749],{"type":259,"value":17671},{"type":254,"tag":622,"props":29751,"children":29752},{"style":629},[29753],{"type":259,"value":652},{"type":254,"tag":622,"props":29755,"children":29756},{"style":629},[29757],{"type":259,"value":676},{"type":254,"tag":622,"props":29759,"children":29760},{"class":624,"line":4384},[29761,29765,29769,29773,29777,29781,29785,29789,29793,29797,29801,29805,29809,29813,29817],{"type":254,"tag":622,"props":29762,"children":29763},{"style":768},[29764],{"type":259,"value":25252},{"type":254,"tag":622,"props":29766,"children":29767},{"style":629},[29768],{"type":259,"value":474},{"type":254,"tag":622,"props":29770,"children":29771},{"style":1202},[29772],{"type":259,"value":25261},{"type":254,"tag":622,"props":29774,"children":29775},{"style":629},[29776],{"type":259,"value":594},{"type":254,"tag":622,"props":29778,"children":29779},{"style":1735},[29780],{"type":259,"value":25270},{"type":254,"tag":622,"props":29782,"children":29783},{"style":629},[29784],{"type":259,"value":25275},{"type":254,"tag":622,"props":29786,"children":29787},{"style":664},[29788],{"type":259,"value":17696},{"type":254,"tag":622,"props":29790,"children":29791},{"style":629},[29792],{"type":259,"value":17340},{"type":254,"tag":622,"props":29794,"children":29795},{"style":1202},[29796],{"type":259,"value":25288},{"type":254,"tag":622,"props":29798,"children":29799},{"style":629},[29800],{"type":259,"value":594},{"type":254,"tag":622,"props":29802,"children":29803},{"style":1202},[29804],{"type":259,"value":25297},{"type":254,"tag":622,"props":29806,"children":29807},{"style":629},[29808],{"type":259,"value":2421},{"type":254,"tag":622,"props":29810,"children":29811},{"style":664},[29812],{"type":259,"value":17713},{"type":254,"tag":622,"props":29814,"children":29815},{"style":629},[29816],{"type":259,"value":25275},{"type":254,"tag":622,"props":29818,"children":29819},{"style":629},[29820],{"type":259,"value":676},{"type":254,"tag":622,"props":29822,"children":29823},{"class":624,"line":4416},[29824,29828,29832],{"type":254,"tag":622,"props":29825,"children":29826},{"style":629},[29827],{"type":259,"value":2421},{"type":254,"tag":622,"props":29829,"children":29830},{"style":1202},[29831],{"type":259,"value":2309},{"type":254,"tag":622,"props":29833,"children":29834},{"style":629},[29835],{"type":259,"value":2350},{"type":254,"tag":622,"props":29837,"children":29838},{"class":624,"line":4456},[29839],{"type":254,"tag":622,"props":29840,"children":29841},{"emptyLinePlaceholder":1699},[29842],{"type":259,"value":1702},{"type":254,"tag":622,"props":29844,"children":29845},{"class":624,"line":4488},[29846,29850,29854,29858,29862,29866,29870,29874,29878,29882,29886],{"type":254,"tag":622,"props":29847,"children":29848},{"style":644},[29849],{"type":259,"value":24497},{"type":254,"tag":622,"props":29851,"children":29852},{"style":1202},[29853],{"type":259,"value":25833},{"type":254,"tag":622,"props":29855,"children":29856},{"style":629},[29857],{"type":259,"value":1654},{"type":254,"tag":622,"props":29859,"children":29860},{"style":1202},[29861],{"type":259,"value":25261},{"type":254,"tag":622,"props":29863,"children":29864},{"style":629},[29865],{"type":259,"value":594},{"type":254,"tag":622,"props":29867,"children":29868},{"style":1735},[29869],{"type":259,"value":24715},{"type":254,"tag":622,"props":29871,"children":29872},{"style":1202},[29873],{"type":259,"value":25854},{"type":254,"tag":622,"props":29875,"children":29876},{"style":629},[29877],{"type":259,"value":594},{"type":254,"tag":622,"props":29879,"children":29880},{"style":1735},[29881],{"type":259,"value":25863},{"type":254,"tag":622,"props":29883,"children":29884},{"style":1202},[29885],{"type":259,"value":25868},{"type":254,"tag":622,"props":29887,"children":29888},{"style":629},[29889],{"type":259,"value":2350},{"type":254,"tag":622,"props":29891,"children":29892},{"class":624,"line":4496},[29893,29897,29901,29905,29909,29913,29917],{"type":254,"tag":622,"props":29894,"children":29895},{"style":644},[29896],{"type":259,"value":24497},{"type":254,"tag":622,"props":29898,"children":29899},{"style":1202},[29900],{"type":259,"value":25768},{"type":254,"tag":622,"props":29902,"children":29903},{"style":629},[29904],{"type":259,"value":1654},{"type":254,"tag":622,"props":29906,"children":29907},{"style":1202},[29908],{"type":259,"value":21237},{"type":254,"tag":622,"props":29910,"children":29911},{"style":629},[29912],{"type":259,"value":594},{"type":254,"tag":622,"props":29914,"children":29915},{"style":1202},[29916],{"type":259,"value":17148},{"type":254,"tag":622,"props":29918,"children":29919},{"style":629},[29920],{"type":259,"value":2350},{"type":254,"tag":622,"props":29922,"children":29923},{"class":624,"line":4564},[29924],{"type":254,"tag":622,"props":29925,"children":29926},{"emptyLinePlaceholder":1699},[29927],{"type":259,"value":1702},{"type":254,"tag":622,"props":29929,"children":29930},{"class":624,"line":4572},[29931,29935,29939,29943,29947,29951,29955,29959,29963,29967],{"type":254,"tag":622,"props":29932,"children":29933},{"style":629},[29934],{"type":259,"value":21937},{"type":254,"tag":622,"props":29936,"children":29937},{"style":1202},[29938],{"type":259,"value":25506},{"type":254,"tag":622,"props":29940,"children":29941},{"style":629},[29942],{"type":259,"value":594},{"type":254,"tag":622,"props":29944,"children":29945},{"style":1735},[29946],{"type":259,"value":25515},{"type":254,"tag":622,"props":29948,"children":29949},{"style":1202},[29950],{"type":259,"value":1668},{"type":254,"tag":622,"props":29952,"children":29953},{"style":629},[29954],{"type":259,"value":652},{"type":254,"tag":622,"props":29956,"children":29957},{"style":664},[29958],{"type":259,"value":21955},{"type":254,"tag":622,"props":29960,"children":29961},{"style":629},[29962],{"type":259,"value":652},{"type":254,"tag":622,"props":29964,"children":29965},{"style":629},[29966],{"type":259,"value":3149},{"type":254,"tag":622,"props":29968,"children":29969},{"style":629},[29970],{"type":259,"value":702},{"type":254,"tag":622,"props":29972,"children":29973},{"class":624,"line":4644},[29974,29978,29982,29986,29990,29994],{"type":254,"tag":622,"props":29975,"children":29976},{"style":768},[29977],{"type":259,"value":25547},{"type":254,"tag":622,"props":29979,"children":29980},{"style":629},[29981],{"type":259,"value":474},{"type":254,"tag":622,"props":29983,"children":29984},{"style":1202},[29985],{"type":259,"value":21178},{"type":254,"tag":622,"props":29987,"children":29988},{"style":629},[29989],{"type":259,"value":594},{"type":254,"tag":622,"props":29991,"children":29992},{"style":1202},[29993],{"type":259,"value":17175},{"type":254,"tag":622,"props":29995,"children":29996},{"style":629},[29997],{"type":259,"value":676},{"type":254,"tag":622,"props":29999,"children":30000},{"class":624,"line":4652},[30001,30005,30009,30013,30017,30021,30025,30029],{"type":254,"tag":622,"props":30002,"children":30003},{"style":768},[30004],{"type":259,"value":25575},{"type":254,"tag":622,"props":30006,"children":30007},{"style":629},[30008],{"type":259,"value":474},{"type":254,"tag":622,"props":30010,"children":30011},{"style":1202},[30012],{"type":259,"value":25506},{"type":254,"tag":622,"props":30014,"children":30015},{"style":629},[30016],{"type":259,"value":594},{"type":254,"tag":622,"props":30018,"children":30019},{"style":1202},[30020],{"type":259,"value":25592},{"type":254,"tag":622,"props":30022,"children":30023},{"style":629},[30024],{"type":259,"value":594},{"type":254,"tag":622,"props":30026,"children":30027},{"style":1202},[30028],{"type":259,"value":22028},{"type":254,"tag":622,"props":30030,"children":30031},{"style":629},[30032],{"type":259,"value":676},{"type":254,"tag":622,"props":30034,"children":30035},{"class":624,"line":4660},[30036,30040,30044,30048,30052,30056],{"type":254,"tag":622,"props":30037,"children":30038},{"style":768},[30039],{"type":259,"value":25612},{"type":254,"tag":622,"props":30041,"children":30042},{"style":629},[30043],{"type":259,"value":474},{"type":254,"tag":622,"props":30045,"children":30046},{"style":1202},[30047],{"type":259,"value":25461},{"type":254,"tag":622,"props":30049,"children":30050},{"style":629},[30051],{"type":259,"value":594},{"type":254,"tag":622,"props":30053,"children":30054},{"style":1202},[30055],{"type":259,"value":21955},{"type":254,"tag":622,"props":30057,"children":30058},{"style":629},[30059],{"type":259,"value":676},{"type":254,"tag":622,"props":30061,"children":30062},{"class":624,"line":4692},[30063,30067,30071,30075,30079,30083,30087,30091,30095,30099,30103],{"type":254,"tag":622,"props":30064,"children":30065},{"style":768},[30066],{"type":259,"value":25640},{"type":254,"tag":622,"props":30068,"children":30069},{"style":629},[30070],{"type":259,"value":474},{"type":254,"tag":622,"props":30072,"children":30073},{"style":1202},[30074],{"type":259,"value":25261},{"type":254,"tag":622,"props":30076,"children":30077},{"style":629},[30078],{"type":259,"value":594},{"type":254,"tag":622,"props":30080,"children":30081},{"style":1735},[30082],{"type":259,"value":25270},{"type":254,"tag":622,"props":30084,"children":30085},{"style":629},[30086],{"type":259,"value":25275},{"type":254,"tag":622,"props":30088,"children":30089},{"style":664},[30090],{"type":259,"value":21541},{"type":254,"tag":622,"props":30092,"children":30093},{"style":629},[30094],{"type":259,"value":17340},{"type":254,"tag":622,"props":30096,"children":30097},{"style":1202},[30098],{"type":259,"value":17148},{"type":254,"tag":622,"props":30100,"children":30101},{"style":629},[30102],{"type":259,"value":25677},{"type":254,"tag":622,"props":30104,"children":30105},{"style":629},[30106],{"type":259,"value":676},{"type":254,"tag":622,"props":30108,"children":30109},{"class":624,"line":4700},[30110,30114,30118],{"type":254,"tag":622,"props":30111,"children":30112},{"style":629},[30113],{"type":259,"value":2421},{"type":254,"tag":622,"props":30115,"children":30116},{"style":1202},[30117],{"type":259,"value":2309},{"type":254,"tag":622,"props":30119,"children":30120},{"style":629},[30121],{"type":259,"value":2350},{"type":254,"tag":622,"props":30123,"children":30124},{"class":624,"line":4756},[30125],{"type":254,"tag":622,"props":30126,"children":30127},{"emptyLinePlaceholder":1699},[30128],{"type":259,"value":1702},{"type":254,"tag":622,"props":30130,"children":30131},{"class":624,"line":4808},[30132,30136,30140,30144,30148,30152,30156,30160,30164,30168],{"type":254,"tag":622,"props":30133,"children":30134},{"style":629},[30135],{"type":259,"value":21937},{"type":254,"tag":622,"props":30137,"children":30138},{"style":1202},[30139],{"type":259,"value":24515},{"type":254,"tag":622,"props":30141,"children":30142},{"style":629},[30143],{"type":259,"value":594},{"type":254,"tag":622,"props":30145,"children":30146},{"style":1735},[30147],{"type":259,"value":26268},{"type":254,"tag":622,"props":30149,"children":30150},{"style":1202},[30151],{"type":259,"value":1668},{"type":254,"tag":622,"props":30153,"children":30154},{"style":629},[30155],{"type":259,"value":652},{"type":254,"tag":622,"props":30157,"children":30158},{"style":664},[30159],{"type":259,"value":17204},{"type":254,"tag":622,"props":30161,"children":30162},{"style":629},[30163],{"type":259,"value":652},{"type":254,"tag":622,"props":30165,"children":30166},{"style":629},[30167],{"type":259,"value":3149},{"type":254,"tag":622,"props":30169,"children":30170},{"style":629},[30171],{"type":259,"value":702},{"type":254,"tag":622,"props":30173,"children":30174},{"class":624,"line":4864},[30175,30179,30183,30187,30191,30195],{"type":254,"tag":622,"props":30176,"children":30177},{"style":768},[30178],{"type":259,"value":26090},{"type":254,"tag":622,"props":30180,"children":30181},{"style":629},[30182],{"type":259,"value":474},{"type":254,"tag":622,"props":30184,"children":30185},{"style":1202},[30186],{"type":259,"value":20498},{"type":254,"tag":622,"props":30188,"children":30189},{"style":629},[30190],{"type":259,"value":594},{"type":254,"tag":622,"props":30192,"children":30193},{"style":1202},[30194],{"type":259,"value":16394},{"type":254,"tag":622,"props":30196,"children":30197},{"style":629},[30198],{"type":259,"value":676},{"type":254,"tag":622,"props":30200,"children":30201},{"class":624,"line":4892},[30202,30206,30210,30214,30218,30222],{"type":254,"tag":622,"props":30203,"children":30204},{"style":768},[30205],{"type":259,"value":26327},{"type":254,"tag":622,"props":30207,"children":30208},{"style":629},[30209],{"type":259,"value":474},{"type":254,"tag":622,"props":30211,"children":30212},{"style":629},[30213],{"type":259,"value":661},{"type":254,"tag":622,"props":30215,"children":30216},{"style":664},[30217],{"type":259,"value":26131},{"type":254,"tag":622,"props":30219,"children":30220},{"style":629},[30221],{"type":259,"value":652},{"type":254,"tag":622,"props":30223,"children":30224},{"style":629},[30225],{"type":259,"value":676},{"type":254,"tag":622,"props":30227,"children":30228},{"class":624,"line":4900},[30229,30233,30237,30241,30245,30249],{"type":254,"tag":622,"props":30230,"children":30231},{"style":768},[30232],{"type":259,"value":26355},{"type":254,"tag":622,"props":30234,"children":30235},{"style":629},[30236],{"type":259,"value":474},{"type":254,"tag":622,"props":30238,"children":30239},{"style":1202},[30240],{"type":259,"value":21237},{"type":254,"tag":622,"props":30242,"children":30243},{"style":629},[30244],{"type":259,"value":594},{"type":254,"tag":622,"props":30246,"children":30247},{"style":1202},[30248],{"type":259,"value":17204},{"type":254,"tag":622,"props":30250,"children":30251},{"style":629},[30252],{"type":259,"value":676},{"type":254,"tag":622,"props":30254,"children":30255},{"class":624,"line":4944},[30256,30260,30264],{"type":254,"tag":622,"props":30257,"children":30258},{"style":629},[30259],{"type":259,"value":2421},{"type":254,"tag":622,"props":30261,"children":30262},{"style":1202},[30263],{"type":259,"value":2309},{"type":254,"tag":622,"props":30265,"children":30266},{"style":629},[30267],{"type":259,"value":2350},{"type":254,"tag":622,"props":30269,"children":30270},{"class":624,"line":4952},[30271],{"type":254,"tag":622,"props":30272,"children":30273},{"emptyLinePlaceholder":1699},[30274],{"type":259,"value":1702},{"type":254,"tag":622,"props":30276,"children":30277},{"class":624,"line":4960},[30278,30282,30286,30290,30294,30298,30302,30306,30310,30314],{"type":254,"tag":622,"props":30279,"children":30280},{"style":629},[30281],{"type":259,"value":21937},{"type":254,"tag":622,"props":30283,"children":30284},{"style":1202},[30285],{"type":259,"value":24515},{"type":254,"tag":622,"props":30287,"children":30288},{"style":629},[30289],{"type":259,"value":594},{"type":254,"tag":622,"props":30291,"children":30292},{"style":1735},[30293],{"type":259,"value":26268},{"type":254,"tag":622,"props":30295,"children":30296},{"style":1202},[30297],{"type":259,"value":1668},{"type":254,"tag":622,"props":30299,"children":30300},{"style":629},[30301],{"type":259,"value":652},{"type":254,"tag":622,"props":30303,"children":30304},{"style":664},[30305],{"type":259,"value":17148},{"type":254,"tag":622,"props":30307,"children":30308},{"style":629},[30309],{"type":259,"value":652},{"type":254,"tag":622,"props":30311,"children":30312},{"style":629},[30313],{"type":259,"value":3149},{"type":254,"tag":622,"props":30315,"children":30316},{"style":629},[30317],{"type":259,"value":702},{"type":254,"tag":622,"props":30319,"children":30320},{"class":624,"line":4968},[30321,30325,30329,30333,30337,30341],{"type":254,"tag":622,"props":30322,"children":30323},{"style":768},[30324],{"type":259,"value":26090},{"type":254,"tag":622,"props":30326,"children":30327},{"style":629},[30328],{"type":259,"value":474},{"type":254,"tag":622,"props":30330,"children":30331},{"style":1202},[30332],{"type":259,"value":20498},{"type":254,"tag":622,"props":30334,"children":30335},{"style":629},[30336],{"type":259,"value":594},{"type":254,"tag":622,"props":30338,"children":30339},{"style":1202},[30340],{"type":259,"value":16394},{"type":254,"tag":622,"props":30342,"children":30343},{"style":629},[30344],{"type":259,"value":676},{"type":254,"tag":622,"props":30346,"children":30347},{"class":624,"line":4996},[30348,30352,30356,30360,30364,30368],{"type":254,"tag":622,"props":30349,"children":30350},{"style":768},[30351],{"type":259,"value":26327},{"type":254,"tag":622,"props":30353,"children":30354},{"style":629},[30355],{"type":259,"value":474},{"type":254,"tag":622,"props":30357,"children":30358},{"style":629},[30359],{"type":259,"value":661},{"type":254,"tag":622,"props":30361,"children":30362},{"style":664},[30363],{"type":259,"value":26487},{"type":254,"tag":622,"props":30365,"children":30366},{"style":629},[30367],{"type":259,"value":652},{"type":254,"tag":622,"props":30369,"children":30370},{"style":629},[30371],{"type":259,"value":676},{"type":254,"tag":622,"props":30373,"children":30374},{"class":624,"line":5004},[30375,30379,30383,30387,30391,30395],{"type":254,"tag":622,"props":30376,"children":30377},{"style":768},[30378],{"type":259,"value":26355},{"type":254,"tag":622,"props":30380,"children":30381},{"style":629},[30382],{"type":259,"value":474},{"type":254,"tag":622,"props":30384,"children":30385},{"style":1202},[30386],{"type":259,"value":21237},{"type":254,"tag":622,"props":30388,"children":30389},{"style":629},[30390],{"type":259,"value":594},{"type":254,"tag":622,"props":30392,"children":30393},{"style":1202},[30394],{"type":259,"value":17148},{"type":254,"tag":622,"props":30396,"children":30397},{"style":629},[30398],{"type":259,"value":676},{"type":254,"tag":622,"props":30400,"children":30401},{"class":624,"line":5060},[30402,30406,30410],{"type":254,"tag":622,"props":30403,"children":30404},{"style":629},[30405],{"type":259,"value":2421},{"type":254,"tag":622,"props":30407,"children":30408},{"style":1202},[30409],{"type":259,"value":2309},{"type":254,"tag":622,"props":30411,"children":30412},{"style":629},[30413],{"type":259,"value":2350},{"type":254,"tag":622,"props":30415,"children":30416},{"class":624,"line":5116},[30417],{"type":254,"tag":622,"props":30418,"children":30419},{"emptyLinePlaceholder":1699},[30420],{"type":259,"value":1702},{"type":254,"tag":622,"props":30422,"children":30423},{"class":624,"line":5208},[30424,30428,30432,30436,30440,30444,30448,30452,30456,30460],{"type":254,"tag":622,"props":30425,"children":30426},{"style":629},[30427],{"type":259,"value":21937},{"type":254,"tag":622,"props":30429,"children":30430},{"style":1202},[30431],{"type":259,"value":24515},{"type":254,"tag":622,"props":30433,"children":30434},{"style":629},[30435],{"type":259,"value":594},{"type":254,"tag":622,"props":30437,"children":30438},{"style":1735},[30439],{"type":259,"value":26268},{"type":254,"tag":622,"props":30441,"children":30442},{"style":1202},[30443],{"type":259,"value":1668},{"type":254,"tag":622,"props":30445,"children":30446},{"style":629},[30447],{"type":259,"value":652},{"type":254,"tag":622,"props":30449,"children":30450},{"style":664},[30451],{"type":259,"value":551},{"type":254,"tag":622,"props":30453,"children":30454},{"style":629},[30455],{"type":259,"value":652},{"type":254,"tag":622,"props":30457,"children":30458},{"style":629},[30459],{"type":259,"value":3149},{"type":254,"tag":622,"props":30461,"children":30462},{"style":629},[30463],{"type":259,"value":702},{"type":254,"tag":622,"props":30465,"children":30466},{"class":624,"line":5240},[30467,30471,30475,30479,30483,30487],{"type":254,"tag":622,"props":30468,"children":30469},{"style":768},[30470],{"type":259,"value":26090},{"type":254,"tag":622,"props":30472,"children":30473},{"style":629},[30474],{"type":259,"value":474},{"type":254,"tag":622,"props":30476,"children":30477},{"style":1202},[30478],{"type":259,"value":20498},{"type":254,"tag":622,"props":30480,"children":30481},{"style":629},[30482],{"type":259,"value":594},{"type":254,"tag":622,"props":30484,"children":30485},{"style":1202},[30486],{"type":259,"value":16394},{"type":254,"tag":622,"props":30488,"children":30489},{"style":629},[30490],{"type":259,"value":676},{"type":254,"tag":622,"props":30492,"children":30493},{"class":624,"line":5260},[30494,30498,30502,30506,30510,30514],{"type":254,"tag":622,"props":30495,"children":30496},{"style":768},[30497],{"type":259,"value":26327},{"type":254,"tag":622,"props":30499,"children":30500},{"style":629},[30501],{"type":259,"value":474},{"type":254,"tag":622,"props":30503,"children":30504},{"style":629},[30505],{"type":259,"value":661},{"type":254,"tag":622,"props":30507,"children":30508},{"style":664},[30509],{"type":259,"value":26634},{"type":254,"tag":622,"props":30511,"children":30512},{"style":629},[30513],{"type":259,"value":652},{"type":254,"tag":622,"props":30515,"children":30516},{"style":629},[30517],{"type":259,"value":676},{"type":254,"tag":622,"props":30519,"children":30520},{"class":624,"line":5332},[30521,30525,30529,30533,30537,30541],{"type":254,"tag":622,"props":30522,"children":30523},{"style":768},[30524],{"type":259,"value":26355},{"type":254,"tag":622,"props":30526,"children":30527},{"style":629},[30528],{"type":259,"value":474},{"type":254,"tag":622,"props":30530,"children":30531},{"style":1202},[30532],{"type":259,"value":21698},{"type":254,"tag":622,"props":30534,"children":30535},{"style":629},[30536],{"type":259,"value":594},{"type":254,"tag":622,"props":30538,"children":30539},{"style":1202},[30540],{"type":259,"value":25003},{"type":254,"tag":622,"props":30542,"children":30543},{"style":629},[30544],{"type":259,"value":676},{"type":254,"tag":622,"props":30546,"children":30547},{"class":624,"line":5352},[30548,30552,30556],{"type":254,"tag":622,"props":30549,"children":30550},{"style":629},[30551],{"type":259,"value":2421},{"type":254,"tag":622,"props":30553,"children":30554},{"style":1202},[30555],{"type":259,"value":2309},{"type":254,"tag":622,"props":30557,"children":30558},{"style":629},[30559],{"type":259,"value":2350},{"type":254,"tag":622,"props":30561,"children":30562},{"class":624,"line":5364},[30563],{"type":254,"tag":622,"props":30564,"children":30565},{"emptyLinePlaceholder":1699},[30566],{"type":259,"value":1702},{"type":254,"tag":622,"props":30568,"children":30569},{"class":624,"line":5376},[30570,30574,30578,30582,30586,30590,30594,30598,30602,30606],{"type":254,"tag":622,"props":30571,"children":30572},{"style":629},[30573],{"type":259,"value":21937},{"type":254,"tag":622,"props":30575,"children":30576},{"style":1202},[30577],{"type":259,"value":24515},{"type":254,"tag":622,"props":30579,"children":30580},{"style":629},[30581],{"type":259,"value":594},{"type":254,"tag":622,"props":30583,"children":30584},{"style":1735},[30585],{"type":259,"value":26268},{"type":254,"tag":622,"props":30587,"children":30588},{"style":1202},[30589],{"type":259,"value":1668},{"type":254,"tag":622,"props":30591,"children":30592},{"style":629},[30593],{"type":259,"value":652},{"type":254,"tag":622,"props":30595,"children":30596},{"style":664},[30597],{"type":259,"value":27932},{"type":254,"tag":622,"props":30599,"children":30600},{"style":629},[30601],{"type":259,"value":652},{"type":254,"tag":622,"props":30603,"children":30604},{"style":629},[30605],{"type":259,"value":3149},{"type":254,"tag":622,"props":30607,"children":30608},{"style":629},[30609],{"type":259,"value":702},{"type":254,"tag":622,"props":30611,"children":30612},{"class":624,"line":5396},[30613,30617,30621,30625,30629,30633],{"type":254,"tag":622,"props":30614,"children":30615},{"style":768},[30616],{"type":259,"value":26090},{"type":254,"tag":622,"props":30618,"children":30619},{"style":629},[30620],{"type":259,"value":474},{"type":254,"tag":622,"props":30622,"children":30623},{"style":1202},[30624],{"type":259,"value":20498},{"type":254,"tag":622,"props":30626,"children":30627},{"style":629},[30628],{"type":259,"value":594},{"type":254,"tag":622,"props":30630,"children":30631},{"style":1202},[30632],{"type":259,"value":16394},{"type":254,"tag":622,"props":30634,"children":30635},{"style":629},[30636],{"type":259,"value":676},{"type":254,"tag":622,"props":30638,"children":30639},{"class":624,"line":5464},[30640,30644,30648,30652,30656,30660],{"type":254,"tag":622,"props":30641,"children":30642},{"style":768},[30643],{"type":259,"value":26327},{"type":254,"tag":622,"props":30645,"children":30646},{"style":629},[30647],{"type":259,"value":474},{"type":254,"tag":622,"props":30649,"children":30650},{"style":629},[30651],{"type":259,"value":661},{"type":254,"tag":622,"props":30653,"children":30654},{"style":664},[30655],{"type":259,"value":27851},{"type":254,"tag":622,"props":30657,"children":30658},{"style":629},[30659],{"type":259,"value":652},{"type":254,"tag":622,"props":30661,"children":30662},{"style":629},[30663],{"type":259,"value":676},{"type":254,"tag":622,"props":30665,"children":30666},{"class":624,"line":5484},[30667,30671,30675,30679,30683,30687,30691,30695,30699,30703,30707],{"type":254,"tag":622,"props":30668,"children":30669},{"style":768},[30670],{"type":259,"value":26355},{"type":254,"tag":622,"props":30672,"children":30673},{"style":629},[30674],{"type":259,"value":474},{"type":254,"tag":622,"props":30676,"children":30677},{"style":1202},[30678],{"type":259,"value":20010},{"type":254,"tag":622,"props":30680,"children":30681},{"style":629},[30682],{"type":259,"value":594},{"type":254,"tag":622,"props":30684,"children":30685},{"style":1735},[30686],{"type":259,"value":28022},{"type":254,"tag":622,"props":30688,"children":30689},{"style":1202},[30690],{"type":259,"value":1668},{"type":254,"tag":622,"props":30692,"children":30693},{"style":629},[30694],{"type":259,"value":652},{"type":254,"tag":622,"props":30696,"children":30697},{"style":664},[30698],{"type":259,"value":28035},{"type":254,"tag":622,"props":30700,"children":30701},{"style":629},[30702],{"type":259,"value":652},{"type":254,"tag":622,"props":30704,"children":30705},{"style":1202},[30706],{"type":259,"value":2309},{"type":254,"tag":622,"props":30708,"children":30709},{"style":629},[30710],{"type":259,"value":676},{"type":254,"tag":622,"props":30712,"children":30713},{"class":624,"line":5500},[30714,30718,30722],{"type":254,"tag":622,"props":30715,"children":30716},{"style":629},[30717],{"type":259,"value":2421},{"type":254,"tag":622,"props":30719,"children":30720},{"style":1202},[30721],{"type":259,"value":2309},{"type":254,"tag":622,"props":30723,"children":30724},{"style":629},[30725],{"type":259,"value":2350},{"type":254,"tag":622,"props":30727,"children":30728},{"class":624,"line":5508},[30729],{"type":254,"tag":622,"props":30730,"children":30731},{"emptyLinePlaceholder":1699},[30732],{"type":259,"value":1702},{"type":254,"tag":622,"props":30734,"children":30735},{"class":624,"line":5516},[30736,30740,30744,30748,30752,30756,30760,30764,30768,30772,30776,30780,30784,30788],{"type":254,"tag":622,"props":30737,"children":30738},{"style":644},[30739],{"type":259,"value":24497},{"type":254,"tag":622,"props":30741,"children":30742},{"style":1202},[30743],{"type":259,"value":28134},{"type":254,"tag":622,"props":30745,"children":30746},{"style":629},[30747],{"type":259,"value":1654},{"type":254,"tag":622,"props":30749,"children":30750},{"style":1735},[30751],{"type":259,"value":28090},{"type":254,"tag":622,"props":30753,"children":30754},{"style":1202},[30755],{"type":259,"value":1668},{"type":254,"tag":622,"props":30757,"children":30758},{"style":629},[30759],{"type":259,"value":652},{"type":254,"tag":622,"props":30761,"children":30762},{"style":664},[30763],{"type":259,"value":28155},{"type":254,"tag":622,"props":30765,"children":30766},{"style":629},[30767],{"type":259,"value":652},{"type":254,"tag":622,"props":30769,"children":30770},{"style":629},[30771],{"type":259,"value":3149},{"type":254,"tag":622,"props":30773,"children":30774},{"style":629},[30775],{"type":259,"value":661},{"type":254,"tag":622,"props":30777,"children":30778},{"style":664},[30779],{"type":259,"value":28172},{"type":254,"tag":622,"props":30781,"children":30782},{"style":629},[30783],{"type":259,"value":652},{"type":254,"tag":622,"props":30785,"children":30786},{"style":1202},[30787],{"type":259,"value":2309},{"type":254,"tag":622,"props":30789,"children":30790},{"style":629},[30791],{"type":259,"value":2350},{"type":254,"tag":622,"props":30793,"children":30794},{"class":624,"line":5524},[30795,30799,30803,30807,30811,30815,30819,30823,30827,30831],{"type":254,"tag":622,"props":30796,"children":30797},{"style":629},[30798],{"type":259,"value":21937},{"type":254,"tag":622,"props":30800,"children":30801},{"style":1202},[30802],{"type":259,"value":24515},{"type":254,"tag":622,"props":30804,"children":30805},{"style":629},[30806],{"type":259,"value":594},{"type":254,"tag":622,"props":30808,"children":30809},{"style":1735},[30810],{"type":259,"value":28204},{"type":254,"tag":622,"props":30812,"children":30813},{"style":1202},[30814],{"type":259,"value":1668},{"type":254,"tag":622,"props":30816,"children":30817},{"style":629},[30818],{"type":259,"value":652},{"type":254,"tag":622,"props":30820,"children":30821},{"style":664},[30822],{"type":259,"value":28217},{"type":254,"tag":622,"props":30824,"children":30825},{"style":629},[30826],{"type":259,"value":652},{"type":254,"tag":622,"props":30828,"children":30829},{"style":629},[30830],{"type":259,"value":3149},{"type":254,"tag":622,"props":30832,"children":30833},{"style":629},[30834],{"type":259,"value":702},{"type":254,"tag":622,"props":30836,"children":30837},{"class":624,"line":5544},[30838,30842,30846,30850,30854,30858],{"type":254,"tag":622,"props":30839,"children":30840},{"style":768},[30841],{"type":259,"value":26090},{"type":254,"tag":622,"props":30843,"children":30844},{"style":629},[30845],{"type":259,"value":474},{"type":254,"tag":622,"props":30847,"children":30848},{"style":1202},[30849],{"type":259,"value":20498},{"type":254,"tag":622,"props":30851,"children":30852},{"style":629},[30853],{"type":259,"value":594},{"type":254,"tag":622,"props":30855,"children":30856},{"style":1202},[30857],{"type":259,"value":16394},{"type":254,"tag":622,"props":30859,"children":30860},{"style":629},[30861],{"type":259,"value":676},{"type":254,"tag":622,"props":30863,"children":30864},{"class":624,"line":5560},[30865,30869,30873,30877,30881,30885],{"type":254,"tag":622,"props":30866,"children":30867},{"style":768},[30868],{"type":259,"value":28264},{"type":254,"tag":622,"props":30870,"children":30871},{"style":629},[30872],{"type":259,"value":474},{"type":254,"tag":622,"props":30874,"children":30875},{"style":629},[30876],{"type":259,"value":661},{"type":254,"tag":622,"props":30878,"children":30879},{"style":664},[30880],{"type":259,"value":23830},{"type":254,"tag":622,"props":30882,"children":30883},{"style":629},[30884],{"type":259,"value":652},{"type":254,"tag":622,"props":30886,"children":30887},{"style":629},[30888],{"type":259,"value":676},{"type":254,"tag":622,"props":30890,"children":30891},{"class":624,"line":5576},[30892,30896,30900,30904,30908,30912],{"type":254,"tag":622,"props":30893,"children":30894},{"style":768},[30895],{"type":259,"value":28292},{"type":254,"tag":622,"props":30897,"children":30898},{"style":629},[30899],{"type":259,"value":474},{"type":254,"tag":622,"props":30901,"children":30902},{"style":629},[30903],{"type":259,"value":661},{"type":254,"tag":622,"props":30905,"children":30906},{"style":664},[30907],{"type":259,"value":28305},{"type":254,"tag":622,"props":30909,"children":30910},{"style":629},[30911],{"type":259,"value":652},{"type":254,"tag":622,"props":30913,"children":30914},{"style":629},[30915],{"type":259,"value":676},{"type":254,"tag":622,"props":30917,"children":30918},{"class":624,"line":5592},[30919,30923,30927,30931],{"type":254,"tag":622,"props":30920,"children":30921},{"style":768},[30922],{"type":259,"value":28321},{"type":254,"tag":622,"props":30924,"children":30925},{"style":629},[30926],{"type":259,"value":474},{"type":254,"tag":622,"props":30928,"children":30929},{"style":1202},[30930],{"type":259,"value":28330},{"type":254,"tag":622,"props":30932,"children":30933},{"style":629},[30934],{"type":259,"value":676},{"type":254,"tag":622,"props":30936,"children":30937},{"class":624,"line":5608},[30938,30942,30946,30950,30954,30958],{"type":254,"tag":622,"props":30939,"children":30940},{"style":768},[30941],{"type":259,"value":28342},{"type":254,"tag":622,"props":30943,"children":30944},{"style":629},[30945],{"type":259,"value":474},{"type":254,"tag":622,"props":30947,"children":30948},{"style":629},[30949],{"type":259,"value":661},{"type":254,"tag":622,"props":30951,"children":30952},{"style":664},[30953],{"type":259,"value":22902},{"type":254,"tag":622,"props":30955,"children":30956},{"style":629},[30957],{"type":259,"value":652},{"type":254,"tag":622,"props":30959,"children":30960},{"style":629},[30961],{"type":259,"value":676},{"type":254,"tag":622,"props":30963,"children":30964},{"class":624,"line":5624},[30965,30969,30973,30977,30981,30985],{"type":254,"tag":622,"props":30966,"children":30967},{"style":768},[30968],{"type":259,"value":28370},{"type":254,"tag":622,"props":30970,"children":30971},{"style":629},[30972],{"type":259,"value":474},{"type":254,"tag":622,"props":30974,"children":30975},{"style":629},[30976],{"type":259,"value":661},{"type":254,"tag":622,"props":30978,"children":30979},{"style":664},[30980],{"type":259,"value":28383},{"type":254,"tag":622,"props":30982,"children":30983},{"style":629},[30984],{"type":259,"value":652},{"type":254,"tag":622,"props":30986,"children":30987},{"style":629},[30988],{"type":259,"value":676},{"type":254,"tag":622,"props":30990,"children":30991},{"class":624,"line":5636},[30992,30996,31000,31004,31008,31012],{"type":254,"tag":622,"props":30993,"children":30994},{"style":768},[30995],{"type":259,"value":28399},{"type":254,"tag":622,"props":30997,"children":30998},{"style":629},[30999],{"type":259,"value":474},{"type":254,"tag":622,"props":31001,"children":31002},{"style":629},[31003],{"type":259,"value":661},{"type":254,"tag":622,"props":31005,"children":31006},{"style":664},[31007],{"type":259,"value":28412},{"type":254,"tag":622,"props":31009,"children":31010},{"style":629},[31011],{"type":259,"value":652},{"type":254,"tag":622,"props":31013,"children":31014},{"style":629},[31015],{"type":259,"value":676},{"type":254,"tag":622,"props":31017,"children":31018},{"class":624,"line":5644},[31019,31023,31027,31031],{"type":254,"tag":622,"props":31020,"children":31021},{"style":768},[31022],{"type":259,"value":28428},{"type":254,"tag":622,"props":31024,"children":31025},{"style":629},[31026],{"type":259,"value":474},{"type":254,"tag":622,"props":31028,"children":31029},{"style":7715},[31030],{"type":259,"value":7718},{"type":254,"tag":622,"props":31032,"children":31033},{"style":629},[31034],{"type":259,"value":676},{"type":254,"tag":622,"props":31036,"children":31037},{"class":624,"line":5652},[31038,31042,31046],{"type":254,"tag":622,"props":31039,"children":31040},{"style":629},[31041],{"type":259,"value":2421},{"type":254,"tag":622,"props":31043,"children":31044},{"style":1202},[31045],{"type":259,"value":2309},{"type":254,"tag":622,"props":31047,"children":31048},{"style":629},[31049],{"type":259,"value":2350},{"type":254,"tag":255,"props":31051,"children":31052},{},[31053,31054,31060],{"type":259,"value":23713},{"type":254,"tag":262,"props":31055,"children":31058},{"href":31056,"rel":31057},"https://github.com/TechWatching/AzureOIDC",[266],[31059],{"type":259,"value":23723},{"type":259,"value":594},{"type":254,"tag":255,"props":31062,"children":31063},{},[31064],{"type":259,"value":31065},"I hope you enjoyed this article. Please feel free to share your thoughts in the comments, ask questions, or make suggestions. Keep learning.",{"type":254,"tag":1260,"props":31067,"children":31068},{},[31069],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":31071},[31072,31078,31079,31088,31089],{"id":23777,"depth":635,"text":23780,"children":31073},[31074,31075,31076,31077],{"id":23802,"depth":679,"text":23805},{"id":23862,"depth":679,"text":23865},{"id":23946,"depth":679,"text":23949},{"id":24047,"depth":679,"text":24050},{"id":24061,"depth":635,"text":24064},{"id":24127,"depth":635,"text":24130,"children":31080},[31081,31082,31083,31085,31086,31087],{"id":24133,"depth":679,"text":24136},{"id":16812,"depth":679,"text":16815},{"id":24760,"depth":679,"text":31084},"Create the identity in Azure Active Directory for the GitHub Actions workflow",{"id":25384,"depth":679,"text":25387},{"id":25997,"depth":679,"text":26000},{"id":26704,"depth":679,"text":26707},{"id":28518,"depth":635,"text":28521},{"id":10572,"depth":635,"text":10575,"children":31090},[31091,31092,31093,31094],{"id":28743,"depth":679,"text":28746},{"id":28759,"depth":679,"text":28762},{"id":28793,"depth":679,"text":28796},{"id":28890,"depth":679,"text":28893},"content:1.posts:53.azure-ready-github-repository.md","1.posts/53.azure-ready-github-repository.md",{"_path":163,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":162,"description":31098,"lead":31099,"date":31100,"badge":31101,"image":31102,"tags":31104,"canonical":31106,"body":31107,"_type":1275,"_id":31311,"_source":231,"_file":31312,"_extension":1278},"You may have come across pnpm through discussions with fellow developers, reading blog posts, watching videos, or attending developer conferences. You have probably heard its praises: it's fast, disk-space efficient, and great for monorepos.","Discussion about pnpm usage and popularity.","2023-07-06T00:00:00.000Z",{"label":243},{"src":31103},"/images/pnpm.png",[24281,31105,245],"package manager","https://bordeauxcoders.com/who-is-using-pnpm",{"type":251,"children":31108,"toc":31305},[31109,31113,31118,31124,31129,31141,31146,31155,31160,31166,31171,31180,31185,31194,31200,31205,31210,31218,31223,31261,31266,31274,31280,31285,31290,31295,31300],{"type":254,"tag":255,"props":31110,"children":31111},{},[31112],{"type":259,"value":31098},{"type":254,"tag":255,"props":31114,"children":31115},{},[31116],{"type":259,"value":31117},"However, you might wonder: who is actually using pnpm?",{"type":254,"tag":282,"props":31119,"children":31121},{"id":31120},"a-growing-popularity",[31122],{"type":259,"value":31123},"A growing popularity",{"type":254,"tag":255,"props":31125,"children":31126},{},[31127],{"type":259,"value":31128},"At the time of writing, pnpm has over 24k stars on GitHub, and this number is rapidly increasing. The pnpm Twitter account maintains a thread that tracks the number of stars. Each time the GitHub repository gains 1k stars, a new tweet is posted. For quite some time now, it has been growing by 1K every two months.",{"type":254,"tag":31130,"props":31131,"children":31135},"div",{"className":31132},[31133,31134],"flex","justify-center",[31136],{"type":254,"tag":31137,"props":31138,"children":31140},"tweet",{"id":31139},"1666004997840986116",[],{"type":254,"tag":255,"props":31142,"children":31143},{},[31144],{"type":259,"value":31145},"Another indicator of its growing popularity is its number of downloads. If you go to npm stats you can see how this number evolved compared to npm and yarn.",{"type":254,"tag":255,"props":31147,"children":31148},{},[31149],{"type":254,"tag":1161,"props":31150,"children":31154},{"alt":31151,"className":31152,"src":31153},"npm vs yarn vs pnpm downloads per day",[1165,1166],"/posts/images/pnpm101_whouses_stats.webp",[],{"type":254,"tag":255,"props":31156,"children":31157},{},[31158],{"type":259,"value":31159},"I believe this diagram speaks for itself 🚀.",{"type":254,"tag":282,"props":31161,"children":31163},{"id":31162},"which-companies-are-using-pnpm",[31164],{"type":259,"value":31165},"Which companies are using pnpm?",{"type":254,"tag":255,"props":31167,"children":31168},{},[31169],{"type":259,"value":31170},"There is a page on pnpm's documentation about well-known companies using pnpm.",{"type":254,"tag":255,"props":31172,"children":31173},{},[31174],{"type":254,"tag":1161,"props":31175,"children":31179},{"alt":31176,"className":31177,"src":31178},"Screeshot of the documentation showing companies using pnpm",[1165,1166],"/posts/images/pnpm101_whouses_companies.webp",[],{"type":254,"tag":255,"props":31181,"children":31182},{},[31183],{"type":259,"value":31184},"You can also see some other companies on the StackShare website (but it seems not many companies took the time to fill in the fact that they were using pnpm in their stack).",{"type":254,"tag":255,"props":31186,"children":31187},{},[31188],{"type":254,"tag":1161,"props":31189,"children":31193},{"alt":31190,"className":31191,"src":31192},"Screeshot of the StackShare page showing companies using pnpm",[1165,1166],"/posts/images/pnpm101_whouses_companies_2.webp",[],{"type":254,"tag":282,"props":31195,"children":31197},{"id":31196},"which-popular-open-source-projects-are-using-pnpm",[31198],{"type":259,"value":31199},"Which popular open-source projects are using pnpm?",{"type":254,"tag":255,"props":31201,"children":31202},{},[31203],{"type":259,"value":31204},"If you see a pnpm-lock.yaml or a pnpm-workspace.yaml file in a GitHub repository, then that project is definitively using pnpm to manage its dependencies. You can use this technique to find GitHub projects using pnpm by querying them with GitHub code search.",{"type":254,"tag":255,"props":31206,"children":31207},{},[31208],{"type":259,"value":31209},"I thought it would be interesting to explore which package managers are utilized in the development of popular JavaScript framework projects. And guess what? Many JavaScript frameworks are developed using pnpm 💖.",{"type":254,"tag":427,"props":31211,"children":31212},{"icon":567},[31213],{"type":254,"tag":255,"props":31214,"children":31215},{},[31216],{"type":259,"value":31217},"To check that these projects were using pnpm, I not only verify the presence of pnpm specific files but also checked their continuous integration pipelines (contained in the .github folder) to see what they were using to manage their dependencies.",{"type":254,"tag":255,"props":31219,"children":31220},{},[31221],{"type":259,"value":31222},"Here is a non-exhaustive list of popular JavaScript web frameworks that use pnpm as their package manager:",{"type":254,"tag":332,"props":31224,"children":31225},{},[31226,31231,31236,31241,31246,31251,31256],{"type":254,"tag":336,"props":31227,"children":31228},{},[31229],{"type":259,"value":31230},"Vue",{"type":254,"tag":336,"props":31232,"children":31233},{},[31234],{"type":259,"value":31235},"Nuxt",{"type":254,"tag":336,"props":31237,"children":31238},{},[31239],{"type":259,"value":31240},"Next.js",{"type":254,"tag":336,"props":31242,"children":31243},{},[31244],{"type":259,"value":31245},"SvelteKit",{"type":254,"tag":336,"props":31247,"children":31248},{},[31249],{"type":259,"value":31250},"SolidStart",{"type":254,"tag":336,"props":31252,"children":31253},{},[31254],{"type":259,"value":31255},"Astro",{"type":254,"tag":336,"props":31257,"children":31258},{},[31259],{"type":259,"value":31260},"Qwik",{"type":254,"tag":255,"props":31262,"children":31263},{},[31264],{"type":259,"value":31265},"That's quite an impressive list: most modern JavaScript web frameworks seem to have chosen pnpm. That's also the case for popular frontend tooling projects like Vite or Turbo.",{"type":254,"tag":427,"props":31267,"children":31268},{"icon":567},[31269],{"type":254,"tag":255,"props":31270,"children":31271},{},[31272],{"type":259,"value":31273},"The fact that pnpm is utilized by maintainers for internal development of these frameworks does not imply that these frameworks can only be used with pnpm. Typically, JavaScript frameworks are \"package manager\" agnostic, allowing you to use your preferred package manager when developing a project with one of these frameworks.",{"type":254,"tag":282,"props":31275,"children":31277},{"id":31276},"should-you-use-pnpm-because-others-do",[31278],{"type":259,"value":31279},"Should you use pnpm because others do?",{"type":254,"tag":255,"props":31281,"children":31282},{},[31283],{"type":259,"value":31284},"Short answer: no.",{"type":254,"tag":255,"props":31286,"children":31287},{},[31288],{"type":259,"value":31289},"Choosing a technology solely based on its popularity is not advisable. While popularity is a factor to consider, it should not be the only determining aspect. Thus, you should not use pnpm because well-known companies or popular open-source projects use it.",{"type":254,"tag":255,"props":31291,"children":31292},{},[31293],{"type":259,"value":31294},"However (and here's the long answer 😉), you should consider exploring pnpm, as there must be a reason why all these intelligent individuals have chosen it over npm or yarn. Investigate the issues pnpm resolves for them; perhaps you face similar challenges in your projects. See what problems pnpm solves for them, maybe you have the same problems in your projects. You might not even be aware of certain problems (such as lengthy CI due to time-consuming package installations, excessive space occupied by node modules, or issues with hoisted node modules), but pnpm could potentially make some things easier. Nevertheless, if you are satisfied with your current package manager, there is no need to switch just to imitate the popular frameworks projects.",{"type":254,"tag":255,"props":31296,"children":31297},{},[31298],{"type":259,"value":31299},"I believe people are familiar with npm since it is the default package manager for Node.js projects. They might also know about yarn because it was initially developed by Facebook (who created React) and addressed some issues with npm. However, people recognize and utilize pnpm due to its performance and ability to resolve the problems they might encounter with npm package management. That's also why I use pnpm; it does the job, and it does it quickly.",{"type":254,"tag":255,"props":31301,"children":31302},{},[31303],{"type":259,"value":31304},"Now you know that you're not alone in using pnpm; from renowned companies to popular open-source projects, many people are utilizing it.",{"title":205,"searchDepth":635,"depth":635,"links":31306},[31307,31308,31309,31310],{"id":31120,"depth":635,"text":31123},{"id":31162,"depth":635,"text":31165},{"id":31196,"depth":635,"text":31199},{"id":31276,"depth":635,"text":31279},"content:1.posts:52.pnpm-who-is-using.md","1.posts/52.pnpm-who-is-using.md",{"_path":160,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":159,"description":31314,"date":31315,"image":31316,"badge":31318,"tags":31319,"canonical":31322,"body":31323,"_type":1275,"_id":31642,"_source":231,"_file":31643,"_extension":1278},"Post about continuous integration with Vue.js.","2023-06-22T00:00:00.000Z",{"src":31317},"/images/vue-cicd.webp",{"label":16349},[31320,31321,16349],"Vue.js","CI/CD","https://bordeauxcoders.com/vuejs-cicd-continuous-integration",{"type":251,"children":31324,"toc":31636},[31325,31331,31336,31341,31346,31351,31368,31374,31379,31384,31412,31420,31429,31434,31439,31445,31465,31474,31486,31520,31529,31570,31593,31598,31604,31616],{"type":254,"tag":282,"props":31326,"children":31328},{"id":31327},"why-are-we-talking-about-ci-in-the-first-place",[31329],{"type":259,"value":31330},"Why are we talking about CI in the first place?",{"type":254,"tag":255,"props":31332,"children":31333},{},[31334],{"type":259,"value":31335},"When working on a project, you typically focus on a specific feature at a time, making changes on a dedicated branch for that feature. When it's time for you to integrate these modifications into the project's code base, the code base has likely evolved since you began working on your feature, as other team members have also pushed their work. That's why your code changes may introduce errors in the application you are developing.",{"type":254,"tag":255,"props":31337,"children":31338},{},[31339],{"type":259,"value":31340},"Regardless of that, while implementing your feature you might have broken some tests, added a security vulnerability, reduced code quality, or simply not adhered to all code conventions used by your team. Even if your colleagues review the code of your Pull Request, they can miss some of these issues. Nonetheless, it would be more efficient for such errors to be detected automatically, enabling people to concentrate their feedback on other aspects.",{"type":254,"tag":255,"props":31342,"children":31343},{},[31344],{"type":259,"value":31345},"Continuous Integration enables us to do precisely that: automatically identify potential issues and make the integration of new changes in a project's code base less error-prone.",{"type":254,"tag":255,"props":31347,"children":31348},{},[31349],{"type":259,"value":31350},"According to Microsoft:",{"type":254,"tag":31352,"props":31353,"children":31354},"blockquote",{},[31355],{"type":254,"tag":255,"props":31356,"children":31357},{},[31358,31360,31367],{"type":259,"value":31359},"Continuous integration (CI) is the process of automatically building and testing code every time a team member commits code changes to ",{"type":254,"tag":262,"props":31361,"children":31364},{"href":31362,"rel":31363},"https://learn.microsoft.com/en-us/devops/develop/git/what-is-version-control",[266],[31365],{"type":259,"value":31366},"version control",{"type":259,"value":594},{"type":254,"tag":282,"props":31369,"children":31371},{"id":31370},"what-are-the-steps-involved-in-a-ci-pipeline",[31372],{"type":259,"value":31373},"What are the steps involved in a CI pipeline?",{"type":254,"tag":255,"props":31375,"children":31376},{},[31377],{"type":259,"value":31378},"We often hear discussions about the \"Build pipeline\" and the \"Release pipeline\" as if building the application was the only task performed in a continuous integration pipeline. However, this is far from the truth; the \"Build\" is an important step, but not the only one.",{"type":254,"tag":255,"props":31380,"children":31381},{},[31382],{"type":259,"value":31383},"Up until now, we have talked a lot about continuous integration for projects in general but nothing specific for Vue.js. Why is that? Because the steps for continuous integration of a Vue.js project are the same as for any other project:",{"type":254,"tag":332,"props":31385,"children":31386},{},[31387,31392,31397,31402,31407],{"type":254,"tag":336,"props":31388,"children":31389},{},[31390],{"type":259,"value":31391},"Install dependencies",{"type":254,"tag":336,"props":31393,"children":31394},{},[31395],{"type":259,"value":31396},"Build the application",{"type":254,"tag":336,"props":31398,"children":31399},{},[31400],{"type":259,"value":31401},"Perform code quality static analysis",{"type":254,"tag":336,"props":31403,"children":31404},{},[31405],{"type":259,"value":31406},"Perform security analysis",{"type":254,"tag":336,"props":31408,"children":31409},{},[31410],{"type":259,"value":31411},"Run tests",{"type":254,"tag":427,"props":31413,"children":31414},{"icon":429},[31415],{"type":254,"tag":255,"props":31416,"children":31417},{},[31418],{"type":259,"value":31419},"It's good to know that, as part of the build step, an executable artifact is often generated, and then used (in the same pipeline or a CD pipeline) to deploy the application in an environment.",{"type":254,"tag":255,"props":31421,"children":31422},{},[31423],{"type":254,"tag":1161,"props":31424,"children":31428},{"alt":31425,"className":31426,"src":31427},"Diagram of a CI/CD pipeline.",[1165,1166],"/posts/images/vuecicd_ci_pipeline.png",[],{"type":254,"tag":255,"props":31430,"children":31431},{},[31432],{"type":259,"value":31433},"Depending on your project, preferences, and available services, your continuous integration process may vary, but it should include these steps, regardless of the tools you use within them.",{"type":254,"tag":255,"props":31435,"children":31436},{},[31437],{"type":259,"value":31438},"There might be additional steps in your Continuous Integration pipeline, but the ones mentioned are the primary ones. Moreover, security is not an optional step; it should be an integral part of your continuous integration.",{"type":254,"tag":282,"props":31440,"children":31442},{"id":31441},"leveraging-packagejson-for-your-ci-setup",[31443],{"type":259,"value":31444},"Leveraging package.json for your CI setup",{"type":254,"tag":255,"props":31446,"children":31447},{},[31448,31450,31456,31458,31463],{"type":259,"value":31449},"When you create a Vue.js project using ",{"type":254,"tag":389,"props":31451,"children":31453},{"className":31452},[],[31454],{"type":259,"value":31455},"create-vue",{"type":259,"value":31457},", the generated ",{"type":254,"tag":389,"props":31459,"children":31461},{"className":31460},[],[31462],{"type":259,"value":24338},{"type":259,"value":31464}," file will contain several npm scripts:",{"type":254,"tag":255,"props":31466,"children":31467},{},[31468],{"type":254,"tag":1161,"props":31469,"children":31473},{"alt":31470,"className":31471,"src":31472},"Screenshot of the npm scripts section of a package.json file.",[1165,1166],"/posts/images/vuecicd_ci_packagejson_0.png",[],{"type":254,"tag":255,"props":31475,"children":31476},{},[31477,31479,31484],{"type":259,"value":31478},"You can observe that some of these scripts precisely correspond to the necessary steps for the continuous integration pipeline, such as build and unit tests. We will discuss each of them in future articles but the npm scripts in the default ",{"type":254,"tag":389,"props":31480,"children":31482},{"className":31481},[],[31483],{"type":259,"value":31455},{"type":259,"value":31485}," template are definitively a good starting point to set up your CI.",{"type":254,"tag":255,"props":31487,"children":31488},{},[31489,31491,31497,31499,31504,31506,31511,31513,31518],{"type":259,"value":31490},"You can see that packages used in the npm scripts are specified in the ",{"type":254,"tag":389,"props":31492,"children":31494},{"className":31493},[],[31495],{"type":259,"value":31496},"devDependencies",{"type":259,"value":31498}," section of the ",{"type":254,"tag":389,"props":31500,"children":31502},{"className":31501},[],[31503],{"type":259,"value":24338},{"type":259,"value":31505}," file. That means these packages will be available to use locally or in a CI server after executing the ",{"type":254,"tag":389,"props":31507,"children":31509},{"className":31508},[],[31510],{"type":259,"value":28712},{"type":259,"value":31512}," command. As part of the CI, other packages may also be needed, so you should include them in the ",{"type":254,"tag":389,"props":31514,"children":31516},{"className":31515},[],[31517],{"type":259,"value":31496},{"type":259,"value":31519}," section as well.",{"type":254,"tag":255,"props":31521,"children":31522},{},[31523],{"type":254,"tag":1161,"props":31524,"children":31528},{"alt":31525,"className":31526,"src":31527,"width":19735},"Screenshot of the devdependencies section of a package.json file.",[1165,1166],"/posts/images/vuecicd_ci_packagejson_1.png",[],{"type":254,"tag":255,"props":31530,"children":31531},{},[31532,31534,31539,31541,31552,31554,31560,31562,31568],{"type":259,"value":31533},"In your CI pipeline, I think it's a good idea to directly execute the npm scripts of the ",{"type":254,"tag":389,"props":31535,"children":31537},{"className":31536},[],[31538],{"type":259,"value":24338},{"type":259,"value":31540}," file rather than specifying the packages you want to run along with their corresponding flags and parameters. You can accomplish this by using the ",{"type":254,"tag":262,"props":31542,"children":31545},{"href":31543,"rel":31544},"https://pnpm.io/fr/cli/run",[266],[31546],{"type":254,"tag":389,"props":31547,"children":31549},{"className":31548},[],[31550],{"type":259,"value":31551},"pnpm run",{"type":259,"value":31553}," command like so: ",{"type":254,"tag":389,"props":31555,"children":31557},{"className":31556},[],[31558],{"type":259,"value":31559},"pnpm run build",{"type":259,"value":31561}," or ",{"type":254,"tag":389,"props":31563,"children":31565},{"className":31564},[],[31566],{"type":259,"value":31567},"pnpm build",{"type":259,"value":31569}," (all npm scripts are aliased by pnpm by default). Of course, you'll need to add any missing npm scripts required for your CI. There are several benefits to this approach:",{"type":254,"tag":332,"props":31571,"children":31572},{},[31573,31578,31583,31588],{"type":254,"tag":336,"props":31574,"children":31575},{},[31576],{"type":259,"value":31577},"It simplifies the CI pipeline and makes it easier to read",{"type":254,"tag":336,"props":31579,"children":31580},{},[31581],{"type":259,"value":31582},"you won't have to modify your pipeline when you change something in an npm script (whether it's the package you use or just a parameter)",{"type":254,"tag":336,"props":31584,"children":31585},{},[31586],{"type":259,"value":31587},"the steps in your CI pipeline will be more consistent across projects (including both Vue.js and non-Vue.js projects) if you always use the same npm script names",{"type":254,"tag":336,"props":31589,"children":31590},{},[31591],{"type":259,"value":31592},"the same commands will be executed with the same parameters, whether locally or on a CI server",{"type":254,"tag":255,"props":31594,"children":31595},{},[31596],{"type":259,"value":31597},"It's important to note that you should not wait for a CI pipeline execution to detect issues in your code. The sooner you identify and resolve problems, the better. Before pushing your changes, you should run the npm scripts that test your code and perform static analysis on it.",{"type":254,"tag":282,"props":31599,"children":31601},{"id":31600},"wrapping-it-up",[31602],{"type":259,"value":31603},"Wrapping it up",{"type":254,"tag":255,"props":31605,"children":31606},{},[31607,31609,31614],{"type":259,"value":31608},"Setting up a Continuous Integration pipeline for your Vue.js project is essential for preventing issues, maintaining code quality, ensuring security, and streamlining the development process. By leveraging the npm scripts of the ",{"type":254,"tag":389,"props":31610,"children":31612},{"className":31611},[],[31613],{"type":259,"value":24338},{"type":259,"value":31615}," file you can simplify your CI pipeline and ensure consistency both locally and on the CI server, as well as across your projects.",{"type":254,"tag":255,"props":31617,"children":31618},{},[31619,31621,31627,31628,31634],{"type":259,"value":31620},"Future articles in this series will delve into the details of various stages of a continuous integration pipeline (such as using ",{"type":254,"tag":389,"props":31622,"children":31624},{"className":31623},[],[31625],{"type":259,"value":31626},"vue-tsc",{"type":259,"value":31561},{"type":254,"tag":389,"props":31629,"children":31631},{"className":31630},[],[31632],{"type":259,"value":31633},"eslint",{"type":259,"value":31635}," for static analysis) and their implementation in GitLab CI or GitHub Actions pipelines.",{"title":205,"searchDepth":635,"depth":635,"links":31637},[31638,31639,31640,31641],{"id":31327,"depth":635,"text":31330},{"id":31370,"depth":635,"text":31373},{"id":31441,"depth":635,"text":31444},{"id":31600,"depth":635,"text":31603},"content:1.posts:51.vuecicd-ci.md","1.posts/51.vuecicd-ci.md",{"_path":157,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":156,"description":31645,"lead":31646,"date":31647,"image":31648,"badge":31649,"tags":31650,"canonical":31651,"body":31652,"_type":1275,"_id":31818,"_source":231,"_file":31819,"_extension":1278},"You have a dependency in your project and want to execute a command using it? The pnpm exec command can help you with that.","pnpm exec","2023-06-15T00:00:00.000Z",{"src":31103},{"label":243},[24281,31105,245],"https://bordeauxcoders.com/execute-commands-using-your-project-dependencies",{"type":251,"children":31653,"toc":31813},[31654,31667,31673,31713,31725,31731,31756,31762,31767,31797,31809],{"type":254,"tag":255,"props":31655,"children":31656},{},[31657,31659,31665],{"type":259,"value":31658},"You have a dependency in your project and want to execute a command using it? The ",{"type":254,"tag":262,"props":31660,"children":31663},{"href":31661,"rel":31662},"https://pnpm.io/cli/exec",[266],[31664],{"type":259,"value":31646},{"type":259,"value":31666}," command can help you with that.",{"type":254,"tag":282,"props":31668,"children":31670},{"id":31669},"an-example",[31671],{"type":259,"value":31672},"An example",{"type":254,"tag":612,"props":31674,"children":31676},{"className":16836,"code":31675,"language":16838,"meta":205,"style":205}," pnpm exec eslint . --ext .ts\n",[31677],{"type":254,"tag":389,"props":31678,"children":31679},{"__ignoreMap":205},[31680],{"type":254,"tag":622,"props":31681,"children":31682},{"class":624,"line":625},[31683,31688,31693,31698,31703,31708],{"type":254,"tag":622,"props":31684,"children":31685},{"style":714},[31686],{"type":259,"value":31687}," pnpm",{"type":254,"tag":622,"props":31689,"children":31690},{"style":664},[31691],{"type":259,"value":31692}," exec",{"type":254,"tag":622,"props":31694,"children":31695},{"style":664},[31696],{"type":259,"value":31697}," eslint",{"type":254,"tag":622,"props":31699,"children":31700},{"style":664},[31701],{"type":259,"value":31702}," .",{"type":254,"tag":622,"props":31704,"children":31705},{"style":664},[31706],{"type":259,"value":31707}," --ext",{"type":254,"tag":622,"props":31709,"children":31710},{"style":664},[31711],{"type":259,"value":31712}," .ts\n",{"type":254,"tag":255,"props":31714,"children":31715},{},[31716,31718,31723],{"type":259,"value":31717},"Given that ESLint is a project dependency, this example shows how to use the ",{"type":254,"tag":389,"props":31719,"children":31721},{"className":31720},[],[31722],{"type":259,"value":31646},{"type":259,"value":31724}," command to run the ESLint tool on all TypeScript files within the project.",{"type":254,"tag":282,"props":31726,"children":31728},{"id":31727},"some-use-cases",[31729],{"type":259,"value":31730},"Some use cases",{"type":254,"tag":332,"props":31732,"children":31733},{},[31734,31739,31744],{"type":254,"tag":336,"props":31735,"children":31736},{},[31737],{"type":259,"value":31738},"You need to do a specific command that is not part of your npm scripts",{"type":254,"tag":336,"props":31740,"children":31741},{},[31742],{"type":259,"value":31743},"You want to execute a tool that is a dependency of your project without having to install it globally",{"type":254,"tag":336,"props":31745,"children":31746},{},[31747,31749,31754],{"type":259,"value":31748},"You need to execute a CLI package command in a CI pipeline, and this package is already included in the ",{"type":254,"tag":389,"props":31750,"children":31752},{"className":31751},[],[31753],{"type":259,"value":31496},{"type":259,"value":31755}," of your project.",{"type":254,"tag":282,"props":31757,"children":31759},{"id":31758},"good-to-know",[31760],{"type":259,"value":31761},"Good to know",{"type":254,"tag":255,"props":31763,"children":31764},{},[31765],{"type":259,"value":31766},"If the command you are using does not conflict with a built-in pnpm command, there is no need to specify 'exec'. Referring to the previous example, you can simply run:",{"type":254,"tag":612,"props":31768,"children":31770},{"className":16836,"code":31769,"language":16838,"meta":205,"style":205}," pnpm eslint . --ext .ts\n",[31771],{"type":254,"tag":389,"props":31772,"children":31773},{"__ignoreMap":205},[31774],{"type":254,"tag":622,"props":31775,"children":31776},{"class":624,"line":625},[31777,31781,31785,31789,31793],{"type":254,"tag":622,"props":31778,"children":31779},{"style":714},[31780],{"type":259,"value":31687},{"type":254,"tag":622,"props":31782,"children":31783},{"style":664},[31784],{"type":259,"value":31697},{"type":254,"tag":622,"props":31786,"children":31787},{"style":664},[31788],{"type":259,"value":31702},{"type":254,"tag":622,"props":31790,"children":31791},{"style":664},[31792],{"type":259,"value":31707},{"type":254,"tag":622,"props":31794,"children":31795},{"style":664},[31796],{"type":259,"value":31712},{"type":254,"tag":255,"props":31798,"children":31799},{},[31800,31802,31807],{"type":259,"value":31801},"It's one of the small details that make using ",{"type":254,"tag":389,"props":31803,"children":31805},{"className":31804},[],[31806],{"type":259,"value":24281},{"type":259,"value":31808}," so pleasant.",{"type":254,"tag":1260,"props":31810,"children":31811},{},[31812],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":31814},[31815,31816,31817],{"id":31669,"depth":635,"text":31672},{"id":31727,"depth":635,"text":31730},{"id":31758,"depth":635,"text":31761},"content:1.posts:50.pnpm-exec.md","1.posts/50.pnpm-exec.md",{"_path":154,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":153,"description":31821,"date":31822,"image":31823,"badge":31824,"tags":31825,"canonical":31826,"body":31827,"_type":1275,"_id":32061,"_source":231,"_file":32062,"_extension":1278},"This is the first article of the Vue.js CI/CD series. It will be the opportunity to explain the purpose of the series and the topics we plan to cover.","2023-06-01T00:00:00.000Z",{"src":31317},{"label":16349},[31320,31321,16349],"https://bordeauxcoders.com/introducing-the-vuejs-cicd-series",{"type":251,"children":31828,"toc":32053},[31829,31833,31839,31844,31849,31855,31860,31892,31897,31903,31908,31913,31919,31924,31942,31948,31953,31970,31978,31991,32009,32026,32048],{"type":254,"tag":255,"props":31830,"children":31831},{},[31832],{"type":259,"value":31821},{"type":254,"tag":282,"props":31834,"children":31836},{"id":31835},"why-this-series",[31837],{"type":259,"value":31838},"Why this series?",{"type":254,"tag":255,"props":31840,"children":31841},{},[31842],{"type":259,"value":31843},"We delved deeply into CI/CD for Vue.js when preparing a DevOps practices course for students in engineering school. The course wasn't directly related to Vue.js; however, we chose to use a Vue.js application for hands-on exercises focused on implementing CI/CD pipelines. Through this process, we gained valuable insights that we now wish to share.",{"type":254,"tag":255,"props":31845,"children":31846},{},[31847],{"type":259,"value":31848},"While there are numerous blog posts on Vue.js, not many articles specifically address setting up CI/CD pipelines for Vue.js projects. Yet, having proper continuous integration and automating deployments are two aspects that should not be neglected in a project. That's the main reason why we decided to write this Vue.js CI/CD series.",{"type":254,"tag":282,"props":31850,"children":31852},{"id":31851},"what-are-we-going-to-talk-about",[31853],{"type":259,"value":31854},"What are we going to talk about?",{"type":254,"tag":255,"props":31856,"children":31857},{},[31858],{"type":259,"value":31859},"As you can expect, we will cover the usual topics:",{"type":254,"tag":332,"props":31861,"children":31862},{},[31863,31868,31873,31878,31883,31887],{"type":254,"tag":336,"props":31864,"children":31865},{},[31866],{"type":259,"value":31867},"package management",{"type":254,"tag":336,"props":31869,"children":31870},{},[31871],{"type":259,"value":31872},"build & artifacts",{"type":254,"tag":336,"props":31874,"children":31875},{},[31876],{"type":259,"value":31877},"static analysis",{"type":254,"tag":336,"props":31879,"children":31880},{},[31881],{"type":259,"value":31882},"testing",{"type":254,"tag":336,"props":31884,"children":31885},{},[31886],{"type":259,"value":19570},{"type":254,"tag":336,"props":31888,"children":31889},{},[31890],{"type":259,"value":31891},"deployment",{"type":254,"tag":255,"props":31893,"children":31894},{},[31895],{"type":259,"value":31896},"Examples will be shown using different CI/CD platforms and cloud services.",{"type":254,"tag":455,"props":31898,"children":31900},{"id":31899},"cicd-platforms",[31901],{"type":259,"value":31902},"CI/CD platforms",{"type":254,"tag":255,"props":31904,"children":31905},{},[31906],{"type":259,"value":31907},"We can't cover all the CI/CD platforms so we will focus on GitHub Actions and GitLab CI.",{"type":254,"tag":255,"props":31909,"children":31910},{},[31911],{"type":259,"value":31912},"Even though each platform has its unique features, the majority of the concepts we will discuss can be applied to other platforms as well. So, don't stop reading the series just because you are using a different platform 😉.",{"type":254,"tag":455,"props":31914,"children":31916},{"id":31915},"cloud-services",[31917],{"type":259,"value":31918},"Cloud services",{"type":254,"tag":255,"props":31920,"children":31921},{},[31922],{"type":259,"value":31923},"There are numerous hosting options for a Vue.js application, and we will demonstrate how to deploy an application on at least the following platforms:",{"type":254,"tag":332,"props":31925,"children":31926},{},[31927,31932,31937],{"type":254,"tag":336,"props":31928,"children":31929},{},[31930],{"type":259,"value":31931},"Azure Static Web App",{"type":254,"tag":336,"props":31933,"children":31934},{},[31935],{"type":259,"value":31936},"Vercel",{"type":254,"tag":336,"props":31938,"children":31939},{},[31940],{"type":259,"value":31941},"Netlify",{"type":254,"tag":282,"props":31943,"children":31945},{"id":31944},"which-sample-application-will-we-be-using",[31946],{"type":259,"value":31947},"Which sample application will we be using?",{"type":254,"tag":255,"props":31949,"children":31950},{},[31951],{"type":259,"value":31952},"This series aims to discuss CI/CD for Vue.js applications so that anyone can learn how to set up a CI/CD pipeline for their Vue.js project. That's why we will use the sample code from the basic application generated when creating a new Vue.js project.",{"type":254,"tag":255,"props":31954,"children":31955},{},[31956,31958,31968],{"type":259,"value":31957},"And to be clear, when you start a new Vue.js project you don't want to use the Vue CLI because it is in maintenance mode. Instead, you should use ",{"type":254,"tag":262,"props":31959,"children":31962},{"href":31960,"rel":31961},"https://github.com/vuejs/create-vue",[266],[31963],{"type":254,"tag":389,"props":31964,"children":31966},{"className":31965},[],[31967],{"type":259,"value":31455},{"type":259,"value":31969}," which is based on Vite and is the recommended way of scaffolding a Vue.js project.",{"type":254,"tag":427,"props":31971,"children":31972},{"icon":429},[31973],{"type":254,"tag":255,"props":31974,"children":31975},{},[31976],{"type":259,"value":31977},"I think it's important to mention it because I still see new blog posts talking about creating new projects using Vue CLI.",{"type":254,"tag":255,"props":31979,"children":31980},{},[31981,31983,31989],{"type":259,"value":31982},"So nothing specific in the code of the application we will build and deploy, just the basic things you get when you run the ",{"type":254,"tag":389,"props":31984,"children":31986},{"className":31985},[],[31987],{"type":259,"value":31988},"pnpm create vue@latest",{"type":259,"value":31990}," command with:",{"type":254,"tag":332,"props":31992,"children":31993},{},[31994,31999,32004],{"type":254,"tag":336,"props":31995,"children":31996},{},[31997],{"type":259,"value":31998},"TypeScript enabled ➡️ it's 2023, I don't see any valid reason why to choose Vanilla JS instead of TypeScript so if you are not using TypeScript you probably should",{"type":254,"tag":336,"props":32000,"children":32001},{},[32002],{"type":259,"value":32003},"Vitest enabled ➡️ the vite-native unit test framework you want to use to test your code",{"type":254,"tag":336,"props":32005,"children":32006},{},[32007],{"type":259,"value":32008},"ESLint enabled ➡️ because static analysis should be part of your Continuous Integration pipeline",{"type":254,"tag":255,"props":32010,"children":32011},{},[32012,32014,32024],{"type":259,"value":32013},"The last thing to mention: we will use the latest version of ",{"type":254,"tag":262,"props":32015,"children":32018},{"href":32016,"rel":32017},"https://pnpm.io/",[266],[32019],{"type":254,"tag":389,"props":32020,"children":32022},{"className":32021},[],[32023],{"type":259,"value":24281},{"type":259,"value":32025}," to manage dependencies. Our preferred package manager is pnpm for various reasons, but the primary one is its remarkable speed!",{"type":254,"tag":427,"props":32027,"children":32028},{"icon":429},[32029],{"type":254,"tag":255,"props":32030,"children":32031},{},[32032,32033,32039,32041,32047],{"type":259,"value":1132},{"type":254,"tag":262,"props":32034,"children":32036},{"href":32016,"rel":32035},[266],[32037],{"type":259,"value":32038},"pnpm website",{"type":259,"value":32040}," to read more about pnpm or have a look at our ",{"type":254,"tag":262,"props":32042,"children":32044},{"href":10371,"rel":32043},[266],[32045],{"type":259,"value":32046},"pnpm 101 series",{"type":259,"value":594},{"type":254,"tag":255,"props":32049,"children":32050},{},[32051],{"type":259,"value":32052},"We hope you will have a great time learning about CI/CD for Vue.js application. See you in the next article.",{"title":205,"searchDepth":635,"depth":635,"links":32054},[32055,32056,32060],{"id":31835,"depth":635,"text":31838},{"id":31851,"depth":635,"text":31854,"children":32057},[32058,32059],{"id":31899,"depth":679,"text":31902},{"id":31915,"depth":679,"text":31918},{"id":31944,"depth":635,"text":31947},"content:1.posts:49.vuecicd-introduction.md","1.posts/49.vuecicd-introduction.md",{"_path":151,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":150,"description":32064,"lead":32065,"date":32066,"image":32067,"badge":32068,"tags":32069,"canonical":32070,"body":32071,"_type":1275,"_id":32236,"_source":231,"_file":32237,"_extension":1278},"If you are working on various projects, you have likely encountered situations where you need to have multiple versions of Node.js installed on your computer.","pnpm env","2023-05-25T00:00:00.000Z",{"src":31103},{"label":243},[24281,31105,245],"https://bordeauxcoders.com/manage-multiple-nodejs-versions",{"type":251,"children":32072,"toc":32231},[32073,32077,32094,32098,32132,32137,32142,32148,32153,32167,32171,32204,32227],{"type":254,"tag":255,"props":32074,"children":32075},{},[32076],{"type":259,"value":32064},{"type":254,"tag":255,"props":32078,"children":32079},{},[32080,32082,32092],{"type":259,"value":32081},"You might not know it, but managing multiple Node.js versions is something you can do with pnpm, using the ",{"type":254,"tag":262,"props":32083,"children":32086},{"href":32084,"rel":32085},"https://pnpm.io/fr/cli/env",[266],[32087],{"type":254,"tag":389,"props":32088,"children":32090},{"className":32089},[],[32091],{"type":259,"value":32065},{"type":259,"value":32093}," command.",{"type":254,"tag":282,"props":32095,"children":32096},{"id":31669},[32097],{"type":259,"value":31672},{"type":254,"tag":612,"props":32099,"children":32101},{"className":16836,"code":32100,"language":16838,"meta":205,"style":205},"pnpm env use -g lts \n",[32102],{"type":254,"tag":389,"props":32103,"children":32104},{"__ignoreMap":205},[32105],{"type":254,"tag":622,"props":32106,"children":32107},{"class":624,"line":625},[32108,32112,32117,32122,32127],{"type":254,"tag":622,"props":32109,"children":32110},{"style":714},[32111],{"type":259,"value":24281},{"type":254,"tag":622,"props":32113,"children":32114},{"style":664},[32115],{"type":259,"value":32116}," env",{"type":254,"tag":622,"props":32118,"children":32119},{"style":664},[32120],{"type":259,"value":32121}," use",{"type":254,"tag":622,"props":32123,"children":32124},{"style":664},[32125],{"type":259,"value":32126}," -g",{"type":254,"tag":622,"props":32128,"children":32129},{"style":664},[32130],{"type":259,"value":32131}," lts\n",{"type":254,"tag":255,"props":32133,"children":32134},{},[32135],{"type":259,"value":32136},"This example demonstrates how to install the LTS version of Node.js.",{"type":254,"tag":255,"props":32138,"children":32139},{},[32140],{"type":259,"value":32141},"Additionally, you can install specific versions of Node.js, view the versions already present on your computer, or remove one.",{"type":254,"tag":282,"props":32143,"children":32145},{"id":32144},"why-use-pnpm-as-your-node-version-manager",[32146],{"type":259,"value":32147},"Why use pnpm as your Node version manager?",{"type":254,"tag":255,"props":32149,"children":32150},{},[32151],{"type":259,"value":32152},"Because managing your Node.js version is built into pnpm: if you already use pnpm as your npm package manager, you don't need to install another tool.",{"type":254,"tag":255,"props":32154,"children":32155},{},[32156,32158,32165],{"type":259,"value":32157},"But there is absolutely nothing wrong with using another Node version manager if you prefer. I was using ",{"type":254,"tag":262,"props":32159,"children":32162},{"href":32160,"rel":32161},"https://github.com/coreybutler/nvm-windows",[266],[32163],{"type":259,"value":32164},"nvm-windows",{"type":259,"value":32166}," before and I was happy with it. I just don't see the point of installing it anymore as similar functionality is already available in pnpm.",{"type":254,"tag":282,"props":32168,"children":32169},{"id":31758},[32170],{"type":259,"value":31761},{"type":254,"tag":255,"props":32172,"children":32173},{},[32174,32176,32187,32189,32195,32202],{"type":259,"value":32175},"To specify a Node.js version to use in a project/folder, you can add an ",{"type":254,"tag":262,"props":32177,"children":32180},{"href":32178,"rel":32179},"https://pnpm.io/fr/npmrc",[266],[32181],{"type":254,"tag":389,"props":32182,"children":32184},{"className":32183},[],[32185],{"type":259,"value":32186},".npmrc",{"type":259,"value":32188}," file ",{"type":254,"tag":389,"props":32190,"children":32192},{"className":32191},[],[32193],{"type":259,"value":32194},"use-node-version",{"type":254,"tag":262,"props":32196,"children":32199},{"href":32197,"rel":32198},"https://pnpm.io/fr/npmrc#use-node-version",[266],[32200],{"type":259,"value":32201},"​",{"type":259,"value":32203}," setting, like that:",{"type":254,"tag":612,"props":32205,"children":32207},{"className":16836,"code":32206,"language":16838,"meta":205,"style":205},"use-node-version=16.16.0\n",[32208],{"type":254,"tag":389,"props":32209,"children":32210},{"__ignoreMap":205},[32211],{"type":254,"tag":622,"props":32212,"children":32213},{"class":624,"line":625},[32214,32218,32222],{"type":254,"tag":622,"props":32215,"children":32216},{"style":1202},[32217],{"type":259,"value":32194},{"type":254,"tag":622,"props":32219,"children":32220},{"style":629},[32221],{"type":259,"value":1654},{"type":254,"tag":622,"props":32223,"children":32224},{"style":664},[32225],{"type":259,"value":32226},"16.16.0\n",{"type":254,"tag":1260,"props":32228,"children":32229},{},[32230],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":32232},[32233,32234,32235],{"id":31669,"depth":635,"text":31672},{"id":32144,"depth":635,"text":32147},{"id":31758,"depth":635,"text":31761},"content:1.posts:48.pnpm-env.md","1.posts/48.pnpm-env.md",{"_path":148,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":147,"description":32239,"lead":32240,"date":32241,"image":32242,"badge":32243,"tags":32244,"canonical":32245,"body":32246,"_type":1275,"_id":32426,"_source":231,"_file":32427,"_extension":1278},"Sometimes, all you want to do is grab an npm package and execute a command with it, without having to install it (whether globally or as a dependency).","pnpm dlx","2023-05-18T00:00:00.000Z",{"src":31103},{"label":243},[24281,31105,245],"https://bordeauxcoders.com/perform-dynamic-execution-of-an-npm-package",{"type":251,"children":32247,"toc":32421},[32248,32252,32268,32272,32301,32321,32334,32338,32374,32378,32417],{"type":254,"tag":255,"props":32249,"children":32250},{},[32251],{"type":259,"value":32239},{"type":254,"tag":255,"props":32253,"children":32254},{},[32255,32257,32267],{"type":259,"value":32256},"That's what you can do with ",{"type":254,"tag":262,"props":32258,"children":32261},{"href":32259,"rel":32260},"https://pnpm.io/cli/dlx",[266],[32262],{"type":254,"tag":389,"props":32263,"children":32265},{"className":32264},[],[32266],{"type":259,"value":32240},{"type":259,"value":594},{"type":254,"tag":282,"props":32269,"children":32270},{"id":31669},[32271],{"type":259,"value":31672},{"type":254,"tag":612,"props":32273,"children":32275},{"className":16836,"code":32274,"language":16838,"meta":205,"style":205},"pnpm dlx vercel deploy\n",[32276],{"type":254,"tag":389,"props":32277,"children":32278},{"__ignoreMap":205},[32279],{"type":254,"tag":622,"props":32280,"children":32281},{"class":624,"line":625},[32282,32286,32291,32296],{"type":254,"tag":622,"props":32283,"children":32284},{"style":714},[32285],{"type":259,"value":24281},{"type":254,"tag":622,"props":32287,"children":32288},{"style":664},[32289],{"type":259,"value":32290}," dlx",{"type":254,"tag":622,"props":32292,"children":32293},{"style":664},[32294],{"type":259,"value":32295}," vercel",{"type":254,"tag":622,"props":32297,"children":32298},{"style":664},[32299],{"type":259,"value":32300}," deploy\n",{"type":254,"tag":255,"props":32302,"children":32303},{},[32304,32306,32313,32315,32320],{"type":259,"value":32305},"This example shows how to use the ",{"type":254,"tag":262,"props":32307,"children":32310},{"href":32308,"rel":32309},"https://vercel.com/docs/cli",[266],[32311],{"type":259,"value":32312},"vercel CLI package",{"type":259,"value":32314}," without having to install it thanks to ",{"type":254,"tag":389,"props":32316,"children":32318},{"className":32317},[],[32319],{"type":259,"value":32240},{"type":259,"value":594},{"type":254,"tag":255,"props":32322,"children":32323},{},[32324,32326,32332],{"type":259,"value":32325},"In this example, pnpm downloads the vercel package, and executes it with the command ",{"type":254,"tag":389,"props":32327,"children":32329},{"className":32328},[],[32330],{"type":259,"value":32331},"deploy",{"type":259,"value":32333}," (that deploys a project to the Vercel platform).",{"type":254,"tag":282,"props":32335,"children":32336},{"id":31727},[32337],{"type":259,"value":31730},{"type":254,"tag":332,"props":32339,"children":32340},{},[32341,32346,32351,32356],{"type":254,"tag":336,"props":32342,"children":32343},{},[32344],{"type":259,"value":32345},"You don't want to install globally a package because you only need to execute its binary script once",{"type":254,"tag":336,"props":32347,"children":32348},{},[32349],{"type":259,"value":32350},"You don't want a package to be a dev dependency of your project, or you are not using it in the context of a Node project",{"type":254,"tag":336,"props":32352,"children":32353},{},[32354],{"type":259,"value":32355},"You need to execute a CLI package command from a CI pipeline",{"type":254,"tag":336,"props":32357,"children":32358},{},[32359,32361,32367,32368,32373],{"type":259,"value":32360},"You want to ensure you use the latest version of a package (useful for starter kits like ",{"type":254,"tag":389,"props":32362,"children":32364},{"className":32363},[],[32365],{"type":259,"value":32366},"create-vite",{"type":259,"value":16287},{"type":254,"tag":389,"props":32369,"children":32371},{"className":32370},[],[32372],{"type":259,"value":31455},{"type":259,"value":2309},{"type":254,"tag":282,"props":32375,"children":32376},{"id":31758},[32377],{"type":259,"value":31761},{"type":254,"tag":255,"props":32379,"children":32380},{},[32381,32383,32394,32395,32400,32402,32408,32410,32416],{"type":259,"value":32382},"For starter kits, you can use ",{"type":254,"tag":262,"props":32384,"children":32387},{"href":32385,"rel":32386},"https://pnpm.io/cli/create",[266],[32388],{"type":254,"tag":389,"props":32389,"children":32391},{"className":32390},[],[32392],{"type":259,"value":32393},"pnpm create",{"type":259,"value":24283},{"type":254,"tag":389,"props":32396,"children":32398},{"className":32397},[],[32399],{"type":259,"value":32240},{"type":259,"value":32401},". For instance, executing ",{"type":254,"tag":389,"props":32403,"children":32405},{"className":32404},[],[32406],{"type":259,"value":32407},"pnpm create vue",{"type":259,"value":32409}," is equivalent to executing ",{"type":254,"tag":389,"props":32411,"children":32413},{"className":32412},[],[32414],{"type":259,"value":32415},"pnpm dlx create-vue",{"type":259,"value":594},{"type":254,"tag":1260,"props":32418,"children":32419},{},[32420],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":32422},[32423,32424,32425],{"id":31669,"depth":635,"text":31672},{"id":31727,"depth":635,"text":31730},{"id":31758,"depth":635,"text":31761},"content:1.posts:47.pnpm-dlx.md","1.posts/47.pnpm-dlx.md",{"_path":145,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":144,"description":32429,"lead":32430,"date":32431,"image":32432,"badge":32433,"tags":32434,"ImageAttribution":10063,"body":32435,"_type":1275,"_id":32871,"_source":231,"_file":32872,"_extension":1278},"I usually do not take the time to reflect on my developer journey and look back over the past year. Yet, it's a shame because I miss the opportunity to:","Challenges and achievements of 2022","2023-01-03T00:00:00.000Z",{"src":10057},{"label":10059},[10061,10062],{"type":251,"children":32436,"toc":32862},[32437,32441,32464,32469,32475,32480,32485,32490,32495,32500,32505,32511,32516,32522,32543,32548,32557,32562,32576,32581,32586,32591,32604,32609,32633,32658,32667,32672,32677,32686,32700,32728,32742,32746,32751,32756,32770,32779,32784,32789,32811,32815,32820,32848],{"type":254,"tag":255,"props":32438,"children":32439},{},[32440],{"type":259,"value":32429},{"type":254,"tag":332,"props":32442,"children":32443},{},[32444,32449,32454,32459],{"type":254,"tag":336,"props":32445,"children":32446},{},[32447],{"type":259,"value":32448},"remind me of the challenges I faced",{"type":254,"tag":336,"props":32450,"children":32451},{},[32452],{"type":259,"value":32453},"remember what I have learned",{"type":254,"tag":336,"props":32455,"children":32456},{},[32457],{"type":259,"value":32458},"see what I have accomplished",{"type":254,"tag":336,"props":32460,"children":32461},{},[32462],{"type":259,"value":32463},"think about what I need to do next year to keep moving forward.",{"type":254,"tag":255,"props":32465,"children":32466},{},[32467],{"type":259,"value":32468},"So let's start this 2022 retrospective.",{"type":254,"tag":282,"props":32470,"children":32472},{"id":32471},"coming-back-to-front-end-development",[32473],{"type":259,"value":32474},"Coming \"back\" to front-end development",{"type":254,"tag":255,"props":32476,"children":32477},{},[32478],{"type":259,"value":32479},"During my career, I have mostly done back-end development, especially in recent years when I have been working on cloud projects. I was not totally unfamiliar with front-end development, but I preferred back-end development: thinking about cloud-native architectures speaks to me more than trying to center a div (don't be offended, I'm just kidding).",{"type":254,"tag":255,"props":32481,"children":32482},{},[32483],{"type":259,"value":32484},"However, in October 2021 I joined a team working on a Vue.js web application (with an ASP.NET Core back-end) and have been working on it since. So I had to quickly learn Vue.js and upgrade my front-end skills (which were quite rusty) in 2022.",{"type":254,"tag":255,"props":32486,"children":32487},{},[32488],{"type":259,"value":32489},"As someone used to C# language, and .NET libraries/tooling it was quite challenging to re-learn everything on the front-end. Hopefully, whether it's front-end or back-end, it is still a matter of software development therefore there are many similarities and some knowledge I already had. Yet, I learned a lot from my colleagues who were less experienced but had better front-end skills than me. It's a good reminder that wherever you are in your career, you always have things to learn from others, seniors as well as juniors.",{"type":254,"tag":255,"props":32491,"children":32492},{},[32493],{"type":259,"value":32494},"And you know what? I loved learning Vue.js and I am now a big fan of this framework. I was quite amazed to see it's community driven and that despite its huge popularity there is no big company behind it (like Facebook for React or Google for Angular), but many people and companies sponsoring the framework contributors. I love the ecosystem around Vue.js and I am happy that there is such a great community (lots of contributors live in France by the way).",{"type":254,"tag":255,"props":32496,"children":32497},{},[32498],{"type":259,"value":32499},"Even if I am far from being a Vue.js expert, I am proud to have brought some improvement ideas to the project I am working on (migrate to the composition API, migrate to Vite, add useful libraries like VueUse, ...). I am still not a CSS guru (and I guess I will never be) and I still prefer back-end development over front-end development. However, it's great to have a good understanding of both and be able to do both.",{"type":254,"tag":255,"props":32501,"children":32502},{},[32503],{"type":259,"value":32504},"In my personal time, I took a look at the Nuxt framework (which is a meta-framework built on top of Vue.js) and the developer experience seems awesome. It's a framework I want to explore in 2023.",{"type":254,"tag":282,"props":32506,"children":32508},{"id":32507},"a-step-into-the-world-of-content-creation",[32509],{"type":259,"value":32510},"A step into the world of content creation",{"type":254,"tag":255,"props":32512,"children":32513},{},[32514],{"type":259,"value":32515},"As a developer, it's nice to learn new things but it's even better to share them, not only with colleagues but also with other developers that could benefit from them. That is why I wanted to share with a broader audience what I had discovered at work or during my technology watch.",{"type":254,"tag":455,"props":32517,"children":32519},{"id":32518},"social-media",[32520],{"type":259,"value":32521},"Social media",{"type":254,"tag":255,"props":32523,"children":32524},{},[32525,32527,32534,32535,32542],{"type":259,"value":32526},"I already used blog articles to share my learnings but it takes me a lot of time to write an article and it's often on a specific topic. Sometimes I just want to share one little tip I learned or one article I found worth reading. So in 2022, I started sharing links, tips, and other things on social networks. I did that mostly on LinkedIn and Twitter, yet I recently spent some time on other platforms like ",{"type":254,"tag":262,"props":32528,"children":32531},{"href":32529,"rel":32530},"https://mas.to/@techwatching",[266],[32532],{"type":259,"value":32533},"Mastodon",{"type":259,"value":10347},{"type":254,"tag":262,"props":32536,"children":32539},{"href":32537,"rel":32538},"https://www.showwcase.com/techwatching",[266],[32540],{"type":259,"value":32541},"Showwcase",{"type":259,"value":594},{"type":254,"tag":255,"props":32544,"children":32545},{},[32546],{"type":259,"value":32547},"Here are what my posts look like:",{"type":254,"tag":255,"props":32549,"children":32550},{},[32551],{"type":254,"tag":1161,"props":32552,"children":32556},{"alt":32553,"className":32554,"src":32555},"Posts on Twitter about tips.",[1165,1166],"/posts/images/2022_retro_1.png",[],{"type":254,"tag":255,"props":32558,"children":32559},{},[32560],{"type":259,"value":32561},"To be honest it did not bring me a lot of followers on Twitter or LinkedIn. Especially when you see that some people succeed in building in 6 months a bigger audience that I would ever have 😃. Is it bad? Not really, I would be glad if I could have more followers so that it could bring more readers to my blog but it's not very important. However, I made some nice connections and I am happy to share my learning journey with the few people that are interested.",{"type":254,"tag":255,"props":32563,"children":32564},{},[32565,32567,32574],{"type":259,"value":32566},"In addition to that, I learned a lot about content creation (for example how to schedule and publish the same content on multiple social media with tools like ",{"type":254,"tag":262,"props":32568,"children":32571},{"href":32569,"rel":32570},"https://www.feedhive.com/",[266],[32572],{"type":259,"value":32573},"FeedHive",{"type":259,"value":32575},"). Will it be useful in my current role? I don't think so. Will it help me in a future role or job? I don't know, it might be. Anyway, it's always fun to learn new things.",{"type":254,"tag":255,"props":32577,"children":32578},{},[32579],{"type":259,"value":32580},"I should probably tweet/toot/write more frequently in 2023, but it takes time so we will see.",{"type":254,"tag":455,"props":32582,"children":32584},{"id":32583},"blog",[32585],{"type":259,"value":5},{"type":254,"tag":255,"props":32587,"children":32588},{},[32589],{"type":259,"value":32590},"Writing on social media did not prevent me from writing on my blog. I wrote 15 articles in 2022, it's less than in 2021 (19 articles) but it's fine: I am happy with the articles I wrote. I am also happy to see that my blog's traffic grew:",{"type":254,"tag":332,"props":32592,"children":32593},{},[32594,32599],{"type":254,"tag":336,"props":32595,"children":32596},{},[32597],{"type":259,"value":32598},"28K users vs 12K",{"type":254,"tag":336,"props":32600,"children":32601},{},[32602],{"type":259,"value":32603},"37K pages seen vs 18K",{"type":254,"tag":255,"props":32605,"children":32606},{},[32607],{"type":259,"value":32608},"Here are the most read articles on my blog in 2022 (without taking into account other websites on which I cross-post them):",{"type":254,"tag":332,"props":32610,"children":32611},{},[32612,32617,32621,32625,32629],{"type":254,"tag":336,"props":32613,"children":32614},{},[32615],{"type":259,"value":32616},"AzureWebJobsStorage, the secret you don't need in your Function App",{"type":254,"tag":336,"props":32618,"children":32619},{},[32620],{"type":259,"value":66},{"type":254,"tag":336,"props":32622,"children":32623},{},[32624],{"type":259,"value":114},{"type":254,"tag":336,"props":32626,"children":32627},{},[32628],{"type":259,"value":72},{"type":254,"tag":336,"props":32630,"children":32631},{},[32632],{"type":259,"value":21},{"type":254,"tag":255,"props":32634,"children":32635},{},[32636,32638,32643,32645,32650,32651,32657],{"type":259,"value":32637},"In 2022, I started submitting some of my articles on ",{"type":254,"tag":262,"props":32639,"children":32641},{"href":10350,"rel":32640},[266],[32642],{"type":259,"value":10362},{"type":259,"value":32644}," and continued cross-posting all my articles on ",{"type":254,"tag":262,"props":32646,"children":32648},{"href":10341,"rel":32647},[266],[32649],{"type":259,"value":10345},{"type":259,"value":10347},{"type":254,"tag":262,"props":32652,"children":32655},{"href":32653,"rel":32654},"https://dev.to/techwatching",[266],[32656],{"type":259,"value":10354},{"type":259,"value":594},{"type":254,"tag":255,"props":32659,"children":32660},{},[32661],{"type":254,"tag":1161,"props":32662,"children":32666},{"alt":32663,"className":32664,"src":32665,"width":19735},"List of articles published on DZone by Alexandre Nédélec.",[1165,1166],"/posts/images/2022_retro_2.png",[],{"type":254,"tag":255,"props":32668,"children":32669},{},[32670],{"type":259,"value":32671},"Views are great, nevertheless, what pleased me the most was seeing a few nice comments on my articles.  When you know your articles helped someone or taught something to someone, it's the best achievement.",{"type":254,"tag":255,"props":32673,"children":32674},{},[32675],{"type":259,"value":32676},"The topics I talked the most about in my articles this year were .NET, Azure, tooling, and Pulumi. I am a big fan of Pulumi, I found the technology great (even more after having used Terraform) so writing about it was nice. Thanks to these articles I have been invited to join the Puluminaries program (which recognizes members of the Pulumi community). It's amazing to see that blogging can lead to nice opportunities like that.",{"type":254,"tag":255,"props":32678,"children":32679},{},[32680],{"type":254,"tag":1161,"props":32681,"children":32685},{"alt":32682,"className":32683,"src":32684},"Puluminaries page on the Pulumi website.",[1165,1166],"/posts/images/2022_retro_3.png",[],{"type":254,"tag":255,"props":32687,"children":32688},{},[32689,32691,32698],{"type":259,"value":32690},"Blogging can also be rewarding when you participate in writeathons like the ones Hashnode organizes regularly. I am particularly  glad to have participated in the ",{"type":254,"tag":262,"props":32692,"children":32695},{"href":32693,"rel":32694},"https://townhall.hashnode.com/4articles4weeks-writeathon-the-winners",[266],[32696],{"type":259,"value":32697},"\"4 articles 4 weeks\" writeathon",{"type":259,"value":32699}," and to have been part of the 5 top winners. It was the opportunity to:",{"type":254,"tag":332,"props":32701,"children":32702},{},[32703,32708,32713,32718,32723],{"type":254,"tag":336,"props":32704,"children":32705},{},[32706],{"type":259,"value":32707},"write about non-technical topics",{"type":254,"tag":336,"props":32709,"children":32710},{},[32711],{"type":259,"value":32712},"gain new followers",{"type":254,"tag":336,"props":32714,"children":32715},{},[32716],{"type":259,"value":32717},"have a post featured on Hashnode for the 1st time",{"type":254,"tag":336,"props":32719,"children":32720},{},[32721],{"type":259,"value":32722},"challenge me and publish 4 articles in 4 weeks",{"type":254,"tag":336,"props":32724,"children":32725},{},[32726],{"type":259,"value":32727},"discover other blogs",{"type":254,"tag":255,"props":32729,"children":32730},{},[32731,32733,32740],{"type":259,"value":32732},"In 2023, I plan to keep writing articles on my blog. I have a lot of ideas for articles, yet as a full-time developer and a dad of a 2 years old girl, it is often complicated to find time to write them. The problem is that creating an article (even on a topic I know) always involves a bit of search, and some effort to create the code samples. Furthermore, I am slow at writing. I hope to get better and faster at it. I think improving my use of PKM tools like ",{"type":254,"tag":262,"props":32734,"children":32737},{"href":32735,"rel":32736},"https://obsidian.md/",[266],[32738],{"type":259,"value":32739},"Obsidian",{"type":259,"value":32741}," could help me with that.",{"type":254,"tag":455,"props":32743,"children":32744},{"id":10177},[32745],{"type":259,"value":10180},{"type":254,"tag":255,"props":32747,"children":32748},{},[32749],{"type":259,"value":32750},"Another way of sharing what you learn is public speaking.",{"type":254,"tag":255,"props":32752,"children":32753},{},[32754],{"type":259,"value":32755},"In the past, I gave a few internal talks in my company (about C#, CI/CD, and IaC) and even gave a talk with a colleague about Azure IoT at a small conference organized by my company. However, participating in events organized by your company and mostly internal is not the same as speaking at a developer conference or another public event. That's why I am pleased to have succeeded in 2022 to give my first talk at a developer conference.",{"type":254,"tag":255,"props":32757,"children":32758},{},[32759,32761,32768],{"type":259,"value":32760},"I was a speaker at Cloud Ouest, which took place online in June 2022. I did a talk called \"Infrastructure as Code or Infrastructure as Software\". You can find the replay on ",{"type":254,"tag":262,"props":32762,"children":32765},{"href":32763,"rel":32764},"https://youtu.be/V_Fis-hxQk4",[266],[32766],{"type":259,"value":32767},"YouTube",{"type":259,"value":32769}," (just so you know it's in French 😉).",{"type":254,"tag":255,"props":32771,"children":32772},{},[32773],{"type":254,"tag":1161,"props":32774,"children":32778},{"alt":32775,"className":32776,"src":32777},"Cover of the Cloud Ouest talk about Infrastructure as Code. ",[1165,1166],"/posts/images/2022_retro_4.png",[],{"type":254,"tag":255,"props":32780,"children":32781},{},[32782],{"type":259,"value":32783},"I wish I could have given the same talk at other developer conferences (and especially in-person ones). Unfortunately, my proposals at different Call For Papers were not accepted. To be honest I found it challenging to become a speaker at a developer conference. First, you have to find a topic and prepare your talk without knowing if will ever be selected. Then you have to submit it to different CFPs hoping it will catch the attention of the selection committee which already receives lots of proposals, including some from well-known speakers. All that requires a bit of work and maybe for nothing.",{"type":254,"tag":255,"props":32785,"children":32786},{},[32787],{"type":259,"value":32788},"Nevertheless, speaking at a developer conference is still a goal for 2023. Public speaking is something I want to do, something I want to be better at it, and the opportunity to meet nice people at events and chat with fellow developers. So I will keep submitting proposals to CFPs and see what will happen. I have a few topics in mind around .NET and Azure (which are my 2 fields of predilection) thus I should create new talk proposals. What bothers me a little is that there seem to be few conferences in France dedicated to .NET or Azure in France, but maybe I am not looking properly.",{"type":254,"tag":255,"props":32790,"children":32791},{},[32792,32794,32800,32802,32809],{"type":259,"value":32793},"I almost forgot to mention than in 2022 I participated in a live debate \"Pulumi vs Terraform\" organized by Microsoft Tech Group France. It was recorded so you can find it ",{"type":254,"tag":262,"props":32795,"children":32798},{"href":32796,"rel":32797},"https://youtu.be/7raXBE5XH7Y",[266],[32799],{"type":259,"value":1414},{"type":259,"value":32801}," (again it's in French). It was the first time I was doing a live on YouTube like that and this is not an easy exercise. Because it was a debate, you have to be ready to improvise to counter-argue. It's very different from a talk where you know exactly what you are going to say and when. But it was fun and I am grateful to ",{"type":254,"tag":262,"props":32803,"children":32806},{"href":32804,"rel":32805},"https://www.youtube.com/@MTGFrance_org",[266],[32807],{"type":259,"value":32808},"Microsoft Tech Group",{"type":259,"value":32810}," for giving me the opportunity to participate in this debate.",{"type":254,"tag":282,"props":32812,"children":32813},{"id":10456},[32814],{"type":259,"value":10462},{"type":254,"tag":255,"props":32816,"children":32817},{},[32818],{"type":259,"value":32819},"I have already mentioned most of the things I want to do as a developer in 2023 but not all of them. So let's recap what are my plans for 2023 as a developer:",{"type":254,"tag":332,"props":32821,"children":32822},{},[32823,32827,32832,32836,32840,32844],{"type":254,"tag":336,"props":32824,"children":32825},{},[32826],{"type":259,"value":10106},{"type":254,"tag":336,"props":32828,"children":32829},{},[32830],{"type":259,"value":32831},"explore Azure Container Apps and Dapr (I have already had a glimpse in 2022 but I want to know more)",{"type":254,"tag":336,"props":32833,"children":32834},{},[32835],{"type":259,"value":10116},{"type":254,"tag":336,"props":32837,"children":32838},{},[32839],{"type":259,"value":10121},{"type":254,"tag":336,"props":32841,"children":32842},{},[32843],{"type":259,"value":10126},{"type":254,"tag":336,"props":32845,"children":32846},{},[32847],{"type":259,"value":10131},{"type":254,"tag":255,"props":32849,"children":32850},{},[32851,32853,32860],{"type":259,"value":32852},"But above all, I want 2023 to be a year of learning and sharing as well. As I have said in ",{"type":254,"tag":262,"props":32854,"children":32857},{"href":32855,"rel":32856},"https://www.techwatching.dev/posts/technology-watch-part3#sharing-is-learning",[266],[32858],{"type":259,"value":32859},"one of my articles",{"type":259,"value":32861}," sharing is learning because when you share something you are consolidating your knowledge and continuing your learning journey. So enjoy 2023 and keep learning.",{"title":205,"searchDepth":635,"depth":635,"links":32863},[32864,32865,32870],{"id":32471,"depth":635,"text":32474},{"id":32507,"depth":635,"text":32510,"children":32866},[32867,32868,32869],{"id":32518,"depth":679,"text":32521},{"id":32583,"depth":679,"text":5},{"id":10177,"depth":679,"text":10180},{"id":10456,"depth":635,"text":10462},"content:1.posts:46.2022-retro.md","1.posts/46.2022-retro.md",{"_path":142,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":141,"description":32874,"lead":32875,"date":32876,"image":32877,"badge":32879,"tags":32880,"body":32883,"_type":1275,"_id":37206,"_source":231,"_file":37207,"_extension":1278},"Today is a great time to be a developer:","Pushing an ASP.NET Core API to Azure using .NET from provisioning to deployment","2022-12-18T00:00:00.000Z",{"src":32878},"/images/trees_1.jpg",{"label":16349},[1288,32881,10225,32882,19569,16349,6260],"Nuke","Pipelines",{"type":251,"children":32884,"toc":37177},[32885,32889,32912,32917,32922,32927,32933,32938,32943,32948,32954,32959,32969,32974,32995,33004,33009,33018,33023,33031,33040,33045,33051,33057,33062,33080,33085,33108,33114,33119,33128,33133,33156,33165,33170,33187,33199,33219,33228,33234,33239,33248,33253,33258,33274,33280,33286,33291,33300,33322,33330,33335,33341,33346,33559,33564,33715,33721,33743,33869,33875,33904,34107,34113,34133,34447,34464,34493,34556,34575,34581,34587,34606,34615,34623,34635,34641,34646,34671,35309,35314,35322,35328,35333,35346,35360,35478,35495,35508,35790,35803,35809,35821,36097,36103,36116,36129,36337,36342,36952,36969,36974,36979,36985,36990,36999,37012,37021,37029,37034,37043,37048,37057,37063,37069,37074,37106,37120,37126,37131,37152,37157,37162,37168,37173],{"type":254,"tag":255,"props":32886,"children":32887},{},[32888],{"type":259,"value":32874},{"type":254,"tag":332,"props":32890,"children":32891},{},[32892,32897,32902,32907],{"type":254,"tag":336,"props":32893,"children":32894},{},[32895],{"type":259,"value":32896},"there are plenty of languages and frameworks to choose from to build an application",{"type":254,"tag":336,"props":32898,"children":32899},{},[32900],{"type":259,"value":32901},"there are very powerful IDEs and tools to help us write, analyze, refactor, test and debug code",{"type":254,"tag":336,"props":32903,"children":32904},{},[32905],{"type":259,"value":32906},"there are many nice CI/CD platforms that allow us to package and deploy our applications anywhere",{"type":254,"tag":336,"props":32908,"children":32909},{},[32910],{"type":259,"value":32911},"thanks to cloud platforms and infrastructure as code we can provision infrastructure on-demand in an automated way",{"type":254,"tag":255,"props":32913,"children":32914},{},[32915],{"type":259,"value":32916},"Yet, sometimes it seems quite complex and time-consuming to deploy an application in the cloud.",{"type":254,"tag":255,"props":32918,"children":32919},{},[32920],{"type":259,"value":32921},"As a .NET developer, do I really need to master YAML, and Domain Specific Languages like HCL to deploy a simple ASP.NET Core API in Azure? Should I forget about local debugging when developing CI/CD pipelines? Do I have to learn everything from scratch each time I use another CI/CD platform?",{"type":254,"tag":255,"props":32923,"children":32924},{},[32925],{"type":259,"value":32926},"Thanks to Nuke and Pulumi, I don't think so and that is what we are going to talk about in this article.",{"type":254,"tag":282,"props":32928,"children":32930},{"id":32929},"the-scenario",[32931],{"type":259,"value":32932},"The scenario",{"type":254,"tag":255,"props":32934,"children":32935},{},[32936],{"type":259,"value":32937},"They are already lots of great articles about Pulumi or Nuke, so I won't spend time explaining what they are and why you should use them. Instead, I will show you how you can use them together with an example.",{"type":254,"tag":255,"props":32939,"children":32940},{},[32941],{"type":259,"value":32942},"My scenario is the following: I have a very basic ASP.NET Core API that I want to deploy to Azure App Service using a CI/CD pipeline.",{"type":254,"tag":255,"props":32944,"children":32945},{},[32946],{"type":259,"value":32947},"To do that, I want to use my existing .NET skills and code everything with the language and tools I know and love.",{"type":254,"tag":282,"props":32949,"children":32951},{"id":32950},"steps-of-the-cicd-pipeline",[32952],{"type":259,"value":32953},"Steps of the CI/CD pipeline",{"type":254,"tag":255,"props":32955,"children":32956},{},[32957],{"type":259,"value":32958},"There are often two main steps (or stages or whatever you call them) in a CI / CD pipeline: the packaging and the deployment.",{"type":254,"tag":255,"props":32960,"children":32961},{},[32962],{"type":254,"tag":1161,"props":32963,"children":32968},{"alt":32964,"className":32965,"src":32966,"width":32967},"Diagram with package and deploy steps.",[1165,1166],"/posts/images/pulumi_met_nuke_1.png",600,[],{"type":254,"tag":255,"props":32970,"children":32971},{},[32972],{"type":259,"value":32973},"To package a .NET application, we have to first restore the dependencies, then compile the application and publish it. So my Package step is composed of 3 steps.",{"type":254,"tag":427,"props":32975,"children":32976},{"icon":429},[32977],{"type":254,"tag":255,"props":32978,"children":32979},{},[32980,32982,32993],{"type":259,"value":32981},"A ",{"type":254,"tag":262,"props":32983,"children":32986},{"href":32984,"rel":32985},"https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-publish#description",[266],[32987],{"type":254,"tag":389,"props":32988,"children":32990},{"className":32989},[],[32991],{"type":259,"value":32992},"dotnet publish",{"type":259,"value":32994}," does an implicit restore and build the application so only one step could be used but I like separating these steps for clarity. Moreover it is sometimes needed, for instance when you are restoring packages from private Nuget feeds.",{"type":254,"tag":255,"props":32996,"children":32997},{},[32998],{"type":254,"tag":1161,"props":32999,"children":33003},{"alt":33000,"className":33001,"src":33002,"width":19735},"Diagram with restore, compile, publish and deploy steps.",[1165,1166],"/posts/images/pulumi_met_nuke_2.png",[],{"type":254,"tag":255,"props":33005,"children":33006},{},[33007],{"type":259,"value":33008},"I said the application needed to be deployed to Azure App Service but I don't have an existing Azure App Service resource, and I don't want to manually create one. So I also need a step to deploy the infrastructure",{"type":254,"tag":255,"props":33010,"children":33011},{},[33012],{"type":254,"tag":1161,"props":33013,"children":33017},{"alt":33014,"className":33015,"src":33016,"width":19735},"Diagram with restore, compile, publish, deploy and provision infra steps.",[1165,1166],"/posts/images/pulumi_met_nuke_3.png",[],{"type":254,"tag":255,"props":33019,"children":33020},{},[33021],{"type":259,"value":33022},"It seems fine. I will just add another optional step at the beginning to clean the temporary files I could have created on previous builds.",{"type":254,"tag":427,"props":33024,"children":33025},{"icon":567},[33026],{"type":254,"tag":255,"props":33027,"children":33028},{},[33029],{"type":259,"value":33030},"If your pipeline runs on a hosted agent/runner (managed by the CI/CD platform you use), the Clean step might not be very useful but I intend to also run this pipeline locally.",{"type":254,"tag":255,"props":33032,"children":33033},{},[33034],{"type":254,"tag":1161,"props":33035,"children":33039},{"alt":33036,"className":33037,"src":33038},"Diagram with clean, restore, compile, publish, deploy and provision infra steps.",[1165,1166],"/posts/images/pulumi_met_nuke_4.png",[],{"type":254,"tag":255,"props":33041,"children":33042},{},[33043],{"type":259,"value":33044},"Now, that we know the different steps of our pipeline, let's get to the code.",{"type":254,"tag":282,"props":33046,"children":33048},{"id":33047},"getting-started-with-the-code",[33049],{"type":259,"value":33050},"Getting started with the code",{"type":254,"tag":455,"props":33052,"children":33054},{"id":33053},"code-organization",[33055],{"type":259,"value":33056},"Code organization",{"type":254,"tag":255,"props":33058,"children":33059},{},[33060],{"type":259,"value":33061},"I put all the code in the same Git repository because:",{"type":254,"tag":332,"props":33063,"children":33064},{},[33065,33070,33075],{"type":254,"tag":336,"props":33066,"children":33067},{},[33068],{"type":259,"value":33069},"it makes sense as everything is linked",{"type":254,"tag":336,"props":33071,"children":33072},{},[33073],{"type":259,"value":33074},"it's easier to maintain (all the code in one place)",{"type":254,"tag":336,"props":33076,"children":33077},{},[33078],{"type":259,"value":33079},"it's easier to version (one tag on one commit in one repository)",{"type":254,"tag":255,"props":33081,"children":33082},{},[33083],{"type":259,"value":33084},"I chose to organize my repository with the following folders:",{"type":254,"tag":332,"props":33086,"children":33087},{},[33088,33093,33098,33103],{"type":254,"tag":336,"props":33089,"children":33090},{},[33091],{"type":259,"value":33092},"📁 src ➡️ for the application code of the API",{"type":254,"tag":336,"props":33094,"children":33095},{},[33096],{"type":259,"value":33097},"📁 infra ➡️ for the infrastructure code that provisions the App Service",{"type":254,"tag":336,"props":33099,"children":33100},{},[33101],{"type":259,"value":33102},"📁 build ➡️ for pipeline code that builds and deploys the application",{"type":254,"tag":336,"props":33104,"children":33105},{},[33106],{"type":259,"value":33107},"📁 artifacts ➡️ for the package created by the pipeline",{"type":254,"tag":455,"props":33109,"children":33111},{"id":33110},"create-the-c-projects",[33112],{"type":259,"value":33113},"Create the C# projects",{"type":254,"tag":255,"props":33115,"children":33116},{},[33117],{"type":259,"value":33118},"To create the API project, we just use the default ASP.NET Core API template in .NET 7 that creates a simple Weather API.",{"type":254,"tag":255,"props":33120,"children":33121},{},[33122],{"type":254,"tag":1161,"props":33123,"children":33127},{"alt":33124,"className":33125,"src":33126},"IDE folder explorer of an ASP.NET Core API template.",[1165,1166],"/posts/images/pulumi_met_nuke_5.png",[],{"type":254,"tag":255,"props":33129,"children":33130},{},[33131],{"type":259,"value":33132},"I can initialize the infrastructure project using the Pulumi CLI new command with the azure C# template:",{"type":254,"tag":612,"props":33134,"children":33136},{"className":16836,"code":33135,"language":16838,"meta":205,"style":205},"pulumi new azure-csharp\n",[33137],{"type":254,"tag":389,"props":33138,"children":33139},{"__ignoreMap":205},[33140],{"type":254,"tag":622,"props":33141,"children":33142},{"class":624,"line":625},[33143,33147,33151],{"type":254,"tag":622,"props":33144,"children":33145},{"style":714},[33146],{"type":259,"value":19768},{"type":254,"tag":622,"props":33148,"children":33149},{"style":664},[33150],{"type":259,"value":1624},{"type":254,"tag":622,"props":33152,"children":33153},{"style":664},[33154],{"type":259,"value":33155}," azure-csharp\n",{"type":254,"tag":255,"props":33157,"children":33158},{},[33159],{"type":254,"tag":1161,"props":33160,"children":33164},{"alt":33161,"className":33162,"src":33163},"IDE folder explorer focused on the infra folder containing the Pulumi project.",[1165,1166],"/posts/images/pulumi_met_nuke_6.png",[],{"type":254,"tag":255,"props":33166,"children":33167},{},[33168],{"type":259,"value":33169},"I will show later how to modify the code of the template to provision an App Service.",{"type":254,"tag":31352,"props":33171,"children":33172},{},[33173],{"type":254,"tag":255,"props":33174,"children":33175},{},[33176,33178,33185],{"type":259,"value":33177},"You can check Pulumi ",{"type":254,"tag":262,"props":33179,"children":33182},{"href":33180,"rel":33181},"https://www.pulumi.com/docs/get-started/azure/",[266],[33183],{"type":259,"value":33184},"Getting Started with Azure",{"type":259,"value":33186}," tutorial to see how to set up your environment and start creating Azure resources in C# (or in another language).",{"type":254,"tag":255,"props":33188,"children":33189},{},[33190,33192,33198],{"type":259,"value":33191},"To initialize the build project, we can use Nuke's .NET global tool as explained in the ",{"type":254,"tag":262,"props":33193,"children":33196},{"href":33194,"rel":33195},"https://nuke.build/docs/getting-started/setup/",[266],[33197],{"type":259,"value":6340},{"type":259,"value":474},{"type":254,"tag":612,"props":33200,"children":33202},{"className":16836,"code":33201,"language":16838,"meta":205,"style":205},"nuke :setup\n",[33203],{"type":254,"tag":389,"props":33204,"children":33205},{"__ignoreMap":205},[33206],{"type":254,"tag":622,"props":33207,"children":33208},{"class":624,"line":625},[33209,33214],{"type":254,"tag":622,"props":33210,"children":33211},{"style":714},[33212],{"type":259,"value":33213},"nuke",{"type":254,"tag":622,"props":33215,"children":33216},{"style":664},[33217],{"type":259,"value":33218}," :setup\n",{"type":254,"tag":255,"props":33220,"children":33221},{},[33222],{"type":254,"tag":1161,"props":33223,"children":33227},{"alt":33224,"className":33225,"src":33226},"IDE folder explorer focused on the build folder containing the Nuke project.",[1165,1166],"/posts/images/pulumi_met_nuke_7.png",[],{"type":254,"tag":455,"props":33229,"children":33231},{"id":33230},"everything-in-net",[33232],{"type":259,"value":33233},"Everything in .NET",{"type":254,"tag":255,"props":33235,"children":33236},{},[33237],{"type":259,"value":33238},"What I like about using Pulumi (in .NET) and Nuke is that all the code is just C# code.  My infrastructure project and my build project are standard .NET console applications. And I can open the 3 projects (API, infrastructure, and build) in the same solution in my preferred IDE.",{"type":254,"tag":255,"props":33240,"children":33241},{},[33242],{"type":254,"tag":1161,"props":33243,"children":33247},{"alt":33244,"className":33245,"src":33246},"IDE solution explorer witht the infra, build and api projects.",[1165,1166],"/posts/images/pulumi_met_nuke_8.png",[],{"type":254,"tag":255,"props":33249,"children":33250},{},[33251],{"type":259,"value":33252},"Why does it matter? Because any .NET developer in a team would be able to understand and maintain this code. How many times have you seen a project slow down because the person responsible for the infrastructure code written in YAML, JSON, Bicep, or HCL was on vacation or ill? How often have you been stuck because the only few people in the team that knew how to modify the YAML pipelines were not available?",{"type":254,"tag":255,"props":33254,"children":33255},{},[33256],{"type":259,"value":33257},"But it's not a question of knowledge only. It's also because the developer experience of writing build or infrastructure code in .NET is much better than writing code in YAML or other declarative \"languages\".",{"type":254,"tag":427,"props":33259,"children":33260},{"icon":429},[33261],{"type":254,"tag":255,"props":33262,"children":33263},{},[33264,33266,33272],{"type":259,"value":33265},"I talk a lot about the benefits of using programming languages for infrastructure code in my article \"",{"type":254,"tag":262,"props":33267,"children":33270},{"href":33268,"rel":33269},"https://www.techwatching.dev/posts/pulumi-vs-terraform",[266],[33271],{"type":259,"value":114},{"type":259,"value":33273},"\" if you have not read it yet.",{"type":254,"tag":282,"props":33275,"children":33277},{"id":33276},"implementing-the-pipeline-steps-from-clean-to-publish",[33278],{"type":259,"value":33279},"Implementing the pipeline steps from Clean to Publish",{"type":254,"tag":455,"props":33281,"children":33283},{"id":33282},"the-nuke-pipeline",[33284],{"type":259,"value":33285},"The Nuke pipeline",{"type":254,"tag":255,"props":33287,"children":33288},{},[33289],{"type":259,"value":33290},"Here is what looks like the default build project after its creation:",{"type":254,"tag":255,"props":33292,"children":33293},{},[33294],{"type":254,"tag":1161,"props":33295,"children":33299},{"alt":33296,"className":33297,"src":33298},"Buidl code file with the build steps opened in the IDE.",[1165,1166],"/posts/images/pulumi_met_nuke_9.png",[],{"type":254,"tag":255,"props":33301,"children":33302},{},[33303,33305,33311,33313,33320],{"type":259,"value":33304},"The main method is contained in a ",{"type":254,"tag":389,"props":33306,"children":33308},{"className":33307},[],[33309],{"type":259,"value":33310},"Build.cs",{"type":259,"value":33312}," file. This file contains the steps of the pipeline that are called ",{"type":254,"tag":262,"props":33314,"children":33317},{"href":33315,"rel":33316},"https://nuke.build/docs/fundamentals/targets/",[266],[33318],{"type":259,"value":33319},"Target",{"type":259,"value":33321}," in Nuke. We can set the dependencies between targets.",{"type":254,"tag":427,"props":33323,"children":33324},{"icon":567},[33325],{"type":254,"tag":255,"props":33326,"children":33327},{},[33328],{"type":259,"value":33329},"The build project is a .NET console application so it works out of the box in any .NET IDE or from the command line. But to be more productive you can install a plugin for your IDE that will add snippets and a way to easily debug each target individually.",{"type":254,"tag":255,"props":33331,"children":33332},{},[33333],{"type":259,"value":33334},"As you see we can define properties with the attribute Parameter if we need to pass parameters to our pipeline, like the Configuration parameter.",{"type":254,"tag":455,"props":33336,"children":33338},{"id":33337},"the-clean-target",[33339],{"type":259,"value":33340},"The Clean target",{"type":254,"tag":255,"props":33342,"children":33343},{},[33344],{"type":259,"value":33345},"We can define the Clean target like that:",{"type":254,"tag":612,"props":33347,"children":33349},{"className":1423,"code":33348,"language":1425,"meta":205,"style":205},"Target Clean => _ => _  \n    .Before(Restore)  \n    .Executes(() =>  \n    {  \n        SourceDirectory.GlobDirectories(\"*/bin\", \"*/obj\").ForEach(DeleteDirectory);  \n        EnsureCleanDirectory(ArtifactsDirectory);  \n    });\n",[33350],{"type":254,"tag":389,"props":33351,"children":33352},{"__ignoreMap":205},[33353,33382,33412,33437,33449,33527,33552],{"type":254,"tag":622,"props":33354,"children":33355},{"class":624,"line":625},[33356,33361,33365,33369,33373,33377],{"type":254,"tag":622,"props":33357,"children":33358},{"style":1202},[33359],{"type":259,"value":33360},"Target ",{"type":254,"tag":622,"props":33362,"children":33363},{"style":714},[33364],{"type":259,"value":20737},{"type":254,"tag":622,"props":33366,"children":33367},{"style":629},[33368],{"type":259,"value":2697},{"type":254,"tag":622,"props":33370,"children":33371},{"style":714},[33372],{"type":259,"value":8954},{"type":254,"tag":622,"props":33374,"children":33375},{"style":629},[33376],{"type":259,"value":2697},{"type":254,"tag":622,"props":33378,"children":33379},{"style":1202},[33380],{"type":259,"value":33381}," _  \n",{"type":254,"tag":622,"props":33383,"children":33384},{"class":624,"line":635},[33385,33389,33394,33398,33403,33407],{"type":254,"tag":622,"props":33386,"children":33387},{"style":629},[33388],{"type":259,"value":2678},{"type":254,"tag":622,"props":33390,"children":33391},{"style":1735},[33392],{"type":259,"value":33393},"Before",{"type":254,"tag":622,"props":33395,"children":33396},{"style":629},[33397],{"type":259,"value":1668},{"type":254,"tag":622,"props":33399,"children":33400},{"style":1202},[33401],{"type":259,"value":33402},"Restore",{"type":254,"tag":622,"props":33404,"children":33405},{"style":629},[33406],{"type":259,"value":2309},{"type":254,"tag":622,"props":33408,"children":33409},{"style":1202},[33410],{"type":259,"value":33411},"  \n",{"type":254,"tag":622,"props":33413,"children":33414},{"class":624,"line":679},[33415,33419,33424,33429,33433],{"type":254,"tag":622,"props":33416,"children":33417},{"style":629},[33418],{"type":259,"value":2678},{"type":254,"tag":622,"props":33420,"children":33421},{"style":1735},[33422],{"type":259,"value":33423},"Executes",{"type":254,"tag":622,"props":33425,"children":33426},{"style":629},[33427],{"type":259,"value":33428},"(()",{"type":254,"tag":622,"props":33430,"children":33431},{"style":629},[33432],{"type":259,"value":2697},{"type":254,"tag":622,"props":33434,"children":33435},{"style":1202},[33436],{"type":259,"value":33411},{"type":254,"tag":622,"props":33438,"children":33439},{"class":624,"line":705},[33440,33445],{"type":254,"tag":622,"props":33441,"children":33442},{"style":629},[33443],{"type":259,"value":33444},"    {",{"type":254,"tag":622,"props":33446,"children":33447},{"style":1202},[33448],{"type":259,"value":33411},{"type":254,"tag":622,"props":33450,"children":33451},{"class":624,"line":732},[33452,33457,33461,33466,33470,33474,33479,33483,33487,33491,33496,33500,33504,33509,33513,33518,33523],{"type":254,"tag":622,"props":33453,"children":33454},{"style":1202},[33455],{"type":259,"value":33456},"        SourceDirectory",{"type":254,"tag":622,"props":33458,"children":33459},{"style":629},[33460],{"type":259,"value":594},{"type":254,"tag":622,"props":33462,"children":33463},{"style":1735},[33464],{"type":259,"value":33465},"GlobDirectories",{"type":254,"tag":622,"props":33467,"children":33468},{"style":629},[33469],{"type":259,"value":1668},{"type":254,"tag":622,"props":33471,"children":33472},{"style":629},[33473],{"type":259,"value":652},{"type":254,"tag":622,"props":33475,"children":33476},{"style":664},[33477],{"type":259,"value":33478},"*/bin",{"type":254,"tag":622,"props":33480,"children":33481},{"style":629},[33482],{"type":259,"value":652},{"type":254,"tag":622,"props":33484,"children":33485},{"style":629},[33486],{"type":259,"value":3149},{"type":254,"tag":622,"props":33488,"children":33489},{"style":629},[33490],{"type":259,"value":661},{"type":254,"tag":622,"props":33492,"children":33493},{"style":664},[33494],{"type":259,"value":33495},"*/obj",{"type":254,"tag":622,"props":33497,"children":33498},{"style":629},[33499],{"type":259,"value":652},{"type":254,"tag":622,"props":33501,"children":33502},{"style":629},[33503],{"type":259,"value":2280},{"type":254,"tag":622,"props":33505,"children":33506},{"style":1735},[33507],{"type":259,"value":33508},"ForEach",{"type":254,"tag":622,"props":33510,"children":33511},{"style":629},[33512],{"type":259,"value":1668},{"type":254,"tag":622,"props":33514,"children":33515},{"style":1202},[33516],{"type":259,"value":33517},"DeleteDirectory",{"type":254,"tag":622,"props":33519,"children":33520},{"style":629},[33521],{"type":259,"value":33522},");",{"type":254,"tag":622,"props":33524,"children":33525},{"style":1202},[33526],{"type":259,"value":33411},{"type":254,"tag":622,"props":33528,"children":33529},{"class":624,"line":759},[33530,33535,33539,33544,33548],{"type":254,"tag":622,"props":33531,"children":33532},{"style":1735},[33533],{"type":259,"value":33534},"        EnsureCleanDirectory",{"type":254,"tag":622,"props":33536,"children":33537},{"style":629},[33538],{"type":259,"value":1668},{"type":254,"tag":622,"props":33540,"children":33541},{"style":1202},[33542],{"type":259,"value":33543},"ArtifactsDirectory",{"type":254,"tag":622,"props":33545,"children":33546},{"style":629},[33547],{"type":259,"value":33522},{"type":254,"tag":622,"props":33549,"children":33550},{"style":1202},[33551],{"type":259,"value":33411},{"type":254,"tag":622,"props":33553,"children":33554},{"class":624,"line":798},[33555],{"type":254,"tag":622,"props":33556,"children":33557},{"style":629},[33558],{"type":259,"value":8244},{"type":254,"tag":255,"props":33560,"children":33561},{},[33562],{"type":259,"value":33563},"This code deletes all the bin and obj directories of the source directory. It also deletes the content in the artifacts directory. Nuke overloads the division operator to allow us to easily define paths in the project.",{"type":254,"tag":612,"props":33565,"children":33567},{"className":1423,"code":33566,"language":1425,"meta":205,"style":205},"AbsolutePath SourceDirectory => RootDirectory / \"src\";  \n  \nAbsolutePath InfrastructureDirectory => RootDirectory / \"infra\";  \n  \nAbsolutePath ArtifactsDirectory => RootDirectory / \"artifacts\";\n",[33568],{"type":254,"tag":389,"props":33569,"children":33570},{"__ignoreMap":205},[33571,33618,33625,33669,33676],{"type":254,"tag":622,"props":33572,"children":33573},{"class":624,"line":625},[33574,33579,33584,33588,33593,33597,33601,33606,33610,33614],{"type":254,"tag":622,"props":33575,"children":33576},{"style":1202},[33577],{"type":259,"value":33578},"AbsolutePath ",{"type":254,"tag":622,"props":33580,"children":33581},{"style":714},[33582],{"type":259,"value":33583},"SourceDirectory",{"type":254,"tag":622,"props":33585,"children":33586},{"style":629},[33587],{"type":259,"value":2697},{"type":254,"tag":622,"props":33589,"children":33590},{"style":1202},[33591],{"type":259,"value":33592}," RootDirectory ",{"type":254,"tag":622,"props":33594,"children":33595},{"style":629},[33596],{"type":259,"value":9768},{"type":254,"tag":622,"props":33598,"children":33599},{"style":629},[33600],{"type":259,"value":661},{"type":254,"tag":622,"props":33602,"children":33603},{"style":664},[33604],{"type":259,"value":33605},"src",{"type":254,"tag":622,"props":33607,"children":33608},{"style":629},[33609],{"type":259,"value":652},{"type":254,"tag":622,"props":33611,"children":33612},{"style":629},[33613],{"type":259,"value":2000},{"type":254,"tag":622,"props":33615,"children":33616},{"style":1202},[33617],{"type":259,"value":33411},{"type":254,"tag":622,"props":33619,"children":33620},{"class":624,"line":635},[33621],{"type":254,"tag":622,"props":33622,"children":33623},{"style":1202},[33624],{"type":259,"value":33411},{"type":254,"tag":622,"props":33626,"children":33627},{"class":624,"line":679},[33628,33632,33637,33641,33645,33649,33653,33657,33661,33665],{"type":254,"tag":622,"props":33629,"children":33630},{"style":1202},[33631],{"type":259,"value":33578},{"type":254,"tag":622,"props":33633,"children":33634},{"style":714},[33635],{"type":259,"value":33636},"InfrastructureDirectory",{"type":254,"tag":622,"props":33638,"children":33639},{"style":629},[33640],{"type":259,"value":2697},{"type":254,"tag":622,"props":33642,"children":33643},{"style":1202},[33644],{"type":259,"value":33592},{"type":254,"tag":622,"props":33646,"children":33647},{"style":629},[33648],{"type":259,"value":9768},{"type":254,"tag":622,"props":33650,"children":33651},{"style":629},[33652],{"type":259,"value":661},{"type":254,"tag":622,"props":33654,"children":33655},{"style":664},[33656],{"type":259,"value":16314},{"type":254,"tag":622,"props":33658,"children":33659},{"style":629},[33660],{"type":259,"value":652},{"type":254,"tag":622,"props":33662,"children":33663},{"style":629},[33664],{"type":259,"value":2000},{"type":254,"tag":622,"props":33666,"children":33667},{"style":1202},[33668],{"type":259,"value":33411},{"type":254,"tag":622,"props":33670,"children":33671},{"class":624,"line":705},[33672],{"type":254,"tag":622,"props":33673,"children":33674},{"style":1202},[33675],{"type":259,"value":33411},{"type":254,"tag":622,"props":33677,"children":33678},{"class":624,"line":732},[33679,33683,33687,33691,33695,33699,33703,33707,33711],{"type":254,"tag":622,"props":33680,"children":33681},{"style":1202},[33682],{"type":259,"value":33578},{"type":254,"tag":622,"props":33684,"children":33685},{"style":714},[33686],{"type":259,"value":33543},{"type":254,"tag":622,"props":33688,"children":33689},{"style":629},[33690],{"type":259,"value":2697},{"type":254,"tag":622,"props":33692,"children":33693},{"style":1202},[33694],{"type":259,"value":33592},{"type":254,"tag":622,"props":33696,"children":33697},{"style":629},[33698],{"type":259,"value":9768},{"type":254,"tag":622,"props":33700,"children":33701},{"style":629},[33702],{"type":259,"value":661},{"type":254,"tag":622,"props":33704,"children":33705},{"style":664},[33706],{"type":259,"value":20421},{"type":254,"tag":622,"props":33708,"children":33709},{"style":629},[33710],{"type":259,"value":652},{"type":254,"tag":622,"props":33712,"children":33713},{"style":629},[33714],{"type":259,"value":2350},{"type":254,"tag":455,"props":33716,"children":33718},{"id":33717},"the-restore-target",[33719],{"type":259,"value":33720},"The Restore target",{"type":254,"tag":255,"props":33722,"children":33723},{},[33724,33726,33732,33734,33741],{"type":259,"value":33725},"To restore .NET dependencies, we can use the ",{"type":254,"tag":389,"props":33727,"children":33729},{"className":33728},[],[33730],{"type":259,"value":33731},"dotnet restore",{"type":259,"value":33733}," command. Nuke supports ",{"type":254,"tag":262,"props":33735,"children":33738},{"href":33736,"rel":33737},"https://nuke.build/docs/common/cli-tools/",[266],[33739],{"type":259,"value":33740},"executing CLI tools",{"type":259,"value":33742}," and has even auto-generated CLI wrappers for some common tools like dotnet CLI to use a Fluent API instead of string interpolation to pass parameters.",{"type":254,"tag":612,"props":33744,"children":33746},{"className":1423,"code":33745,"language":1425,"meta":205,"style":205},"Target Restore => _ => _  \n    .Executes(() =>  \n    {  \n        DotNetRestore(_ => _.SetProjectFile(Solution));  \n    });\n",[33747],{"type":254,"tag":389,"props":33748,"children":33749},{"__ignoreMap":205},[33750,33777,33800,33811,33862],{"type":254,"tag":622,"props":33751,"children":33752},{"class":624,"line":625},[33753,33757,33761,33765,33769,33773],{"type":254,"tag":622,"props":33754,"children":33755},{"style":1202},[33756],{"type":259,"value":33360},{"type":254,"tag":622,"props":33758,"children":33759},{"style":714},[33760],{"type":259,"value":33402},{"type":254,"tag":622,"props":33762,"children":33763},{"style":629},[33764],{"type":259,"value":2697},{"type":254,"tag":622,"props":33766,"children":33767},{"style":714},[33768],{"type":259,"value":8954},{"type":254,"tag":622,"props":33770,"children":33771},{"style":629},[33772],{"type":259,"value":2697},{"type":254,"tag":622,"props":33774,"children":33775},{"style":1202},[33776],{"type":259,"value":33381},{"type":254,"tag":622,"props":33778,"children":33779},{"class":624,"line":635},[33780,33784,33788,33792,33796],{"type":254,"tag":622,"props":33781,"children":33782},{"style":629},[33783],{"type":259,"value":2678},{"type":254,"tag":622,"props":33785,"children":33786},{"style":1735},[33787],{"type":259,"value":33423},{"type":254,"tag":622,"props":33789,"children":33790},{"style":629},[33791],{"type":259,"value":33428},{"type":254,"tag":622,"props":33793,"children":33794},{"style":629},[33795],{"type":259,"value":2697},{"type":254,"tag":622,"props":33797,"children":33798},{"style":1202},[33799],{"type":259,"value":33411},{"type":254,"tag":622,"props":33801,"children":33802},{"class":624,"line":679},[33803,33807],{"type":254,"tag":622,"props":33804,"children":33805},{"style":629},[33806],{"type":259,"value":33444},{"type":254,"tag":622,"props":33808,"children":33809},{"style":1202},[33810],{"type":259,"value":33411},{"type":254,"tag":622,"props":33812,"children":33813},{"class":624,"line":705},[33814,33819,33823,33827,33831,33835,33839,33844,33848,33853,33858],{"type":254,"tag":622,"props":33815,"children":33816},{"style":1735},[33817],{"type":259,"value":33818},"        DotNetRestore",{"type":254,"tag":622,"props":33820,"children":33821},{"style":629},[33822],{"type":259,"value":1668},{"type":254,"tag":622,"props":33824,"children":33825},{"style":714},[33826],{"type":259,"value":8945},{"type":254,"tag":622,"props":33828,"children":33829},{"style":629},[33830],{"type":259,"value":2697},{"type":254,"tag":622,"props":33832,"children":33833},{"style":1202},[33834],{"type":259,"value":8954},{"type":254,"tag":622,"props":33836,"children":33837},{"style":629},[33838],{"type":259,"value":594},{"type":254,"tag":622,"props":33840,"children":33841},{"style":1735},[33842],{"type":259,"value":33843},"SetProjectFile",{"type":254,"tag":622,"props":33845,"children":33846},{"style":629},[33847],{"type":259,"value":1668},{"type":254,"tag":622,"props":33849,"children":33850},{"style":1202},[33851],{"type":259,"value":33852},"Solution",{"type":254,"tag":622,"props":33854,"children":33855},{"style":629},[33856],{"type":259,"value":33857},"));",{"type":254,"tag":622,"props":33859,"children":33860},{"style":1202},[33861],{"type":259,"value":33411},{"type":254,"tag":622,"props":33863,"children":33864},{"class":624,"line":732},[33865],{"type":254,"tag":622,"props":33866,"children":33867},{"style":629},[33868],{"type":259,"value":8244},{"type":254,"tag":455,"props":33870,"children":33872},{"id":33871},"the-compile-target",[33873],{"type":259,"value":33874},"The Compile target",{"type":254,"tag":255,"props":33876,"children":33877},{},[33878,33880,33886,33888,33894,33896,33902],{"type":259,"value":33879},"The compile target uses the ",{"type":254,"tag":389,"props":33881,"children":33883},{"className":33882},[],[33884],{"type":259,"value":33885},"dotnet build",{"type":259,"value":33887}," command. We can start to see the benefits of using this Fluent API that provides us with autocompletion and documentation. For instance, as we already restored the dependencies in the previous step, we can set the ",{"type":254,"tag":389,"props":33889,"children":33891},{"className":33890},[],[33892],{"type":259,"value":33893},"--no-restore",{"type":259,"value":33895}," option using the ",{"type":254,"tag":389,"props":33897,"children":33899},{"className":33898},[],[33900],{"type":259,"value":33901},"EnableNoRestore",{"type":259,"value":33903}," auto-generated method.",{"type":254,"tag":612,"props":33905,"children":33907},{"className":1423,"code":33906,"language":1425,"meta":205,"style":205},"Target Compile => _ => _  \n    .DependsOn(Restore)  \n    .Executes(() =>  \n    {  \n        DotNetBuild(_ => _  \n            .SetProjectFile(Solution)  \n            .SetConfiguration(Configuration)  \n            .EnableNoRestore());  \n    });\n",[33908],{"type":254,"tag":389,"props":33909,"children":33910},{"__ignoreMap":205},[33911,33939,33967,33990,34001,34025,34052,34080,34100],{"type":254,"tag":622,"props":33912,"children":33913},{"class":624,"line":625},[33914,33918,33923,33927,33931,33935],{"type":254,"tag":622,"props":33915,"children":33916},{"style":1202},[33917],{"type":259,"value":33360},{"type":254,"tag":622,"props":33919,"children":33920},{"style":714},[33921],{"type":259,"value":33922},"Compile",{"type":254,"tag":622,"props":33924,"children":33925},{"style":629},[33926],{"type":259,"value":2697},{"type":254,"tag":622,"props":33928,"children":33929},{"style":714},[33930],{"type":259,"value":8954},{"type":254,"tag":622,"props":33932,"children":33933},{"style":629},[33934],{"type":259,"value":2697},{"type":254,"tag":622,"props":33936,"children":33937},{"style":1202},[33938],{"type":259,"value":33381},{"type":254,"tag":622,"props":33940,"children":33941},{"class":624,"line":635},[33942,33946,33951,33955,33959,33963],{"type":254,"tag":622,"props":33943,"children":33944},{"style":629},[33945],{"type":259,"value":2678},{"type":254,"tag":622,"props":33947,"children":33948},{"style":1735},[33949],{"type":259,"value":33950},"DependsOn",{"type":254,"tag":622,"props":33952,"children":33953},{"style":629},[33954],{"type":259,"value":1668},{"type":254,"tag":622,"props":33956,"children":33957},{"style":1202},[33958],{"type":259,"value":33402},{"type":254,"tag":622,"props":33960,"children":33961},{"style":629},[33962],{"type":259,"value":2309},{"type":254,"tag":622,"props":33964,"children":33965},{"style":1202},[33966],{"type":259,"value":33411},{"type":254,"tag":622,"props":33968,"children":33969},{"class":624,"line":679},[33970,33974,33978,33982,33986],{"type":254,"tag":622,"props":33971,"children":33972},{"style":629},[33973],{"type":259,"value":2678},{"type":254,"tag":622,"props":33975,"children":33976},{"style":1735},[33977],{"type":259,"value":33423},{"type":254,"tag":622,"props":33979,"children":33980},{"style":629},[33981],{"type":259,"value":33428},{"type":254,"tag":622,"props":33983,"children":33984},{"style":629},[33985],{"type":259,"value":2697},{"type":254,"tag":622,"props":33987,"children":33988},{"style":1202},[33989],{"type":259,"value":33411},{"type":254,"tag":622,"props":33991,"children":33992},{"class":624,"line":705},[33993,33997],{"type":254,"tag":622,"props":33994,"children":33995},{"style":629},[33996],{"type":259,"value":33444},{"type":254,"tag":622,"props":33998,"children":33999},{"style":1202},[34000],{"type":259,"value":33411},{"type":254,"tag":622,"props":34002,"children":34003},{"class":624,"line":732},[34004,34009,34013,34017,34021],{"type":254,"tag":622,"props":34005,"children":34006},{"style":1735},[34007],{"type":259,"value":34008},"        DotNetBuild",{"type":254,"tag":622,"props":34010,"children":34011},{"style":629},[34012],{"type":259,"value":1668},{"type":254,"tag":622,"props":34014,"children":34015},{"style":714},[34016],{"type":259,"value":8945},{"type":254,"tag":622,"props":34018,"children":34019},{"style":629},[34020],{"type":259,"value":2697},{"type":254,"tag":622,"props":34022,"children":34023},{"style":1202},[34024],{"type":259,"value":33381},{"type":254,"tag":622,"props":34026,"children":34027},{"class":624,"line":759},[34028,34032,34036,34040,34044,34048],{"type":254,"tag":622,"props":34029,"children":34030},{"style":629},[34031],{"type":259,"value":9102},{"type":254,"tag":622,"props":34033,"children":34034},{"style":1735},[34035],{"type":259,"value":33843},{"type":254,"tag":622,"props":34037,"children":34038},{"style":629},[34039],{"type":259,"value":1668},{"type":254,"tag":622,"props":34041,"children":34042},{"style":1202},[34043],{"type":259,"value":33852},{"type":254,"tag":622,"props":34045,"children":34046},{"style":629},[34047],{"type":259,"value":2309},{"type":254,"tag":622,"props":34049,"children":34050},{"style":1202},[34051],{"type":259,"value":33411},{"type":254,"tag":622,"props":34053,"children":34054},{"class":624,"line":798},[34055,34059,34064,34068,34072,34076],{"type":254,"tag":622,"props":34056,"children":34057},{"style":629},[34058],{"type":259,"value":9102},{"type":254,"tag":622,"props":34060,"children":34061},{"style":1735},[34062],{"type":259,"value":34063},"SetConfiguration",{"type":254,"tag":622,"props":34065,"children":34066},{"style":629},[34067],{"type":259,"value":1668},{"type":254,"tag":622,"props":34069,"children":34070},{"style":1202},[34071],{"type":259,"value":2924},{"type":254,"tag":622,"props":34073,"children":34074},{"style":629},[34075],{"type":259,"value":2309},{"type":254,"tag":622,"props":34077,"children":34078},{"style":1202},[34079],{"type":259,"value":33411},{"type":254,"tag":622,"props":34081,"children":34082},{"class":624,"line":836},[34083,34087,34091,34096],{"type":254,"tag":622,"props":34084,"children":34085},{"style":629},[34086],{"type":259,"value":9102},{"type":254,"tag":622,"props":34088,"children":34089},{"style":1735},[34090],{"type":259,"value":33901},{"type":254,"tag":622,"props":34092,"children":34093},{"style":629},[34094],{"type":259,"value":34095},"());",{"type":254,"tag":622,"props":34097,"children":34098},{"style":1202},[34099],{"type":259,"value":33411},{"type":254,"tag":622,"props":34101,"children":34102},{"class":624,"line":862},[34103],{"type":254,"tag":622,"props":34104,"children":34105},{"style":629},[34106],{"type":259,"value":8244},{"type":254,"tag":455,"props":34108,"children":34110},{"id":34109},"the-publish-target",[34111],{"type":259,"value":34112},"The Publish target",{"type":254,"tag":255,"props":34114,"children":34115},{},[34116,34118,34123,34125,34131],{"type":259,"value":34117},"The publish target uses the ",{"type":254,"tag":389,"props":34119,"children":34121},{"className":34120},[],[34122],{"type":259,"value":32992},{"type":259,"value":34124}," command and then creates a zip ",{"type":254,"tag":389,"props":34126,"children":34128},{"className":34127},[],[34129],{"type":259,"value":34130},"api.zip",{"type":259,"value":34132}," of the resulting package in the artifacts directory.",{"type":254,"tag":612,"props":34134,"children":34136},{"className":1423,"code":34135,"language":1425,"meta":205,"style":205},"Target Publish => _ => _  \n    .DependsOn(Clean, Compile)  \n    .Executes(() =>  \n    {  \n        DotNetPublish(_ => _  \n            .SetProject(Solution.CSharpEverything_Api)  \n            .SetConfiguration(Configuration)  \n            .EnableNoBuild()  \n            .SetOutput(ApiPackageDirectory));  \n  \n        ZipFile.CreateFromDirectory(ApiPackageDirectory, ArtifactsDirectory / \"api.zip\");  \n    });\n",[34137],{"type":254,"tag":389,"props":34138,"children":34139},{"__ignoreMap":205},[34140,34168,34204,34227,34238,34262,34299,34326,34346,34375,34382,34440],{"type":254,"tag":622,"props":34141,"children":34142},{"class":624,"line":625},[34143,34147,34152,34156,34160,34164],{"type":254,"tag":622,"props":34144,"children":34145},{"style":1202},[34146],{"type":259,"value":33360},{"type":254,"tag":622,"props":34148,"children":34149},{"style":714},[34150],{"type":259,"value":34151},"Publish",{"type":254,"tag":622,"props":34153,"children":34154},{"style":629},[34155],{"type":259,"value":2697},{"type":254,"tag":622,"props":34157,"children":34158},{"style":714},[34159],{"type":259,"value":8954},{"type":254,"tag":622,"props":34161,"children":34162},{"style":629},[34163],{"type":259,"value":2697},{"type":254,"tag":622,"props":34165,"children":34166},{"style":1202},[34167],{"type":259,"value":33381},{"type":254,"tag":622,"props":34169,"children":34170},{"class":624,"line":635},[34171,34175,34179,34183,34187,34191,34196,34200],{"type":254,"tag":622,"props":34172,"children":34173},{"style":629},[34174],{"type":259,"value":2678},{"type":254,"tag":622,"props":34176,"children":34177},{"style":1735},[34178],{"type":259,"value":33950},{"type":254,"tag":622,"props":34180,"children":34181},{"style":629},[34182],{"type":259,"value":1668},{"type":254,"tag":622,"props":34184,"children":34185},{"style":1202},[34186],{"type":259,"value":20737},{"type":254,"tag":622,"props":34188,"children":34189},{"style":629},[34190],{"type":259,"value":3149},{"type":254,"tag":622,"props":34192,"children":34193},{"style":1202},[34194],{"type":259,"value":34195}," Compile",{"type":254,"tag":622,"props":34197,"children":34198},{"style":629},[34199],{"type":259,"value":2309},{"type":254,"tag":622,"props":34201,"children":34202},{"style":1202},[34203],{"type":259,"value":33411},{"type":254,"tag":622,"props":34205,"children":34206},{"class":624,"line":679},[34207,34211,34215,34219,34223],{"type":254,"tag":622,"props":34208,"children":34209},{"style":629},[34210],{"type":259,"value":2678},{"type":254,"tag":622,"props":34212,"children":34213},{"style":1735},[34214],{"type":259,"value":33423},{"type":254,"tag":622,"props":34216,"children":34217},{"style":629},[34218],{"type":259,"value":33428},{"type":254,"tag":622,"props":34220,"children":34221},{"style":629},[34222],{"type":259,"value":2697},{"type":254,"tag":622,"props":34224,"children":34225},{"style":1202},[34226],{"type":259,"value":33411},{"type":254,"tag":622,"props":34228,"children":34229},{"class":624,"line":705},[34230,34234],{"type":254,"tag":622,"props":34231,"children":34232},{"style":629},[34233],{"type":259,"value":33444},{"type":254,"tag":622,"props":34235,"children":34236},{"style":1202},[34237],{"type":259,"value":33411},{"type":254,"tag":622,"props":34239,"children":34240},{"class":624,"line":732},[34241,34246,34250,34254,34258],{"type":254,"tag":622,"props":34242,"children":34243},{"style":1735},[34244],{"type":259,"value":34245},"        DotNetPublish",{"type":254,"tag":622,"props":34247,"children":34248},{"style":629},[34249],{"type":259,"value":1668},{"type":254,"tag":622,"props":34251,"children":34252},{"style":714},[34253],{"type":259,"value":8945},{"type":254,"tag":622,"props":34255,"children":34256},{"style":629},[34257],{"type":259,"value":2697},{"type":254,"tag":622,"props":34259,"children":34260},{"style":1202},[34261],{"type":259,"value":33381},{"type":254,"tag":622,"props":34263,"children":34264},{"class":624,"line":759},[34265,34269,34274,34278,34282,34286,34291,34295],{"type":254,"tag":622,"props":34266,"children":34267},{"style":629},[34268],{"type":259,"value":9102},{"type":254,"tag":622,"props":34270,"children":34271},{"style":1735},[34272],{"type":259,"value":34273},"SetProject",{"type":254,"tag":622,"props":34275,"children":34276},{"style":629},[34277],{"type":259,"value":1668},{"type":254,"tag":622,"props":34279,"children":34280},{"style":1202},[34281],{"type":259,"value":33852},{"type":254,"tag":622,"props":34283,"children":34284},{"style":629},[34285],{"type":259,"value":594},{"type":254,"tag":622,"props":34287,"children":34288},{"style":1202},[34289],{"type":259,"value":34290},"CSharpEverything_Api",{"type":254,"tag":622,"props":34292,"children":34293},{"style":629},[34294],{"type":259,"value":2309},{"type":254,"tag":622,"props":34296,"children":34297},{"style":1202},[34298],{"type":259,"value":33411},{"type":254,"tag":622,"props":34300,"children":34301},{"class":624,"line":798},[34302,34306,34310,34314,34318,34322],{"type":254,"tag":622,"props":34303,"children":34304},{"style":629},[34305],{"type":259,"value":9102},{"type":254,"tag":622,"props":34307,"children":34308},{"style":1735},[34309],{"type":259,"value":34063},{"type":254,"tag":622,"props":34311,"children":34312},{"style":629},[34313],{"type":259,"value":1668},{"type":254,"tag":622,"props":34315,"children":34316},{"style":1202},[34317],{"type":259,"value":2924},{"type":254,"tag":622,"props":34319,"children":34320},{"style":629},[34321],{"type":259,"value":2309},{"type":254,"tag":622,"props":34323,"children":34324},{"style":1202},[34325],{"type":259,"value":33411},{"type":254,"tag":622,"props":34327,"children":34328},{"class":624,"line":836},[34329,34333,34338,34342],{"type":254,"tag":622,"props":34330,"children":34331},{"style":629},[34332],{"type":259,"value":9102},{"type":254,"tag":622,"props":34334,"children":34335},{"style":1735},[34336],{"type":259,"value":34337},"EnableNoBuild",{"type":254,"tag":622,"props":34339,"children":34340},{"style":629},[34341],{"type":259,"value":2724},{"type":254,"tag":622,"props":34343,"children":34344},{"style":1202},[34345],{"type":259,"value":33411},{"type":254,"tag":622,"props":34347,"children":34348},{"class":624,"line":862},[34349,34353,34358,34362,34367,34371],{"type":254,"tag":622,"props":34350,"children":34351},{"style":629},[34352],{"type":259,"value":9102},{"type":254,"tag":622,"props":34354,"children":34355},{"style":1735},[34356],{"type":259,"value":34357},"SetOutput",{"type":254,"tag":622,"props":34359,"children":34360},{"style":629},[34361],{"type":259,"value":1668},{"type":254,"tag":622,"props":34363,"children":34364},{"style":1202},[34365],{"type":259,"value":34366},"ApiPackageDirectory",{"type":254,"tag":622,"props":34368,"children":34369},{"style":629},[34370],{"type":259,"value":33857},{"type":254,"tag":622,"props":34372,"children":34373},{"style":1202},[34374],{"type":259,"value":33411},{"type":254,"tag":622,"props":34376,"children":34377},{"class":624,"line":900},[34378],{"type":254,"tag":622,"props":34379,"children":34380},{"style":1202},[34381],{"type":259,"value":33411},{"type":254,"tag":622,"props":34383,"children":34384},{"class":624,"line":938},[34385,34390,34394,34399,34403,34407,34411,34416,34420,34424,34428,34432,34436],{"type":254,"tag":622,"props":34386,"children":34387},{"style":1202},[34388],{"type":259,"value":34389},"        ZipFile",{"type":254,"tag":622,"props":34391,"children":34392},{"style":629},[34393],{"type":259,"value":594},{"type":254,"tag":622,"props":34395,"children":34396},{"style":1735},[34397],{"type":259,"value":34398},"CreateFromDirectory",{"type":254,"tag":622,"props":34400,"children":34401},{"style":629},[34402],{"type":259,"value":1668},{"type":254,"tag":622,"props":34404,"children":34405},{"style":1202},[34406],{"type":259,"value":34366},{"type":254,"tag":622,"props":34408,"children":34409},{"style":629},[34410],{"type":259,"value":3149},{"type":254,"tag":622,"props":34412,"children":34413},{"style":1202},[34414],{"type":259,"value":34415}," ArtifactsDirectory ",{"type":254,"tag":622,"props":34417,"children":34418},{"style":629},[34419],{"type":259,"value":9768},{"type":254,"tag":622,"props":34421,"children":34422},{"style":629},[34423],{"type":259,"value":661},{"type":254,"tag":622,"props":34425,"children":34426},{"style":664},[34427],{"type":259,"value":34130},{"type":254,"tag":622,"props":34429,"children":34430},{"style":629},[34431],{"type":259,"value":652},{"type":254,"tag":622,"props":34433,"children":34434},{"style":629},[34435],{"type":259,"value":33522},{"type":254,"tag":622,"props":34437,"children":34438},{"style":1202},[34439],{"type":259,"value":33411},{"type":254,"tag":622,"props":34441,"children":34442},{"class":624,"line":976},[34443],{"type":254,"tag":622,"props":34444,"children":34445},{"style":629},[34446],{"type":259,"value":8244},{"type":254,"tag":427,"props":34448,"children":34449},{"icon":567},[34450],{"type":254,"tag":255,"props":34451,"children":34452},{},[34453,34455,34462],{"type":259,"value":34454},"If you need more compressing archives options, you can check ",{"type":254,"tag":262,"props":34456,"children":34459},{"href":34457,"rel":34458},"https://nuke.build/docs/common/compression/#compressing-archives",[266],[34460],{"type":259,"value":34461},"Nuke documentation",{"type":259,"value":34463},", they have some utilities to do more complex scenarios.",{"type":254,"tag":255,"props":34465,"children":34466},{},[34467,34469,34476,34478,34483,34485,34491],{"type":259,"value":34468},"You may have noted on the line where I set the project that I have ",{"type":254,"tag":262,"props":34470,"children":34473},{"href":34471,"rel":34472},"https://nuke.build/docs/common/solution-project-model/#strong-typed-project-access",[266],[34474],{"type":259,"value":34475},"strong-typed access to the projects in my solution",{"type":259,"value":34477},". This is possible by adding this field with the ",{"type":254,"tag":389,"props":34479,"children":34481},{"className":34480},[],[34482],{"type":259,"value":33852},{"type":259,"value":34484}," attribute and its ",{"type":254,"tag":389,"props":34486,"children":34488},{"className":34487},[],[34489],{"type":259,"value":34490},"GenerateProjects",{"type":259,"value":34492}," property set to true.",{"type":254,"tag":612,"props":34494,"children":34496},{"className":1423,"code":34495,"language":1425,"meta":205,"style":205},"[Solution(GenerateProjects = true)]  \nreadonly Solution Solution;\n",[34497],{"type":254,"tag":389,"props":34498,"children":34499},{"__ignoreMap":205},[34500,34535],{"type":254,"tag":622,"props":34501,"children":34502},{"class":624,"line":625},[34503,34507,34511,34515,34519,34523,34527,34531],{"type":254,"tag":622,"props":34504,"children":34505},{"style":629},[34506],{"type":259,"value":10993},{"type":254,"tag":622,"props":34508,"children":34509},{"style":714},[34510],{"type":259,"value":33852},{"type":254,"tag":622,"props":34512,"children":34513},{"style":629},[34514],{"type":259,"value":1668},{"type":254,"tag":622,"props":34516,"children":34517},{"style":714},[34518],{"type":259,"value":34490},{"type":254,"tag":622,"props":34520,"children":34521},{"style":629},[34522],{"type":259,"value":1619},{"type":254,"tag":622,"props":34524,"children":34525},{"style":7715},[34526],{"type":259,"value":7718},{"type":254,"tag":622,"props":34528,"children":34529},{"style":629},[34530],{"type":259,"value":9562},{"type":254,"tag":622,"props":34532,"children":34533},{"style":1202},[34534],{"type":259,"value":33411},{"type":254,"tag":622,"props":34536,"children":34537},{"class":624,"line":635},[34538,34543,34548,34552],{"type":254,"tag":622,"props":34539,"children":34540},{"style":644},[34541],{"type":259,"value":34542},"readonly",{"type":254,"tag":622,"props":34544,"children":34545},{"style":714},[34546],{"type":259,"value":34547}," Solution",{"type":254,"tag":622,"props":34549,"children":34550},{"style":714},[34551],{"type":259,"value":34547},{"type":254,"tag":622,"props":34553,"children":34554},{"style":629},[34555],{"type":259,"value":2350},{"type":254,"tag":34557,"props":34558,"children":34560},"calout",{"icon":34559},"i-fluent-emoji-magic-wand",[34561],{"type":254,"tag":255,"props":34562,"children":34563},{},[34564,34566,34573],{"type":259,"value":34565},"It looks like magic but it's not! Nuke uses a ",{"type":254,"tag":262,"props":34567,"children":34570},{"href":34568,"rel":34569},"https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview",[266],[34571],{"type":259,"value":34572},"source generator",{"type":259,"value":34574},"  to do that behind the scenes.",{"type":254,"tag":282,"props":34576,"children":34578},{"id":34577},"provisioning-the-app-service-with-pulumi",[34579],{"type":259,"value":34580},"Provisioning the App Service with Pulumi",{"type":254,"tag":455,"props":34582,"children":34584},{"id":34583},"the-pulumi-project",[34585],{"type":259,"value":34586},"The Pulumi project",{"type":254,"tag":255,"props":34588,"children":34589},{},[34590,34592,34597,34599,34605],{"type":259,"value":34591},"By default, the infrastructure code is contained in the ",{"type":254,"tag":389,"props":34593,"children":34595},{"className":34594},[],[34596],{"type":259,"value":3667},{"type":259,"value":34598}," file of our project. The resources to provision are declared in the lambda in parameter of the ",{"type":254,"tag":389,"props":34600,"children":34602},{"className":34601},[],[34603],{"type":259,"value":34604},"Deployment.RunAsync",{"type":259,"value":7544},{"type":254,"tag":255,"props":34607,"children":34608},{},[34609],{"type":254,"tag":1161,"props":34610,"children":34614},{"alt":34611,"className":34612,"src":34613},"Pulumi program file opened in the IDE.",[1165,1166],"/posts/images/pulumi_met_nuke_10.png",[],{"type":254,"tag":427,"props":34616,"children":34617},{"icon":429},[34618],{"type":254,"tag":255,"props":34619,"children":34620},{},[34621],{"type":259,"value":34622},"The project uses the top-level statement feature of C#.",{"type":254,"tag":255,"props":34624,"children":34625},{},[34626,34628,34633],{"type":259,"value":34627},"As we don't have many resources to declare for our scenario we will keep all the code in the ",{"type":254,"tag":389,"props":34629,"children":34631},{"className":34630},[],[34632],{"type":259,"value":3667},{"type":259,"value":34634}," file but that is not what you would do in a more complex project.",{"type":254,"tag":455,"props":34636,"children":34638},{"id":34637},"azure-resources-to-provision",[34639],{"type":259,"value":34640},"Azure resources to provision",{"type":254,"tag":255,"props":34642,"children":34643},{},[34644],{"type":259,"value":34645},"There are 3 Azure resources we need to create in our stack (instance of a Pulumi program):",{"type":254,"tag":332,"props":34647,"children":34648},{},[34649,34654,34666],{"type":254,"tag":336,"props":34650,"children":34651},{},[34652],{"type":259,"value":34653},"a resource group to contain the different Azure resources",{"type":254,"tag":336,"props":34655,"children":34656},{},[34657,34659],{"type":259,"value":34658},"an App Service Plan which ",{"type":254,"tag":262,"props":34660,"children":34663},{"href":34661,"rel":34662},"https://learn.microsoft.com/en-us/azure/app-service/overview-hosting-plans",[266],[34664],{"type":259,"value":34665},"defines the set of compute resources for a web app to run",{"type":254,"tag":336,"props":34667,"children":34668},{},[34669],{"type":259,"value":34670},"a Web App / App Service which is where the API will be deployed",{"type":254,"tag":612,"props":34672,"children":34674},{"className":1423,"code":34673,"language":1425,"meta":205,"style":205},"var resourceGroup = new ResourceGroup($\"rg-{Deployment.Instance.ProjectName}-{Deployment.Instance.StackName}\");  \n  \nvar appServicePlan = new AppServicePlan($\"plan-{Deployment.Instance.ProjectName}-{Deployment.Instance.StackName}\", new AppServicePlanArgs  \n{  \n    ResourceGroupName = resourceGroup.Name,  \n    Kind = \"App\",  \n    Sku = new SkuDescriptionArgs  \n    {  \n        Tier = \"Basic\",  \n        Name = \"B1\",  \n    },  \n});  \n  \nvar appService = new WebApp($\"app-{Deployment.Instance.ProjectName}-{Deployment.Instance.StackName}\", new WebAppArgs   \n{   \n    ResourceGroupName = resourceGroup.Name,  \n    ServerFarmId = appServicePlan.Id  \n});\n",[34675],{"type":254,"tag":389,"props":34676,"children":34677},{"__ignoreMap":205},[34678,34788,34795,34910,34921,34953,34986,35011,35022,35055,35088,35100,35112,35119,35235,35246,35277,35302],{"type":254,"tag":622,"props":34679,"children":34680},{"class":624,"line":625},[34681,34685,34690,34694,34698,34703,34707,34711,34716,34720,34725,34729,34734,34738,34743,34747,34751,34755,34759,34763,34767,34771,34776,34780,34784],{"type":254,"tag":622,"props":34682,"children":34683},{"style":714},[34684],{"type":259,"value":1710},{"type":254,"tag":622,"props":34686,"children":34687},{"style":714},[34688],{"type":259,"value":34689}," resourceGroup",{"type":254,"tag":622,"props":34691,"children":34692},{"style":629},[34693],{"type":259,"value":1619},{"type":254,"tag":622,"props":34695,"children":34696},{"style":629},[34697],{"type":259,"value":1624},{"type":254,"tag":622,"props":34699,"children":34700},{"style":714},[34701],{"type":259,"value":34702}," ResourceGroup",{"type":254,"tag":622,"props":34704,"children":34705},{"style":629},[34706],{"type":259,"value":1668},{"type":254,"tag":622,"props":34708,"children":34709},{"style":629},[34710],{"type":259,"value":9607},{"type":254,"tag":622,"props":34712,"children":34713},{"style":664},[34714],{"type":259,"value":34715},"rg-",{"type":254,"tag":622,"props":34717,"children":34718},{"style":629},[34719],{"type":259,"value":3234},{"type":254,"tag":622,"props":34721,"children":34722},{"style":1202},[34723],{"type":259,"value":34724},"Deployment",{"type":254,"tag":622,"props":34726,"children":34727},{"style":629},[34728],{"type":259,"value":594},{"type":254,"tag":622,"props":34730,"children":34731},{"style":1202},[34732],{"type":259,"value":34733},"Instance",{"type":254,"tag":622,"props":34735,"children":34736},{"style":629},[34737],{"type":259,"value":594},{"type":254,"tag":622,"props":34739,"children":34740},{"style":1202},[34741],{"type":259,"value":34742},"ProjectName",{"type":254,"tag":622,"props":34744,"children":34745},{"style":629},[34746],{"type":259,"value":2421},{"type":254,"tag":622,"props":34748,"children":34749},{"style":664},[34750],{"type":259,"value":17065},{"type":254,"tag":622,"props":34752,"children":34753},{"style":629},[34754],{"type":259,"value":3234},{"type":254,"tag":622,"props":34756,"children":34757},{"style":1202},[34758],{"type":259,"value":34724},{"type":254,"tag":622,"props":34760,"children":34761},{"style":629},[34762],{"type":259,"value":594},{"type":254,"tag":622,"props":34764,"children":34765},{"style":1202},[34766],{"type":259,"value":34733},{"type":254,"tag":622,"props":34768,"children":34769},{"style":629},[34770],{"type":259,"value":594},{"type":254,"tag":622,"props":34772,"children":34773},{"style":1202},[34774],{"type":259,"value":34775},"StackName",{"type":254,"tag":622,"props":34777,"children":34778},{"style":629},[34779],{"type":259,"value":2443},{"type":254,"tag":622,"props":34781,"children":34782},{"style":629},[34783],{"type":259,"value":33522},{"type":254,"tag":622,"props":34785,"children":34786},{"style":1202},[34787],{"type":259,"value":33411},{"type":254,"tag":622,"props":34789,"children":34790},{"class":624,"line":635},[34791],{"type":254,"tag":622,"props":34792,"children":34793},{"style":1202},[34794],{"type":259,"value":33411},{"type":254,"tag":622,"props":34796,"children":34797},{"class":624,"line":679},[34798,34802,34807,34811,34815,34820,34824,34828,34833,34837,34841,34845,34849,34853,34857,34861,34865,34869,34873,34877,34881,34885,34889,34893,34897,34901,34906],{"type":254,"tag":622,"props":34799,"children":34800},{"style":714},[34801],{"type":259,"value":1710},{"type":254,"tag":622,"props":34803,"children":34804},{"style":714},[34805],{"type":259,"value":34806}," appServicePlan",{"type":254,"tag":622,"props":34808,"children":34809},{"style":629},[34810],{"type":259,"value":1619},{"type":254,"tag":622,"props":34812,"children":34813},{"style":629},[34814],{"type":259,"value":1624},{"type":254,"tag":622,"props":34816,"children":34817},{"style":714},[34818],{"type":259,"value":34819}," AppServicePlan",{"type":254,"tag":622,"props":34821,"children":34822},{"style":629},[34823],{"type":259,"value":1668},{"type":254,"tag":622,"props":34825,"children":34826},{"style":629},[34827],{"type":259,"value":9607},{"type":254,"tag":622,"props":34829,"children":34830},{"style":664},[34831],{"type":259,"value":34832},"plan-",{"type":254,"tag":622,"props":34834,"children":34835},{"style":629},[34836],{"type":259,"value":3234},{"type":254,"tag":622,"props":34838,"children":34839},{"style":1202},[34840],{"type":259,"value":34724},{"type":254,"tag":622,"props":34842,"children":34843},{"style":629},[34844],{"type":259,"value":594},{"type":254,"tag":622,"props":34846,"children":34847},{"style":1202},[34848],{"type":259,"value":34733},{"type":254,"tag":622,"props":34850,"children":34851},{"style":629},[34852],{"type":259,"value":594},{"type":254,"tag":622,"props":34854,"children":34855},{"style":1202},[34856],{"type":259,"value":34742},{"type":254,"tag":622,"props":34858,"children":34859},{"style":629},[34860],{"type":259,"value":2421},{"type":254,"tag":622,"props":34862,"children":34863},{"style":664},[34864],{"type":259,"value":17065},{"type":254,"tag":622,"props":34866,"children":34867},{"style":629},[34868],{"type":259,"value":3234},{"type":254,"tag":622,"props":34870,"children":34871},{"style":1202},[34872],{"type":259,"value":34724},{"type":254,"tag":622,"props":34874,"children":34875},{"style":629},[34876],{"type":259,"value":594},{"type":254,"tag":622,"props":34878,"children":34879},{"style":1202},[34880],{"type":259,"value":34733},{"type":254,"tag":622,"props":34882,"children":34883},{"style":629},[34884],{"type":259,"value":594},{"type":254,"tag":622,"props":34886,"children":34887},{"style":1202},[34888],{"type":259,"value":34775},{"type":254,"tag":622,"props":34890,"children":34891},{"style":629},[34892],{"type":259,"value":2443},{"type":254,"tag":622,"props":34894,"children":34895},{"style":629},[34896],{"type":259,"value":3149},{"type":254,"tag":622,"props":34898,"children":34899},{"style":629},[34900],{"type":259,"value":1624},{"type":254,"tag":622,"props":34902,"children":34903},{"style":714},[34904],{"type":259,"value":34905}," AppServicePlanArgs",{"type":254,"tag":622,"props":34907,"children":34908},{"style":1202},[34909],{"type":259,"value":33411},{"type":254,"tag":622,"props":34911,"children":34912},{"class":624,"line":705},[34913,34917],{"type":254,"tag":622,"props":34914,"children":34915},{"style":629},[34916],{"type":259,"value":3234},{"type":254,"tag":622,"props":34918,"children":34919},{"style":1202},[34920],{"type":259,"value":33411},{"type":254,"tag":622,"props":34922,"children":34923},{"class":624,"line":732},[34924,34929,34933,34937,34941,34945,34949],{"type":254,"tag":622,"props":34925,"children":34926},{"style":1202},[34927],{"type":259,"value":34928},"    ResourceGroupName ",{"type":254,"tag":622,"props":34930,"children":34931},{"style":629},[34932],{"type":259,"value":1654},{"type":254,"tag":622,"props":34934,"children":34935},{"style":1202},[34936],{"type":259,"value":34689},{"type":254,"tag":622,"props":34938,"children":34939},{"style":629},[34940],{"type":259,"value":594},{"type":254,"tag":622,"props":34942,"children":34943},{"style":1202},[34944],{"type":259,"value":2254},{"type":254,"tag":622,"props":34946,"children":34947},{"style":629},[34948],{"type":259,"value":3149},{"type":254,"tag":622,"props":34950,"children":34951},{"style":1202},[34952],{"type":259,"value":33411},{"type":254,"tag":622,"props":34954,"children":34955},{"class":624,"line":759},[34956,34961,34965,34969,34974,34978,34982],{"type":254,"tag":622,"props":34957,"children":34958},{"style":1202},[34959],{"type":259,"value":34960},"    Kind ",{"type":254,"tag":622,"props":34962,"children":34963},{"style":629},[34964],{"type":259,"value":1654},{"type":254,"tag":622,"props":34966,"children":34967},{"style":629},[34968],{"type":259,"value":661},{"type":254,"tag":622,"props":34970,"children":34971},{"style":664},[34972],{"type":259,"value":34973},"App",{"type":254,"tag":622,"props":34975,"children":34976},{"style":629},[34977],{"type":259,"value":652},{"type":254,"tag":622,"props":34979,"children":34980},{"style":629},[34981],{"type":259,"value":3149},{"type":254,"tag":622,"props":34983,"children":34984},{"style":1202},[34985],{"type":259,"value":33411},{"type":254,"tag":622,"props":34987,"children":34988},{"class":624,"line":798},[34989,34994,34998,35002,35007],{"type":254,"tag":622,"props":34990,"children":34991},{"style":1202},[34992],{"type":259,"value":34993},"    Sku ",{"type":254,"tag":622,"props":34995,"children":34996},{"style":629},[34997],{"type":259,"value":1654},{"type":254,"tag":622,"props":34999,"children":35000},{"style":629},[35001],{"type":259,"value":1624},{"type":254,"tag":622,"props":35003,"children":35004},{"style":714},[35005],{"type":259,"value":35006}," SkuDescriptionArgs",{"type":254,"tag":622,"props":35008,"children":35009},{"style":1202},[35010],{"type":259,"value":33411},{"type":254,"tag":622,"props":35012,"children":35013},{"class":624,"line":836},[35014,35018],{"type":254,"tag":622,"props":35015,"children":35016},{"style":629},[35017],{"type":259,"value":33444},{"type":254,"tag":622,"props":35019,"children":35020},{"style":1202},[35021],{"type":259,"value":33411},{"type":254,"tag":622,"props":35023,"children":35024},{"class":624,"line":862},[35025,35030,35034,35038,35043,35047,35051],{"type":254,"tag":622,"props":35026,"children":35027},{"style":1202},[35028],{"type":259,"value":35029},"        Tier ",{"type":254,"tag":622,"props":35031,"children":35032},{"style":629},[35033],{"type":259,"value":1654},{"type":254,"tag":622,"props":35035,"children":35036},{"style":629},[35037],{"type":259,"value":661},{"type":254,"tag":622,"props":35039,"children":35040},{"style":664},[35041],{"type":259,"value":35042},"Basic",{"type":254,"tag":622,"props":35044,"children":35045},{"style":629},[35046],{"type":259,"value":652},{"type":254,"tag":622,"props":35048,"children":35049},{"style":629},[35050],{"type":259,"value":3149},{"type":254,"tag":622,"props":35052,"children":35053},{"style":1202},[35054],{"type":259,"value":33411},{"type":254,"tag":622,"props":35056,"children":35057},{"class":624,"line":900},[35058,35063,35067,35071,35076,35080,35084],{"type":254,"tag":622,"props":35059,"children":35060},{"style":1202},[35061],{"type":259,"value":35062},"        Name ",{"type":254,"tag":622,"props":35064,"children":35065},{"style":629},[35066],{"type":259,"value":1654},{"type":254,"tag":622,"props":35068,"children":35069},{"style":629},[35070],{"type":259,"value":661},{"type":254,"tag":622,"props":35072,"children":35073},{"style":664},[35074],{"type":259,"value":35075},"B1",{"type":254,"tag":622,"props":35077,"children":35078},{"style":629},[35079],{"type":259,"value":652},{"type":254,"tag":622,"props":35081,"children":35082},{"style":629},[35083],{"type":259,"value":3149},{"type":254,"tag":622,"props":35085,"children":35086},{"style":1202},[35087],{"type":259,"value":33411},{"type":254,"tag":622,"props":35089,"children":35090},{"class":624,"line":938},[35091,35096],{"type":254,"tag":622,"props":35092,"children":35093},{"style":629},[35094],{"type":259,"value":35095},"    },",{"type":254,"tag":622,"props":35097,"children":35098},{"style":1202},[35099],{"type":259,"value":33411},{"type":254,"tag":622,"props":35101,"children":35102},{"class":624,"line":976},[35103,35108],{"type":254,"tag":622,"props":35104,"children":35105},{"style":629},[35106],{"type":259,"value":35107},"});",{"type":254,"tag":622,"props":35109,"children":35110},{"style":1202},[35111],{"type":259,"value":33411},{"type":254,"tag":622,"props":35113,"children":35114},{"class":624,"line":1014},[35115],{"type":254,"tag":622,"props":35116,"children":35117},{"style":1202},[35118],{"type":259,"value":33411},{"type":254,"tag":622,"props":35120,"children":35121},{"class":624,"line":1052},[35122,35126,35131,35135,35139,35144,35148,35152,35157,35161,35165,35169,35173,35177,35181,35185,35189,35193,35197,35201,35205,35209,35213,35217,35221,35225,35230],{"type":254,"tag":622,"props":35123,"children":35124},{"style":714},[35125],{"type":259,"value":1710},{"type":254,"tag":622,"props":35127,"children":35128},{"style":714},[35129],{"type":259,"value":35130}," appService",{"type":254,"tag":622,"props":35132,"children":35133},{"style":629},[35134],{"type":259,"value":1619},{"type":254,"tag":622,"props":35136,"children":35137},{"style":629},[35138],{"type":259,"value":1624},{"type":254,"tag":622,"props":35140,"children":35141},{"style":714},[35142],{"type":259,"value":35143}," WebApp",{"type":254,"tag":622,"props":35145,"children":35146},{"style":629},[35147],{"type":259,"value":1668},{"type":254,"tag":622,"props":35149,"children":35150},{"style":629},[35151],{"type":259,"value":9607},{"type":254,"tag":622,"props":35153,"children":35154},{"style":664},[35155],{"type":259,"value":35156},"app-",{"type":254,"tag":622,"props":35158,"children":35159},{"style":629},[35160],{"type":259,"value":3234},{"type":254,"tag":622,"props":35162,"children":35163},{"style":1202},[35164],{"type":259,"value":34724},{"type":254,"tag":622,"props":35166,"children":35167},{"style":629},[35168],{"type":259,"value":594},{"type":254,"tag":622,"props":35170,"children":35171},{"style":1202},[35172],{"type":259,"value":34733},{"type":254,"tag":622,"props":35174,"children":35175},{"style":629},[35176],{"type":259,"value":594},{"type":254,"tag":622,"props":35178,"children":35179},{"style":1202},[35180],{"type":259,"value":34742},{"type":254,"tag":622,"props":35182,"children":35183},{"style":629},[35184],{"type":259,"value":2421},{"type":254,"tag":622,"props":35186,"children":35187},{"style":664},[35188],{"type":259,"value":17065},{"type":254,"tag":622,"props":35190,"children":35191},{"style":629},[35192],{"type":259,"value":3234},{"type":254,"tag":622,"props":35194,"children":35195},{"style":1202},[35196],{"type":259,"value":34724},{"type":254,"tag":622,"props":35198,"children":35199},{"style":629},[35200],{"type":259,"value":594},{"type":254,"tag":622,"props":35202,"children":35203},{"style":1202},[35204],{"type":259,"value":34733},{"type":254,"tag":622,"props":35206,"children":35207},{"style":629},[35208],{"type":259,"value":594},{"type":254,"tag":622,"props":35210,"children":35211},{"style":1202},[35212],{"type":259,"value":34775},{"type":254,"tag":622,"props":35214,"children":35215},{"style":629},[35216],{"type":259,"value":2443},{"type":254,"tag":622,"props":35218,"children":35219},{"style":629},[35220],{"type":259,"value":3149},{"type":254,"tag":622,"props":35222,"children":35223},{"style":629},[35224],{"type":259,"value":1624},{"type":254,"tag":622,"props":35226,"children":35227},{"style":714},[35228],{"type":259,"value":35229}," WebAppArgs",{"type":254,"tag":622,"props":35231,"children":35232},{"style":1202},[35233],{"type":259,"value":35234},"   \n",{"type":254,"tag":622,"props":35236,"children":35237},{"class":624,"line":1078},[35238,35242],{"type":254,"tag":622,"props":35239,"children":35240},{"style":629},[35241],{"type":259,"value":3234},{"type":254,"tag":622,"props":35243,"children":35244},{"style":1202},[35245],{"type":259,"value":35234},{"type":254,"tag":622,"props":35247,"children":35248},{"class":624,"line":1087},[35249,35253,35257,35261,35265,35269,35273],{"type":254,"tag":622,"props":35250,"children":35251},{"style":1202},[35252],{"type":259,"value":34928},{"type":254,"tag":622,"props":35254,"children":35255},{"style":629},[35256],{"type":259,"value":1654},{"type":254,"tag":622,"props":35258,"children":35259},{"style":1202},[35260],{"type":259,"value":34689},{"type":254,"tag":622,"props":35262,"children":35263},{"style":629},[35264],{"type":259,"value":594},{"type":254,"tag":622,"props":35266,"children":35267},{"style":1202},[35268],{"type":259,"value":2254},{"type":254,"tag":622,"props":35270,"children":35271},{"style":629},[35272],{"type":259,"value":3149},{"type":254,"tag":622,"props":35274,"children":35275},{"style":1202},[35276],{"type":259,"value":33411},{"type":254,"tag":622,"props":35278,"children":35279},{"class":624,"line":1096},[35280,35285,35289,35293,35297],{"type":254,"tag":622,"props":35281,"children":35282},{"style":1202},[35283],{"type":259,"value":35284},"    ServerFarmId ",{"type":254,"tag":622,"props":35286,"children":35287},{"style":629},[35288],{"type":259,"value":1654},{"type":254,"tag":622,"props":35290,"children":35291},{"style":1202},[35292],{"type":259,"value":34806},{"type":254,"tag":622,"props":35294,"children":35295},{"style":629},[35296],{"type":259,"value":594},{"type":254,"tag":622,"props":35298,"children":35299},{"style":1202},[35300],{"type":259,"value":35301},"Id  \n",{"type":254,"tag":622,"props":35303,"children":35304},{"class":624,"line":1105},[35305],{"type":254,"tag":622,"props":35306,"children":35307},{"style":629},[35308],{"type":259,"value":6661},{"type":254,"tag":255,"props":35310,"children":35311},{},[35312],{"type":259,"value":35313},"The code is quite simple, and because we are writing C# in our IDE, we have autocompletion and everything we need to make writing the infrastructure code easier.",{"type":254,"tag":427,"props":35315,"children":35316},{"icon":567},[35317],{"type":254,"tag":255,"props":35318,"children":35319},{},[35320],{"type":259,"value":35321},"If you are used to Azure Bicep or ARM templates, the names of the classes or properties will look familiar to you. It's because we are using Azure Native, which is a Pulumi native provider that is generated from Azure APIs.",{"type":254,"tag":455,"props":35323,"children":35325},{"id":35324},"stack-outputs",[35326],{"type":259,"value":35327},"Stack outputs",{"type":254,"tag":255,"props":35329,"children":35330},{},[35331],{"type":259,"value":35332},"Provisioning the cloud resources we need is great but we have to think about the next step which is to deploy our API on these resources. So what will we need for that?",{"type":254,"tag":255,"props":35334,"children":35335},{},[35336,35338,35344],{"type":259,"value":35337},"First, we will need to have the name of the provisioned App Service. That's easy it's the property Name of the ",{"type":254,"tag":389,"props":35339,"children":35341},{"className":35340},[],[35342],{"type":259,"value":35343},"appService",{"type":259,"value":35345}," variable.",{"type":254,"tag":255,"props":35347,"children":35348},{},[35349,35351,35358],{"type":259,"value":35350},"Second, because we are going to use the Kudu API to zip deploy our application to the App Service, we will need the ",{"type":254,"tag":262,"props":35352,"children":35355},{"href":35353,"rel":35354},"https://github.com/projectkudu/kudu/wiki/Deployment-credentials#site-credentials-aka-publish-profile-credentials",[266],[35356],{"type":259,"value":35357},"site credentials (aka the Publishing Profile Credentials)",{"type":259,"value":35359},". These can be retrieved in the Pulumi program using the following code:",{"type":254,"tag":612,"props":35361,"children":35363},{"className":1423,"code":35362,"language":1425,"meta":205,"style":205},"var publishingCredentials = ListWebAppPublishingCredentials.Invoke(new()  \n{  \n    ResourceGroupName = resourceGroup.Name,  \n    Name = appService.Name  \n});\n",[35364],{"type":254,"tag":389,"props":35365,"children":35366},{"__ignoreMap":205},[35367,35405,35416,35447,35471],{"type":254,"tag":622,"props":35368,"children":35369},{"class":624,"line":625},[35370,35374,35379,35383,35388,35392,35396,35401],{"type":254,"tag":622,"props":35371,"children":35372},{"style":714},[35373],{"type":259,"value":1710},{"type":254,"tag":622,"props":35375,"children":35376},{"style":714},[35377],{"type":259,"value":35378}," publishingCredentials",{"type":254,"tag":622,"props":35380,"children":35381},{"style":629},[35382],{"type":259,"value":1619},{"type":254,"tag":622,"props":35384,"children":35385},{"style":1202},[35386],{"type":259,"value":35387}," ListWebAppPublishingCredentials",{"type":254,"tag":622,"props":35389,"children":35390},{"style":629},[35391],{"type":259,"value":594},{"type":254,"tag":622,"props":35393,"children":35394},{"style":1735},[35395],{"type":259,"value":20516},{"type":254,"tag":622,"props":35397,"children":35398},{"style":629},[35399],{"type":259,"value":35400},"(new()",{"type":254,"tag":622,"props":35402,"children":35403},{"style":1202},[35404],{"type":259,"value":33411},{"type":254,"tag":622,"props":35406,"children":35407},{"class":624,"line":635},[35408,35412],{"type":254,"tag":622,"props":35409,"children":35410},{"style":629},[35411],{"type":259,"value":3234},{"type":254,"tag":622,"props":35413,"children":35414},{"style":1202},[35415],{"type":259,"value":33411},{"type":254,"tag":622,"props":35417,"children":35418},{"class":624,"line":679},[35419,35423,35427,35431,35435,35439,35443],{"type":254,"tag":622,"props":35420,"children":35421},{"style":1202},[35422],{"type":259,"value":34928},{"type":254,"tag":622,"props":35424,"children":35425},{"style":629},[35426],{"type":259,"value":1654},{"type":254,"tag":622,"props":35428,"children":35429},{"style":1202},[35430],{"type":259,"value":34689},{"type":254,"tag":622,"props":35432,"children":35433},{"style":629},[35434],{"type":259,"value":594},{"type":254,"tag":622,"props":35436,"children":35437},{"style":1202},[35438],{"type":259,"value":2254},{"type":254,"tag":622,"props":35440,"children":35441},{"style":629},[35442],{"type":259,"value":3149},{"type":254,"tag":622,"props":35444,"children":35445},{"style":1202},[35446],{"type":259,"value":33411},{"type":254,"tag":622,"props":35448,"children":35449},{"class":624,"line":705},[35450,35454,35458,35462,35466],{"type":254,"tag":622,"props":35451,"children":35452},{"style":1202},[35453],{"type":259,"value":20565},{"type":254,"tag":622,"props":35455,"children":35456},{"style":629},[35457],{"type":259,"value":1654},{"type":254,"tag":622,"props":35459,"children":35460},{"style":1202},[35461],{"type":259,"value":35130},{"type":254,"tag":622,"props":35463,"children":35464},{"style":629},[35465],{"type":259,"value":594},{"type":254,"tag":622,"props":35467,"children":35468},{"style":1202},[35469],{"type":259,"value":35470},"Name  \n",{"type":254,"tag":622,"props":35472,"children":35473},{"class":624,"line":732},[35474],{"type":254,"tag":622,"props":35475,"children":35476},{"style":629},[35477],{"type":259,"value":6661},{"type":254,"tag":427,"props":35479,"children":35480},{"icon":429},[35481],{"type":254,"tag":255,"props":35482,"children":35483},{},[35484,35486,35493],{"type":259,"value":35485},"Using the Kudu API is just one of the ",{"type":254,"tag":262,"props":35487,"children":35490},{"href":35488,"rel":35489},"https://learn.microsoft.com/en-us/azure/app-service/deploy-zip?tabs=cli#deploy-a-zip-package",[266],[35491],{"type":259,"value":35492},"many ways",{"type":259,"value":35494}," to deploy a zip package to an App Service. I could have chosen another way like using the Azure CLI, in that case retrieving the site credentials would not have been needed.",{"type":254,"tag":255,"props":35496,"children":35497},{},[35498,35500,35506],{"type":259,"value":35499},"Pulumi, like Terraform, has this concept of stack ",{"type":254,"tag":262,"props":35501,"children":35504},{"href":35502,"rel":35503},"https://www.pulumi.com/docs/intro/concepts/stack/#outputs",[266],[35505],{"type":259,"value":24715},{"type":259,"value":35507},"  where outputs are information about your stack/infrastructure that you want to expose. That is exactly what we need to export our App Service name and our site credentials so that they can be retrieved later by the Nuke code that will take care of the application deployment. To export these values we can return them in a Dictionary like that:",{"type":254,"tag":612,"props":35509,"children":35511},{"className":1423,"code":35510,"language":1425,"meta":205,"style":205},"return new Dictionary\u003Cstring, object?>  \n{  \n    [\"publishingUsername\"] = Output.CreateSecret(publishingCredentials.Apply(c => c.PublishingUserName)),  \n    [\"publishingUserPassword\"] = Output.CreateSecret(publishingCredentials.Apply(c => c.PublishingPassword)),  \n    [\"appServiceName\"] = appService.Name  \n};\n",[35512],{"type":254,"tag":389,"props":35513,"children":35514},{"__ignoreMap":205},[35515,35551,35562,35654,35743,35783],{"type":254,"tag":622,"props":35516,"children":35517},{"class":624,"line":625},[35518,35522,35526,35530,35534,35538,35542,35547],{"type":254,"tag":622,"props":35519,"children":35520},{"style":1196},[35521],{"type":259,"value":23474},{"type":254,"tag":622,"props":35523,"children":35524},{"style":629},[35525],{"type":259,"value":1624},{"type":254,"tag":622,"props":35527,"children":35528},{"style":714},[35529],{"type":259,"value":23483},{"type":254,"tag":622,"props":35531,"children":35532},{"style":629},[35533],{"type":259,"value":1743},{"type":254,"tag":622,"props":35535,"children":35536},{"style":629},[35537],{"type":259,"value":2851},{"type":254,"tag":622,"props":35539,"children":35540},{"style":629},[35541],{"type":259,"value":3149},{"type":254,"tag":622,"props":35543,"children":35544},{"style":629},[35545],{"type":259,"value":35546}," object?>",{"type":254,"tag":622,"props":35548,"children":35549},{"style":1202},[35550],{"type":259,"value":33411},{"type":254,"tag":622,"props":35552,"children":35553},{"class":624,"line":635},[35554,35558],{"type":254,"tag":622,"props":35555,"children":35556},{"style":629},[35557],{"type":259,"value":3234},{"type":254,"tag":622,"props":35559,"children":35560},{"style":1202},[35561],{"type":259,"value":33411},{"type":254,"tag":622,"props":35563,"children":35564},{"class":624,"line":679},[35565,35569,35573,35578,35582,35586,35590,35594,35598,35603,35607,35612,35616,35620,35624,35628,35632,35636,35640,35645,35650],{"type":254,"tag":622,"props":35566,"children":35567},{"style":629},[35568],{"type":259,"value":9494},{"type":254,"tag":622,"props":35570,"children":35571},{"style":629},[35572],{"type":259,"value":652},{"type":254,"tag":622,"props":35574,"children":35575},{"style":664},[35576],{"type":259,"value":35577},"publishingUsername",{"type":254,"tag":622,"props":35579,"children":35580},{"style":629},[35581],{"type":259,"value":652},{"type":254,"tag":622,"props":35583,"children":35584},{"style":629},[35585],{"type":259,"value":20251},{"type":254,"tag":622,"props":35587,"children":35588},{"style":629},[35589],{"type":259,"value":1619},{"type":254,"tag":622,"props":35591,"children":35592},{"style":1202},[35593],{"type":259,"value":22079},{"type":254,"tag":622,"props":35595,"children":35596},{"style":629},[35597],{"type":259,"value":594},{"type":254,"tag":622,"props":35599,"children":35600},{"style":1735},[35601],{"type":259,"value":35602},"CreateSecret",{"type":254,"tag":622,"props":35604,"children":35605},{"style":629},[35606],{"type":259,"value":1668},{"type":254,"tag":622,"props":35608,"children":35609},{"style":1202},[35610],{"type":259,"value":35611},"publishingCredentials",{"type":254,"tag":622,"props":35613,"children":35614},{"style":629},[35615],{"type":259,"value":594},{"type":254,"tag":622,"props":35617,"children":35618},{"style":1735},[35619],{"type":259,"value":21286},{"type":254,"tag":622,"props":35621,"children":35622},{"style":629},[35623],{"type":259,"value":1668},{"type":254,"tag":622,"props":35625,"children":35626},{"style":714},[35627],{"type":259,"value":21295},{"type":254,"tag":622,"props":35629,"children":35630},{"style":629},[35631],{"type":259,"value":2697},{"type":254,"tag":622,"props":35633,"children":35634},{"style":1202},[35635],{"type":259,"value":21304},{"type":254,"tag":622,"props":35637,"children":35638},{"style":629},[35639],{"type":259,"value":594},{"type":254,"tag":622,"props":35641,"children":35642},{"style":1202},[35643],{"type":259,"value":35644},"PublishingUserName",{"type":254,"tag":622,"props":35646,"children":35647},{"style":629},[35648],{"type":259,"value":35649},")),",{"type":254,"tag":622,"props":35651,"children":35652},{"style":1202},[35653],{"type":259,"value":33411},{"type":254,"tag":622,"props":35655,"children":35656},{"class":624,"line":705},[35657,35661,35665,35670,35674,35678,35682,35686,35690,35694,35698,35702,35706,35710,35714,35718,35722,35726,35730,35735,35739],{"type":254,"tag":622,"props":35658,"children":35659},{"style":629},[35660],{"type":259,"value":9494},{"type":254,"tag":622,"props":35662,"children":35663},{"style":629},[35664],{"type":259,"value":652},{"type":254,"tag":622,"props":35666,"children":35667},{"style":664},[35668],{"type":259,"value":35669},"publishingUserPassword",{"type":254,"tag":622,"props":35671,"children":35672},{"style":629},[35673],{"type":259,"value":652},{"type":254,"tag":622,"props":35675,"children":35676},{"style":629},[35677],{"type":259,"value":20251},{"type":254,"tag":622,"props":35679,"children":35680},{"style":629},[35681],{"type":259,"value":1619},{"type":254,"tag":622,"props":35683,"children":35684},{"style":1202},[35685],{"type":259,"value":22079},{"type":254,"tag":622,"props":35687,"children":35688},{"style":629},[35689],{"type":259,"value":594},{"type":254,"tag":622,"props":35691,"children":35692},{"style":1735},[35693],{"type":259,"value":35602},{"type":254,"tag":622,"props":35695,"children":35696},{"style":629},[35697],{"type":259,"value":1668},{"type":254,"tag":622,"props":35699,"children":35700},{"style":1202},[35701],{"type":259,"value":35611},{"type":254,"tag":622,"props":35703,"children":35704},{"style":629},[35705],{"type":259,"value":594},{"type":254,"tag":622,"props":35707,"children":35708},{"style":1735},[35709],{"type":259,"value":21286},{"type":254,"tag":622,"props":35711,"children":35712},{"style":629},[35713],{"type":259,"value":1668},{"type":254,"tag":622,"props":35715,"children":35716},{"style":714},[35717],{"type":259,"value":21295},{"type":254,"tag":622,"props":35719,"children":35720},{"style":629},[35721],{"type":259,"value":2697},{"type":254,"tag":622,"props":35723,"children":35724},{"style":1202},[35725],{"type":259,"value":21304},{"type":254,"tag":622,"props":35727,"children":35728},{"style":629},[35729],{"type":259,"value":594},{"type":254,"tag":622,"props":35731,"children":35732},{"style":1202},[35733],{"type":259,"value":35734},"PublishingPassword",{"type":254,"tag":622,"props":35736,"children":35737},{"style":629},[35738],{"type":259,"value":35649},{"type":254,"tag":622,"props":35740,"children":35741},{"style":1202},[35742],{"type":259,"value":33411},{"type":254,"tag":622,"props":35744,"children":35745},{"class":624,"line":732},[35746,35750,35754,35759,35763,35767,35771,35775,35779],{"type":254,"tag":622,"props":35747,"children":35748},{"style":629},[35749],{"type":259,"value":9494},{"type":254,"tag":622,"props":35751,"children":35752},{"style":629},[35753],{"type":259,"value":652},{"type":254,"tag":622,"props":35755,"children":35756},{"style":664},[35757],{"type":259,"value":35758},"appServiceName",{"type":254,"tag":622,"props":35760,"children":35761},{"style":629},[35762],{"type":259,"value":652},{"type":254,"tag":622,"props":35764,"children":35765},{"style":629},[35766],{"type":259,"value":20251},{"type":254,"tag":622,"props":35768,"children":35769},{"style":629},[35770],{"type":259,"value":1619},{"type":254,"tag":622,"props":35772,"children":35773},{"style":1202},[35774],{"type":259,"value":35130},{"type":254,"tag":622,"props":35776,"children":35777},{"style":629},[35778],{"type":259,"value":594},{"type":254,"tag":622,"props":35780,"children":35781},{"style":1202},[35782],{"type":259,"value":35470},{"type":254,"tag":622,"props":35784,"children":35785},{"class":624,"line":759},[35786],{"type":254,"tag":622,"props":35787,"children":35788},{"style":629},[35789],{"type":259,"value":1693},{"type":254,"tag":255,"props":35791,"children":35792},{},[35793,35795,35801],{"type":259,"value":35794},"You might notice that we use the ",{"type":254,"tag":389,"props":35796,"children":35798},{"className":35797},[],[35799],{"type":259,"value":35800},"Output.CreateSecret",{"type":259,"value":35802}," method to create outputs for our publishing credentials. The aim is to tell Pulumi to treat these values as secrets, and that's what it will do by encrypting them in the state file for extra protection (that is not something Terraform does by the way).",{"type":254,"tag":455,"props":35804,"children":35806},{"id":35805},"implementing-the-provision-infrastructure-step",[35807],{"type":259,"value":35808},"Implementing the Provision Infrastructure step",{"type":254,"tag":255,"props":35810,"children":35811},{},[35812,35814,35819],{"type":259,"value":35813},"To deploy the infrastructure, we can use the ",{"type":254,"tag":389,"props":35815,"children":35817},{"className":35816},[],[35818],{"type":259,"value":23629},{"type":259,"value":35820}," command. We will write the code in a fluent way as we did with the dotnet CLI:",{"type":254,"tag":612,"props":35822,"children":35824},{"className":1423,"code":35823,"language":1425,"meta":205,"style":205},"AbsolutePath InfrastructureDirectory => RootDirectory / \"infra\";\n\nTarget ProvisionInfra => _ => _  \n    .Description(\"Provision the infrastructure on Azure\")  \n    .Executes(() =>  \n    {  \n        PulumiTasks.PulumiUp(_ => _  \n            .SetCwd(InfrastructureDirectory)  \n            .SetStack(\"dev\")  \n            .EnableSkipPreview());  \n    });\n",[35825],{"type":254,"tag":389,"props":35826,"children":35827},{"__ignoreMap":205},[35828,35867,35874,35902,35939,35962,35973,36006,36034,36070,36090],{"type":254,"tag":622,"props":35829,"children":35830},{"class":624,"line":625},[35831,35835,35839,35843,35847,35851,35855,35859,35863],{"type":254,"tag":622,"props":35832,"children":35833},{"style":1202},[35834],{"type":259,"value":33578},{"type":254,"tag":622,"props":35836,"children":35837},{"style":714},[35838],{"type":259,"value":33636},{"type":254,"tag":622,"props":35840,"children":35841},{"style":629},[35842],{"type":259,"value":2697},{"type":254,"tag":622,"props":35844,"children":35845},{"style":1202},[35846],{"type":259,"value":33592},{"type":254,"tag":622,"props":35848,"children":35849},{"style":629},[35850],{"type":259,"value":9768},{"type":254,"tag":622,"props":35852,"children":35853},{"style":629},[35854],{"type":259,"value":661},{"type":254,"tag":622,"props":35856,"children":35857},{"style":664},[35858],{"type":259,"value":16314},{"type":254,"tag":622,"props":35860,"children":35861},{"style":629},[35862],{"type":259,"value":652},{"type":254,"tag":622,"props":35864,"children":35865},{"style":629},[35866],{"type":259,"value":2350},{"type":254,"tag":622,"props":35868,"children":35869},{"class":624,"line":635},[35870],{"type":254,"tag":622,"props":35871,"children":35872},{"emptyLinePlaceholder":1699},[35873],{"type":259,"value":1702},{"type":254,"tag":622,"props":35875,"children":35876},{"class":624,"line":679},[35877,35881,35886,35890,35894,35898],{"type":254,"tag":622,"props":35878,"children":35879},{"style":1202},[35880],{"type":259,"value":33360},{"type":254,"tag":622,"props":35882,"children":35883},{"style":714},[35884],{"type":259,"value":35885},"ProvisionInfra",{"type":254,"tag":622,"props":35887,"children":35888},{"style":629},[35889],{"type":259,"value":2697},{"type":254,"tag":622,"props":35891,"children":35892},{"style":714},[35893],{"type":259,"value":8954},{"type":254,"tag":622,"props":35895,"children":35896},{"style":629},[35897],{"type":259,"value":2697},{"type":254,"tag":622,"props":35899,"children":35900},{"style":1202},[35901],{"type":259,"value":33381},{"type":254,"tag":622,"props":35903,"children":35904},{"class":624,"line":705},[35905,35909,35914,35918,35922,35927,35931,35935],{"type":254,"tag":622,"props":35906,"children":35907},{"style":629},[35908],{"type":259,"value":2678},{"type":254,"tag":622,"props":35910,"children":35911},{"style":1735},[35912],{"type":259,"value":35913},"Description",{"type":254,"tag":622,"props":35915,"children":35916},{"style":629},[35917],{"type":259,"value":1668},{"type":254,"tag":622,"props":35919,"children":35920},{"style":629},[35921],{"type":259,"value":652},{"type":254,"tag":622,"props":35923,"children":35924},{"style":664},[35925],{"type":259,"value":35926},"Provision the infrastructure on Azure",{"type":254,"tag":622,"props":35928,"children":35929},{"style":629},[35930],{"type":259,"value":652},{"type":254,"tag":622,"props":35932,"children":35933},{"style":629},[35934],{"type":259,"value":2309},{"type":254,"tag":622,"props":35936,"children":35937},{"style":1202},[35938],{"type":259,"value":33411},{"type":254,"tag":622,"props":35940,"children":35941},{"class":624,"line":732},[35942,35946,35950,35954,35958],{"type":254,"tag":622,"props":35943,"children":35944},{"style":629},[35945],{"type":259,"value":2678},{"type":254,"tag":622,"props":35947,"children":35948},{"style":1735},[35949],{"type":259,"value":33423},{"type":254,"tag":622,"props":35951,"children":35952},{"style":629},[35953],{"type":259,"value":33428},{"type":254,"tag":622,"props":35955,"children":35956},{"style":629},[35957],{"type":259,"value":2697},{"type":254,"tag":622,"props":35959,"children":35960},{"style":1202},[35961],{"type":259,"value":33411},{"type":254,"tag":622,"props":35963,"children":35964},{"class":624,"line":759},[35965,35969],{"type":254,"tag":622,"props":35966,"children":35967},{"style":629},[35968],{"type":259,"value":33444},{"type":254,"tag":622,"props":35970,"children":35971},{"style":1202},[35972],{"type":259,"value":33411},{"type":254,"tag":622,"props":35974,"children":35975},{"class":624,"line":798},[35976,35981,35985,35990,35994,35998,36002],{"type":254,"tag":622,"props":35977,"children":35978},{"style":1202},[35979],{"type":259,"value":35980},"        PulumiTasks",{"type":254,"tag":622,"props":35982,"children":35983},{"style":629},[35984],{"type":259,"value":594},{"type":254,"tag":622,"props":35986,"children":35987},{"style":1735},[35988],{"type":259,"value":35989},"PulumiUp",{"type":254,"tag":622,"props":35991,"children":35992},{"style":629},[35993],{"type":259,"value":1668},{"type":254,"tag":622,"props":35995,"children":35996},{"style":714},[35997],{"type":259,"value":8945},{"type":254,"tag":622,"props":35999,"children":36000},{"style":629},[36001],{"type":259,"value":2697},{"type":254,"tag":622,"props":36003,"children":36004},{"style":1202},[36005],{"type":259,"value":33381},{"type":254,"tag":622,"props":36007,"children":36008},{"class":624,"line":836},[36009,36013,36018,36022,36026,36030],{"type":254,"tag":622,"props":36010,"children":36011},{"style":629},[36012],{"type":259,"value":9102},{"type":254,"tag":622,"props":36014,"children":36015},{"style":1735},[36016],{"type":259,"value":36017},"SetCwd",{"type":254,"tag":622,"props":36019,"children":36020},{"style":629},[36021],{"type":259,"value":1668},{"type":254,"tag":622,"props":36023,"children":36024},{"style":1202},[36025],{"type":259,"value":33636},{"type":254,"tag":622,"props":36027,"children":36028},{"style":629},[36029],{"type":259,"value":2309},{"type":254,"tag":622,"props":36031,"children":36032},{"style":1202},[36033],{"type":259,"value":33411},{"type":254,"tag":622,"props":36035,"children":36036},{"class":624,"line":862},[36037,36041,36046,36050,36054,36058,36062,36066],{"type":254,"tag":622,"props":36038,"children":36039},{"style":629},[36040],{"type":259,"value":9102},{"type":254,"tag":622,"props":36042,"children":36043},{"style":1735},[36044],{"type":259,"value":36045},"SetStack",{"type":254,"tag":622,"props":36047,"children":36048},{"style":629},[36049],{"type":259,"value":1668},{"type":254,"tag":622,"props":36051,"children":36052},{"style":629},[36053],{"type":259,"value":652},{"type":254,"tag":622,"props":36055,"children":36056},{"style":664},[36057],{"type":259,"value":19878},{"type":254,"tag":622,"props":36059,"children":36060},{"style":629},[36061],{"type":259,"value":652},{"type":254,"tag":622,"props":36063,"children":36064},{"style":629},[36065],{"type":259,"value":2309},{"type":254,"tag":622,"props":36067,"children":36068},{"style":1202},[36069],{"type":259,"value":33411},{"type":254,"tag":622,"props":36071,"children":36072},{"class":624,"line":900},[36073,36077,36082,36086],{"type":254,"tag":622,"props":36074,"children":36075},{"style":629},[36076],{"type":259,"value":9102},{"type":254,"tag":622,"props":36078,"children":36079},{"style":1735},[36080],{"type":259,"value":36081},"EnableSkipPreview",{"type":254,"tag":622,"props":36083,"children":36084},{"style":629},[36085],{"type":259,"value":34095},{"type":254,"tag":622,"props":36087,"children":36088},{"style":1202},[36089],{"type":259,"value":33411},{"type":254,"tag":622,"props":36091,"children":36092},{"class":624,"line":938},[36093],{"type":254,"tag":622,"props":36094,"children":36095},{"style":629},[36096],{"type":259,"value":8244},{"type":254,"tag":282,"props":36098,"children":36100},{"id":36099},"deploying-the-aspnet-core-api-to-azure-app-service",[36101],{"type":259,"value":36102},"Deploying the ASP.NET Core API to Azure App Service",{"type":254,"tag":255,"props":36104,"children":36105},{},[36106,36108,36114],{"type":259,"value":36107},"I previously said we were going to use the Kudu API to deploy our application. You can check the ",{"type":254,"tag":262,"props":36109,"children":36112},{"href":36110,"rel":36111},"https://github.com/projectkudu/kudu/wiki/Deploying-from-a-zip-file-or-url",[266],[36113],{"type":259,"value":6340},{"type":259,"value":36115}," about that but concretely we will do a POST request to the zipdeploy endpoint using Basic authentication.",{"type":254,"tag":255,"props":36117,"children":36118},{},[36119,36121,36127],{"type":259,"value":36120},"To retrieve a stack output, we can use the ",{"type":254,"tag":389,"props":36122,"children":36124},{"className":36123},[],[36125],{"type":259,"value":36126},"pulumi stack output",{"type":259,"value":36128}," command. To avoid duplicating the code I wrote a short method:",{"type":254,"tag":612,"props":36130,"children":36132},{"className":1423,"code":36131,"language":1425,"meta":205,"style":205},"string GetPulumiOutput(string outputName)  \n{  \n    return PulumiTasks.PulumiStackOutput(_ => _  \n            .SetCwd(InfrastructureDirectory)  \n            .SetPropertyName(outputName)  \n            .EnableShowSecrets()\n            .DisableProcessLogOutput())  \n        .StdToText();  \n}\n",[36133],{"type":254,"tag":389,"props":36134,"children":36135},{"__ignoreMap":205},[36136,36169,36180,36217,36244,36273,36289,36309,36330],{"type":254,"tag":622,"props":36137,"children":36138},{"class":624,"line":625},[36139,36143,36148,36152,36156,36161,36165],{"type":254,"tag":622,"props":36140,"children":36141},{"style":629},[36142],{"type":259,"value":2851},{"type":254,"tag":622,"props":36144,"children":36145},{"style":1735},[36146],{"type":259,"value":36147}," GetPulumiOutput",{"type":254,"tag":622,"props":36149,"children":36150},{"style":629},[36151],{"type":259,"value":1668},{"type":254,"tag":622,"props":36153,"children":36154},{"style":629},[36155],{"type":259,"value":2851},{"type":254,"tag":622,"props":36157,"children":36158},{"style":714},[36159],{"type":259,"value":36160}," outputName",{"type":254,"tag":622,"props":36162,"children":36163},{"style":629},[36164],{"type":259,"value":2309},{"type":254,"tag":622,"props":36166,"children":36167},{"style":1202},[36168],{"type":259,"value":33411},{"type":254,"tag":622,"props":36170,"children":36171},{"class":624,"line":635},[36172,36176],{"type":254,"tag":622,"props":36173,"children":36174},{"style":629},[36175],{"type":259,"value":3234},{"type":254,"tag":622,"props":36177,"children":36178},{"style":1202},[36179],{"type":259,"value":33411},{"type":254,"tag":622,"props":36181,"children":36182},{"class":624,"line":679},[36183,36187,36192,36196,36201,36205,36209,36213],{"type":254,"tag":622,"props":36184,"children":36185},{"style":1196},[36186],{"type":259,"value":2358},{"type":254,"tag":622,"props":36188,"children":36189},{"style":1202},[36190],{"type":259,"value":36191}," PulumiTasks",{"type":254,"tag":622,"props":36193,"children":36194},{"style":629},[36195],{"type":259,"value":594},{"type":254,"tag":622,"props":36197,"children":36198},{"style":1735},[36199],{"type":259,"value":36200},"PulumiStackOutput",{"type":254,"tag":622,"props":36202,"children":36203},{"style":629},[36204],{"type":259,"value":1668},{"type":254,"tag":622,"props":36206,"children":36207},{"style":714},[36208],{"type":259,"value":8945},{"type":254,"tag":622,"props":36210,"children":36211},{"style":629},[36212],{"type":259,"value":2697},{"type":254,"tag":622,"props":36214,"children":36215},{"style":1202},[36216],{"type":259,"value":33381},{"type":254,"tag":622,"props":36218,"children":36219},{"class":624,"line":705},[36220,36224,36228,36232,36236,36240],{"type":254,"tag":622,"props":36221,"children":36222},{"style":629},[36223],{"type":259,"value":9102},{"type":254,"tag":622,"props":36225,"children":36226},{"style":1735},[36227],{"type":259,"value":36017},{"type":254,"tag":622,"props":36229,"children":36230},{"style":629},[36231],{"type":259,"value":1668},{"type":254,"tag":622,"props":36233,"children":36234},{"style":1202},[36235],{"type":259,"value":33636},{"type":254,"tag":622,"props":36237,"children":36238},{"style":629},[36239],{"type":259,"value":2309},{"type":254,"tag":622,"props":36241,"children":36242},{"style":1202},[36243],{"type":259,"value":33411},{"type":254,"tag":622,"props":36245,"children":36246},{"class":624,"line":732},[36247,36251,36256,36260,36265,36269],{"type":254,"tag":622,"props":36248,"children":36249},{"style":629},[36250],{"type":259,"value":9102},{"type":254,"tag":622,"props":36252,"children":36253},{"style":1735},[36254],{"type":259,"value":36255},"SetPropertyName",{"type":254,"tag":622,"props":36257,"children":36258},{"style":629},[36259],{"type":259,"value":1668},{"type":254,"tag":622,"props":36261,"children":36262},{"style":1202},[36263],{"type":259,"value":36264},"outputName",{"type":254,"tag":622,"props":36266,"children":36267},{"style":629},[36268],{"type":259,"value":2309},{"type":254,"tag":622,"props":36270,"children":36271},{"style":1202},[36272],{"type":259,"value":33411},{"type":254,"tag":622,"props":36274,"children":36275},{"class":624,"line":759},[36276,36280,36285],{"type":254,"tag":622,"props":36277,"children":36278},{"style":629},[36279],{"type":259,"value":9102},{"type":254,"tag":622,"props":36281,"children":36282},{"style":1735},[36283],{"type":259,"value":36284},"EnableShowSecrets",{"type":254,"tag":622,"props":36286,"children":36287},{"style":629},[36288],{"type":259,"value":1634},{"type":254,"tag":622,"props":36290,"children":36291},{"class":624,"line":798},[36292,36296,36301,36305],{"type":254,"tag":622,"props":36293,"children":36294},{"style":629},[36295],{"type":259,"value":9102},{"type":254,"tag":622,"props":36297,"children":36298},{"style":1735},[36299],{"type":259,"value":36300},"DisableProcessLogOutput",{"type":254,"tag":622,"props":36302,"children":36303},{"style":629},[36304],{"type":259,"value":25868},{"type":254,"tag":622,"props":36306,"children":36307},{"style":1202},[36308],{"type":259,"value":33411},{"type":254,"tag":622,"props":36310,"children":36311},{"class":624,"line":836},[36312,36316,36321,36326],{"type":254,"tag":622,"props":36313,"children":36314},{"style":629},[36315],{"type":259,"value":7310},{"type":254,"tag":622,"props":36317,"children":36318},{"style":1735},[36319],{"type":259,"value":36320},"StdToText",{"type":254,"tag":622,"props":36322,"children":36323},{"style":629},[36324],{"type":259,"value":36325},"();",{"type":254,"tag":622,"props":36327,"children":36328},{"style":1202},[36329],{"type":259,"value":33411},{"type":254,"tag":622,"props":36331,"children":36332},{"class":624,"line":862},[36333],{"type":254,"tag":622,"props":36334,"children":36335},{"style":629},[36336],{"type":259,"value":1111},{"type":254,"tag":255,"props":36338,"children":36339},{},[36340],{"type":259,"value":36341},"The step itself is not very complicated, just standard C# code using an HttpClient to send a POST request (with our application package as the content) to the Kudu API.",{"type":254,"tag":612,"props":36343,"children":36345},{"className":1423,"code":36344,"language":1425,"meta":205,"style":205},"Target Deploy => _ => _  \n    .DependsOn(Publish)  \n    .After(ProvisionInfra)  \n    .Executes(async () =>  \n    {  \n        var publishingUsername = GetPulumiOutput(\"publishingUsername\");  \n        var publishingUserPassword = GetPulumiOutput(\"publishingUserPassword\");  \n        var base64Auth = Convert.ToBase64String(Encoding.Default.GetBytes($\"{publishingUsername}:{publishingUserPassword}\"));  \n  \n        await using var package = File.OpenRead(ArtifactsDirectory / \"api.zip\");  \n        using var httpClient = new HttpClient();  \n        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\"Basic\", base64Auth);  \n        await httpClient.PostAsync($\"https://{GetPulumiOutput(\"appServiceName\")}.scm.azurewebsites.net/api/zipdeploy\",  \n            new StreamContent(package));  \n    });\n",[36346],{"type":254,"tag":389,"props":36347,"children":36348},{"__ignoreMap":205},[36349,36377,36404,36432,36463,36474,36518,36562,36657,36664,36735,36771,36842,36915,36945],{"type":254,"tag":622,"props":36350,"children":36351},{"class":624,"line":625},[36352,36356,36361,36365,36369,36373],{"type":254,"tag":622,"props":36353,"children":36354},{"style":1202},[36355],{"type":259,"value":33360},{"type":254,"tag":622,"props":36357,"children":36358},{"style":714},[36359],{"type":259,"value":36360},"Deploy",{"type":254,"tag":622,"props":36362,"children":36363},{"style":629},[36364],{"type":259,"value":2697},{"type":254,"tag":622,"props":36366,"children":36367},{"style":714},[36368],{"type":259,"value":8954},{"type":254,"tag":622,"props":36370,"children":36371},{"style":629},[36372],{"type":259,"value":2697},{"type":254,"tag":622,"props":36374,"children":36375},{"style":1202},[36376],{"type":259,"value":33381},{"type":254,"tag":622,"props":36378,"children":36379},{"class":624,"line":635},[36380,36384,36388,36392,36396,36400],{"type":254,"tag":622,"props":36381,"children":36382},{"style":629},[36383],{"type":259,"value":2678},{"type":254,"tag":622,"props":36385,"children":36386},{"style":1735},[36387],{"type":259,"value":33950},{"type":254,"tag":622,"props":36389,"children":36390},{"style":629},[36391],{"type":259,"value":1668},{"type":254,"tag":622,"props":36393,"children":36394},{"style":1202},[36395],{"type":259,"value":34151},{"type":254,"tag":622,"props":36397,"children":36398},{"style":629},[36399],{"type":259,"value":2309},{"type":254,"tag":622,"props":36401,"children":36402},{"style":1202},[36403],{"type":259,"value":33411},{"type":254,"tag":622,"props":36405,"children":36406},{"class":624,"line":679},[36407,36411,36416,36420,36424,36428],{"type":254,"tag":622,"props":36408,"children":36409},{"style":629},[36410],{"type":259,"value":2678},{"type":254,"tag":622,"props":36412,"children":36413},{"style":1735},[36414],{"type":259,"value":36415},"After",{"type":254,"tag":622,"props":36417,"children":36418},{"style":629},[36419],{"type":259,"value":1668},{"type":254,"tag":622,"props":36421,"children":36422},{"style":1202},[36423],{"type":259,"value":35885},{"type":254,"tag":622,"props":36425,"children":36426},{"style":629},[36427],{"type":259,"value":2309},{"type":254,"tag":622,"props":36429,"children":36430},{"style":1202},[36431],{"type":259,"value":33411},{"type":254,"tag":622,"props":36433,"children":36434},{"class":624,"line":705},[36435,36439,36443,36447,36451,36455,36459],{"type":254,"tag":622,"props":36436,"children":36437},{"style":629},[36438],{"type":259,"value":2678},{"type":254,"tag":622,"props":36440,"children":36441},{"style":1735},[36442],{"type":259,"value":33423},{"type":254,"tag":622,"props":36444,"children":36445},{"style":629},[36446],{"type":259,"value":1668},{"type":254,"tag":622,"props":36448,"children":36449},{"style":644},[36450],{"type":259,"value":2837},{"type":254,"tag":622,"props":36452,"children":36453},{"style":629},[36454],{"type":259,"value":9116},{"type":254,"tag":622,"props":36456,"children":36457},{"style":629},[36458],{"type":259,"value":2697},{"type":254,"tag":622,"props":36460,"children":36461},{"style":1202},[36462],{"type":259,"value":33411},{"type":254,"tag":622,"props":36464,"children":36465},{"class":624,"line":732},[36466,36470],{"type":254,"tag":622,"props":36467,"children":36468},{"style":629},[36469],{"type":259,"value":33444},{"type":254,"tag":622,"props":36471,"children":36472},{"style":1202},[36473],{"type":259,"value":33411},{"type":254,"tag":622,"props":36475,"children":36476},{"class":624,"line":759},[36477,36481,36486,36490,36494,36498,36502,36506,36510,36514],{"type":254,"tag":622,"props":36478,"children":36479},{"style":714},[36480],{"type":259,"value":3582},{"type":254,"tag":622,"props":36482,"children":36483},{"style":714},[36484],{"type":259,"value":36485}," publishingUsername",{"type":254,"tag":622,"props":36487,"children":36488},{"style":629},[36489],{"type":259,"value":1619},{"type":254,"tag":622,"props":36491,"children":36492},{"style":1735},[36493],{"type":259,"value":36147},{"type":254,"tag":622,"props":36495,"children":36496},{"style":629},[36497],{"type":259,"value":1668},{"type":254,"tag":622,"props":36499,"children":36500},{"style":629},[36501],{"type":259,"value":652},{"type":254,"tag":622,"props":36503,"children":36504},{"style":664},[36505],{"type":259,"value":35577},{"type":254,"tag":622,"props":36507,"children":36508},{"style":629},[36509],{"type":259,"value":652},{"type":254,"tag":622,"props":36511,"children":36512},{"style":629},[36513],{"type":259,"value":33522},{"type":254,"tag":622,"props":36515,"children":36516},{"style":1202},[36517],{"type":259,"value":33411},{"type":254,"tag":622,"props":36519,"children":36520},{"class":624,"line":798},[36521,36525,36530,36534,36538,36542,36546,36550,36554,36558],{"type":254,"tag":622,"props":36522,"children":36523},{"style":714},[36524],{"type":259,"value":3582},{"type":254,"tag":622,"props":36526,"children":36527},{"style":714},[36528],{"type":259,"value":36529}," publishingUserPassword",{"type":254,"tag":622,"props":36531,"children":36532},{"style":629},[36533],{"type":259,"value":1619},{"type":254,"tag":622,"props":36535,"children":36536},{"style":1735},[36537],{"type":259,"value":36147},{"type":254,"tag":622,"props":36539,"children":36540},{"style":629},[36541],{"type":259,"value":1668},{"type":254,"tag":622,"props":36543,"children":36544},{"style":629},[36545],{"type":259,"value":652},{"type":254,"tag":622,"props":36547,"children":36548},{"style":664},[36549],{"type":259,"value":35669},{"type":254,"tag":622,"props":36551,"children":36552},{"style":629},[36553],{"type":259,"value":652},{"type":254,"tag":622,"props":36555,"children":36556},{"style":629},[36557],{"type":259,"value":33522},{"type":254,"tag":622,"props":36559,"children":36560},{"style":1202},[36561],{"type":259,"value":33411},{"type":254,"tag":622,"props":36563,"children":36564},{"class":624,"line":836},[36565,36569,36574,36578,36582,36586,36591,36595,36600,36604,36608,36612,36617,36621,36625,36629,36633,36637,36641,36645,36649,36653],{"type":254,"tag":622,"props":36566,"children":36567},{"style":714},[36568],{"type":259,"value":3582},{"type":254,"tag":622,"props":36570,"children":36571},{"style":714},[36572],{"type":259,"value":36573}," base64Auth",{"type":254,"tag":622,"props":36575,"children":36576},{"style":629},[36577],{"type":259,"value":1619},{"type":254,"tag":622,"props":36579,"children":36580},{"style":1202},[36581],{"type":259,"value":12573},{"type":254,"tag":622,"props":36583,"children":36584},{"style":629},[36585],{"type":259,"value":594},{"type":254,"tag":622,"props":36587,"children":36588},{"style":1735},[36589],{"type":259,"value":36590},"ToBase64String",{"type":254,"tag":622,"props":36592,"children":36593},{"style":629},[36594],{"type":259,"value":1668},{"type":254,"tag":622,"props":36596,"children":36597},{"style":1202},[36598],{"type":259,"value":36599},"Encoding",{"type":254,"tag":622,"props":36601,"children":36602},{"style":629},[36603],{"type":259,"value":594},{"type":254,"tag":622,"props":36605,"children":36606},{"style":1202},[36607],{"type":259,"value":2933},{"type":254,"tag":622,"props":36609,"children":36610},{"style":629},[36611],{"type":259,"value":594},{"type":254,"tag":622,"props":36613,"children":36614},{"style":1735},[36615],{"type":259,"value":36616},"GetBytes",{"type":254,"tag":622,"props":36618,"children":36619},{"style":629},[36620],{"type":259,"value":1668},{"type":254,"tag":622,"props":36622,"children":36623},{"style":629},[36624],{"type":259,"value":23556},{"type":254,"tag":622,"props":36626,"children":36627},{"style":1202},[36628],{"type":259,"value":35577},{"type":254,"tag":622,"props":36630,"children":36631},{"style":629},[36632],{"type":259,"value":2421},{"type":254,"tag":622,"props":36634,"children":36635},{"style":664},[36636],{"type":259,"value":474},{"type":254,"tag":622,"props":36638,"children":36639},{"style":629},[36640],{"type":259,"value":3234},{"type":254,"tag":622,"props":36642,"children":36643},{"style":1202},[36644],{"type":259,"value":35669},{"type":254,"tag":622,"props":36646,"children":36647},{"style":629},[36648],{"type":259,"value":2443},{"type":254,"tag":622,"props":36650,"children":36651},{"style":629},[36652],{"type":259,"value":33857},{"type":254,"tag":622,"props":36654,"children":36655},{"style":1202},[36656],{"type":259,"value":33411},{"type":254,"tag":622,"props":36658,"children":36659},{"class":624,"line":862},[36660],{"type":254,"tag":622,"props":36661,"children":36662},{"style":1202},[36663],{"type":259,"value":33411},{"type":254,"tag":622,"props":36665,"children":36666},{"class":624,"line":900},[36667,36671,36676,36680,36685,36689,36693,36697,36702,36706,36711,36715,36719,36723,36727,36731],{"type":254,"tag":622,"props":36668,"children":36669},{"style":629},[36670],{"type":259,"value":7029},{"type":254,"tag":622,"props":36672,"children":36673},{"style":1196},[36674],{"type":259,"value":36675}," using",{"type":254,"tag":622,"props":36677,"children":36678},{"style":714},[36679],{"type":259,"value":1609},{"type":254,"tag":622,"props":36681,"children":36682},{"style":714},[36683],{"type":259,"value":36684}," package",{"type":254,"tag":622,"props":36686,"children":36687},{"style":629},[36688],{"type":259,"value":1619},{"type":254,"tag":622,"props":36690,"children":36691},{"style":1202},[36692],{"type":259,"value":22927},{"type":254,"tag":622,"props":36694,"children":36695},{"style":629},[36696],{"type":259,"value":594},{"type":254,"tag":622,"props":36698,"children":36699},{"style":1735},[36700],{"type":259,"value":36701},"OpenRead",{"type":254,"tag":622,"props":36703,"children":36704},{"style":629},[36705],{"type":259,"value":1668},{"type":254,"tag":622,"props":36707,"children":36708},{"style":1202},[36709],{"type":259,"value":36710},"ArtifactsDirectory ",{"type":254,"tag":622,"props":36712,"children":36713},{"style":629},[36714],{"type":259,"value":9768},{"type":254,"tag":622,"props":36716,"children":36717},{"style":629},[36718],{"type":259,"value":661},{"type":254,"tag":622,"props":36720,"children":36721},{"style":664},[36722],{"type":259,"value":34130},{"type":254,"tag":622,"props":36724,"children":36725},{"style":629},[36726],{"type":259,"value":652},{"type":254,"tag":622,"props":36728,"children":36729},{"style":629},[36730],{"type":259,"value":33522},{"type":254,"tag":622,"props":36732,"children":36733},{"style":1202},[36734],{"type":259,"value":33411},{"type":254,"tag":622,"props":36736,"children":36737},{"class":624,"line":938},[36738,36743,36747,36751,36755,36759,36763,36767],{"type":254,"tag":622,"props":36739,"children":36740},{"style":1196},[36741],{"type":259,"value":36742},"        using",{"type":254,"tag":622,"props":36744,"children":36745},{"style":714},[36746],{"type":259,"value":1609},{"type":254,"tag":622,"props":36748,"children":36749},{"style":714},[36750],{"type":259,"value":1614},{"type":254,"tag":622,"props":36752,"children":36753},{"style":629},[36754],{"type":259,"value":1619},{"type":254,"tag":622,"props":36756,"children":36757},{"style":629},[36758],{"type":259,"value":1624},{"type":254,"tag":622,"props":36760,"children":36761},{"style":714},[36762],{"type":259,"value":1629},{"type":254,"tag":622,"props":36764,"children":36765},{"style":629},[36766],{"type":259,"value":36325},{"type":254,"tag":622,"props":36768,"children":36769},{"style":1202},[36770],{"type":259,"value":33411},{"type":254,"tag":622,"props":36772,"children":36773},{"class":624,"line":976},[36774,36779,36783,36788,36792,36797,36801,36805,36810,36814,36818,36822,36826,36830,36834,36838],{"type":254,"tag":622,"props":36775,"children":36776},{"style":1202},[36777],{"type":259,"value":36778},"        httpClient",{"type":254,"tag":622,"props":36780,"children":36781},{"style":629},[36782],{"type":259,"value":594},{"type":254,"tag":622,"props":36784,"children":36785},{"style":1202},[36786],{"type":259,"value":36787},"DefaultRequestHeaders",{"type":254,"tag":622,"props":36789,"children":36790},{"style":629},[36791],{"type":259,"value":594},{"type":254,"tag":622,"props":36793,"children":36794},{"style":1202},[36795],{"type":259,"value":36796},"Authorization ",{"type":254,"tag":622,"props":36798,"children":36799},{"style":629},[36800],{"type":259,"value":1654},{"type":254,"tag":622,"props":36802,"children":36803},{"style":629},[36804],{"type":259,"value":1624},{"type":254,"tag":622,"props":36806,"children":36807},{"style":714},[36808],{"type":259,"value":36809}," AuthenticationHeaderValue",{"type":254,"tag":622,"props":36811,"children":36812},{"style":629},[36813],{"type":259,"value":1668},{"type":254,"tag":622,"props":36815,"children":36816},{"style":629},[36817],{"type":259,"value":652},{"type":254,"tag":622,"props":36819,"children":36820},{"style":664},[36821],{"type":259,"value":35042},{"type":254,"tag":622,"props":36823,"children":36824},{"style":629},[36825],{"type":259,"value":652},{"type":254,"tag":622,"props":36827,"children":36828},{"style":629},[36829],{"type":259,"value":3149},{"type":254,"tag":622,"props":36831,"children":36832},{"style":1202},[36833],{"type":259,"value":36573},{"type":254,"tag":622,"props":36835,"children":36836},{"style":629},[36837],{"type":259,"value":33522},{"type":254,"tag":622,"props":36839,"children":36840},{"style":1202},[36841],{"type":259,"value":33411},{"type":254,"tag":622,"props":36843,"children":36844},{"class":624,"line":1014},[36845,36849,36853,36857,36862,36866,36870,36875,36879,36884,36889,36893,36898,36903,36907,36911],{"type":254,"tag":622,"props":36846,"children":36847},{"style":629},[36848],{"type":259,"value":7029},{"type":254,"tag":622,"props":36850,"children":36851},{"style":1202},[36852],{"type":259,"value":1614},{"type":254,"tag":622,"props":36854,"children":36855},{"style":629},[36856],{"type":259,"value":594},{"type":254,"tag":622,"props":36858,"children":36859},{"style":1735},[36860],{"type":259,"value":36861},"PostAsync",{"type":254,"tag":622,"props":36863,"children":36864},{"style":629},[36865],{"type":259,"value":1668},{"type":254,"tag":622,"props":36867,"children":36868},{"style":629},[36869],{"type":259,"value":9607},{"type":254,"tag":622,"props":36871,"children":36872},{"style":664},[36873],{"type":259,"value":36874},"https://",{"type":254,"tag":622,"props":36876,"children":36877},{"style":629},[36878],{"type":259,"value":3234},{"type":254,"tag":622,"props":36880,"children":36881},{"style":1735},[36882],{"type":259,"value":36883},"GetPulumiOutput",{"type":254,"tag":622,"props":36885,"children":36886},{"style":629},[36887],{"type":259,"value":36888},"(\"",{"type":254,"tag":622,"props":36890,"children":36891},{"style":664},[36892],{"type":259,"value":35758},{"type":254,"tag":622,"props":36894,"children":36895},{"style":629},[36896],{"type":259,"value":36897},"\")}",{"type":254,"tag":622,"props":36899,"children":36900},{"style":664},[36901],{"type":259,"value":36902},".scm.azurewebsites.net/api/zipdeploy",{"type":254,"tag":622,"props":36904,"children":36905},{"style":629},[36906],{"type":259,"value":652},{"type":254,"tag":622,"props":36908,"children":36909},{"style":629},[36910],{"type":259,"value":3149},{"type":254,"tag":622,"props":36912,"children":36913},{"style":1202},[36914],{"type":259,"value":33411},{"type":254,"tag":622,"props":36916,"children":36917},{"class":624,"line":1052},[36918,36923,36928,36932,36937,36941],{"type":254,"tag":622,"props":36919,"children":36920},{"style":629},[36921],{"type":259,"value":36922},"            new",{"type":254,"tag":622,"props":36924,"children":36925},{"style":714},[36926],{"type":259,"value":36927}," StreamContent",{"type":254,"tag":622,"props":36929,"children":36930},{"style":629},[36931],{"type":259,"value":1668},{"type":254,"tag":622,"props":36933,"children":36934},{"style":1202},[36935],{"type":259,"value":36936},"package",{"type":254,"tag":622,"props":36938,"children":36939},{"style":629},[36940],{"type":259,"value":33857},{"type":254,"tag":622,"props":36942,"children":36943},{"style":1202},[36944],{"type":259,"value":33411},{"type":254,"tag":622,"props":36946,"children":36947},{"class":624,"line":1078},[36948],{"type":254,"tag":622,"props":36949,"children":36950},{"style":629},[36951],{"type":259,"value":8244},{"type":254,"tag":427,"props":36953,"children":36954},{"icon":567},[36955],{"type":254,"tag":255,"props":36956,"children":36957},{},[36958,36960,36967],{"type":259,"value":36959},"George Dangl already wrote a nice ",{"type":254,"tag":262,"props":36961,"children":36964},{"href":36962,"rel":36963},"https://blog.dangl.me/archive/lets-use-nuke-to-quickly-deploy-an-app-to-azure-via-zip-deployment/",[266],[36965],{"type":259,"value":36966},"article",{"type":259,"value":36968}," about using Nuke to deploy an application to Azure App Service using the Kudu API, so you can have a look at it. The code in the article is similar to the one we have here except that the credentials don't come from Pulumi outputs but from an Azure Key Vault.",{"type":254,"tag":255,"props":36970,"children":36971},{},[36972],{"type":259,"value":36973},"What I like about this approach is that you know exactly what you are doing, and the deployment logic is not hidden from you in an obscure YAML task whose code you will never read (yes I am talking to you Azure Pipelines and GitHub Actions 😃).",{"type":254,"tag":255,"props":36975,"children":36976},{},[36977],{"type":259,"value":36978},"But the awesome part in Nuke is that you can put a breakpoint in the code and debug it locally. If you need to modify your pipeline, you don't need to write YAML code modifications without knowing if it would work or not 🤞, commit and push your modifications, wait for an agent to run the changed pipeline in the cloud, wait for it to fail, browse the logs to try to understand the problem, and try again until it works.",{"type":254,"tag":282,"props":36980,"children":36982},{"id":36981},"final-pipeline",[36983],{"type":259,"value":36984},"Final pipeline",{"type":254,"tag":255,"props":36986,"children":36987},{},[36988],{"type":259,"value":36989},"If I fold everything, the pipeline code we created looks like that:",{"type":254,"tag":255,"props":36991,"children":36992},{},[36993],{"type":254,"tag":1161,"props":36994,"children":36998},{"alt":36995,"className":36996,"src":36997},"Complete Nuke pipeline in the Build.cs file.",[1165,1166],"/posts/images/pulumi_met_nuke_11.png",[],{"type":254,"tag":255,"props":37000,"children":37001},{},[37002,37004,37010],{"type":259,"value":37003},"I think it is quite clear with the different steps/targets defined with their dependencies/order. Yet if this is not clear enough for you, you can use the ",{"type":254,"tag":389,"props":37005,"children":37007},{"className":37006},[],[37008],{"type":259,"value":37009},"nuke --plan",{"type":259,"value":37011}," command to display a visual representation of the pipeline (how cool is that !?)",{"type":254,"tag":255,"props":37013,"children":37014},{},[37015],{"type":254,"tag":1161,"props":37016,"children":37020},{"alt":37017,"className":37018,"src":37019},"Nuke execution plan displayed as a graph.",[1165,1166],"/posts/images/pulumi_met_nuke_12.png",[],{"type":254,"tag":427,"props":37022,"children":37023},{"icon":429},[37024],{"type":254,"tag":255,"props":37025,"children":37026},{},[37027],{"type":259,"value":37028},"You can see that the execution plan is almost identical to the pipelines steps we talked about in the beginning of the article. The only difference is that we added to the Publish step a dependence on Clean.",{"type":254,"tag":255,"props":37030,"children":37031},{},[37032],{"type":259,"value":37033},"Let's execute the complete pipeline:",{"type":254,"tag":255,"props":37035,"children":37036},{},[37037],{"type":254,"tag":1161,"props":37038,"children":37042},{"alt":37039,"className":37040,"src":37041,"width":32967},"Pipeline output in terminal showing the different steps and their status.",[1165,1166],"/posts/images/pulumi_met_nuke_13.png",[],{"type":254,"tag":255,"props":37044,"children":37045},{},[37046],{"type":259,"value":37047},"If I go to my Azure portal I can see the new Azure resources, among them an App Service where my Weather API is deployed.",{"type":254,"tag":255,"props":37049,"children":37050},{},[37051],{"type":254,"tag":1161,"props":37052,"children":37056},{"alt":37053,"className":37054,"src":37055,"width":19735},"Weather API weather forecast endpoint response opened in a browser.",[1165,1166],"/posts/images/pulumi_met_nuke_14.png",[],{"type":254,"tag":282,"props":37058,"children":37060},{"id":37059},"conclusion",[37061],{"type":259,"value":37062},"Conclusion",{"type":254,"tag":455,"props":37064,"children":37066},{"id":37065},"improvements-to-the-example-pipeline",[37067],{"type":259,"value":37068},"Improvements to the example pipeline",{"type":254,"tag":255,"props":37070,"children":37071},{},[37072],{"type":259,"value":37073},"The pipeline I have shown in this article is just a simple sample. They are lots of things that could be done to improve it. Beyond obvious ones like adding a Test target or using GitVersion to version the package, I want to talk about some choices I made in the pipeline implementation that may not be the best ones.",{"type":254,"tag":255,"props":37075,"children":37076},{},[37077,37079,37086,37088,37095,37097,37104],{"type":259,"value":37078},"As I said there are many ways to deploy a package to an App Service. While using the Kudu API is fine and allowed me to show you how we can use Pulumi stack outputs to retrieve publishing credentials, it might be a bit limited in some cases and involves a bit of manual code to make the HTTP request. A good alternative would be to use the Azure CLI that has ",{"type":254,"tag":262,"props":37080,"children":37083},{"href":37081,"rel":37082},"https://learn.microsoft.com/en-us/cli/azure/webapp/deployment/source?view=azure-cli-latest#az-webapp-deployment-source-config-zip",[266],[37084],{"type":259,"value":37085},"a command",{"type":259,"value":37087}," for that. But my preferred option would be to use the ",{"type":254,"tag":262,"props":37089,"children":37092},{"href":37090,"rel":37091},"https://learn.microsoft.com/en-us/dotnet/api/overview/azure/resource-manager?view=azure-dotnet",[266],[37093],{"type":259,"value":37094},"Azure Resource Manager libraries for .NET",{"type":259,"value":37096},". Unfortunately this SDK is quite new and miss ",{"type":254,"tag":262,"props":37098,"children":37101},{"href":37099,"rel":37100},"https://github.com/Azure/azure-sdk-for-net/issues/30577",[266],[37102],{"type":259,"value":37103},"samples",{"type":259,"value":37105}," on how to do that.",{"type":254,"tag":255,"props":37107,"children":37108},{},[37109,37111,37118],{"type":259,"value":37110},"Speaking of SDK, Pulumi has an API called the ",{"type":254,"tag":262,"props":37112,"children":37115},{"href":37113,"rel":37114},"https://www.pulumi.com/automation/",[266],[37116],{"type":259,"value":37117},"Automation API",{"type":259,"value":37119}," to use the Pulumi engine as an SDK. I think it would be a better option than using the Pulumi CLI. Generally speaking, I think using SDK instead of CLIs to write the targets of a pipeline brings more power, more flexibility, and a better developer experience.",{"type":254,"tag":455,"props":37121,"children":37123},{"id":37122},"about-nuke-and-cicd",[37124],{"type":259,"value":37125},"About Nuke and CI/CD",{"type":254,"tag":255,"props":37127,"children":37128},{},[37129],{"type":259,"value":37130},"Nuke has many features I did not show in this small example. If we add some attributes to the pipeline code, Nuke can generate YAML workflow files to execute the Nuke pipeline. When executing the pipeline locally everything works fine because I am logged in to Pulumi CLI and Azure CLI in my terminal but I have to add secret parameters to my Nuke pipeline (a Pulumi token and an Azure Service Principal identifier/password) to make the authentication works when the pipeline is run from a CI/CD platform runner/agent.",{"type":254,"tag":255,"props":37132,"children":37133},{},[37134,37136,37142,37143,37150],{"type":259,"value":37135},"Moreover, there are many things I don't know yet about Nuke because I am just starting to use it. That is why I advise you to have a look at its ",{"type":254,"tag":262,"props":37137,"children":37140},{"href":37138,"rel":37139},"https://nuke.build/docs/introduction/",[266],[37141],{"type":259,"value":6340},{"type":259,"value":16280},{"type":254,"tag":262,"props":37144,"children":37147},{"href":37145,"rel":37146},"https://nuke.build/resources/",[266],[37148],{"type":259,"value":37149},"at some resources",{"type":259,"value":37151}," and start playing with it by yourself.",{"type":254,"tag":255,"props":37153,"children":37154},{},[37155],{"type":259,"value":37156},"In the future, I see myself using Nuke for most of my CI pipelines, and not only for .NET projects (because I can run any CLI tools from Nuke, it also works for front projects where I would use the pnpm CLI for instance). I am not saying that because I am afraid of YAML or because I'm not familiar with ready-made tasks like Azure Pipelines tasks or GitHub Actions. I have been using Azure Pipelines for several years now and I have also played a bit with GitHub Actions. They are good platforms but lack local debugging and the great developer experience provided by a tool like Nuke. So I will continue using them but to run my Nuke pipelines 😉.",{"type":254,"tag":255,"props":37158,"children":37159},{},[37160],{"type":259,"value":37161},"Concerning the CD pipelines, I don't know yet if I can use Nuke for all my use cases. They are real benefits to using Nuke for deployments but I still have to investigate how some things can be done like deploying to multiple environments, and handling approvals between environments.",{"type":254,"tag":455,"props":37163,"children":37165},{"id":37164},"pulumi-nuke-the-perfect-combo",[37166],{"type":259,"value":37167},"Pulumi & Nuke, the perfect combo?",{"type":254,"tag":255,"props":37169,"children":37170},{},[37171],{"type":259,"value":37172},"I don't know if it's the perfect combo but it's definitively one I love. Having .NET everywhere, from infrastructure code to pipeline code without forgetting the application code is awesome.",{"type":254,"tag":1260,"props":37174,"children":37175},{},[37176],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":37178},[37179,37180,37181,37186,37193,37199,37200,37201],{"id":32929,"depth":635,"text":32932},{"id":32950,"depth":635,"text":32953},{"id":33047,"depth":635,"text":33050,"children":37182},[37183,37184,37185],{"id":33053,"depth":679,"text":33056},{"id":33110,"depth":679,"text":33113},{"id":33230,"depth":679,"text":33233},{"id":33276,"depth":635,"text":33279,"children":37187},[37188,37189,37190,37191,37192],{"id":33282,"depth":679,"text":33285},{"id":33337,"depth":679,"text":33340},{"id":33717,"depth":679,"text":33720},{"id":33871,"depth":679,"text":33874},{"id":34109,"depth":679,"text":34112},{"id":34577,"depth":635,"text":34580,"children":37194},[37195,37196,37197,37198],{"id":34583,"depth":679,"text":34586},{"id":34637,"depth":679,"text":34640},{"id":35324,"depth":679,"text":35327},{"id":35805,"depth":679,"text":35808},{"id":36099,"depth":635,"text":36102},{"id":36981,"depth":635,"text":36984},{"id":37059,"depth":635,"text":37062,"children":37202},[37203,37204,37205],{"id":37065,"depth":679,"text":37068},{"id":37122,"depth":679,"text":37125},{"id":37164,"depth":679,"text":37167},"content:1.posts:45.when-pulumi-met-nuke.md","1.posts/45.when-pulumi-met-nuke.md",{"_path":139,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":138,"description":37209,"lead":37209,"date":37210,"image":37211,"badge":37212,"tags":37213,"body":37216,"_type":1275,"_id":37453,"_source":231,"_file":37454,"_extension":1278},"Some tips about .NET, pnpm, and Azure DevOps.","2022-11-20T00:00:00.000Z",{"src":9258},{"label":9260},[9262,1288,37214,24281,31105,37215,9264,245],"winget","vscode",{"type":251,"children":37217,"toc":37448},[37218,37224,37238,37258,37280,37286,37316,37321,37329,37341,37350,37358,37363,37372,37380,37393,37399,37404,37413,37431,37440,37444],{"type":254,"tag":282,"props":37219,"children":37221},{"id":37220},"net-tip-of-the-week-install-net-7-using-winget",[37222],{"type":259,"value":37223},".NET tip of the week: install .NET 7 using winget",{"type":254,"tag":255,"props":37225,"children":37226},{},[37227,37229,37236],{"type":259,"value":37228},"This week, I installed .NET 7 on my laptop and I used ",{"type":254,"tag":262,"props":37230,"children":37233},{"href":37231,"rel":37232},"https://learn.microsoft.com/en-us/windows/package-manager/",[266],[37234],{"type":259,"value":37235},"Windows Package Manager",{"type":259,"value":37237}," for that:",{"type":254,"tag":612,"props":37239,"children":37241},{"className":10656,"code":37240,"language":10658,"meta":205,"style":205},"winget install Microsoft.DotNet.SDK.7\n",[37242],{"type":254,"tag":389,"props":37243,"children":37244},{"__ignoreMap":205},[37245],{"type":254,"tag":622,"props":37246,"children":37247},{"class":624,"line":625},[37248,37253],{"type":254,"tag":622,"props":37249,"children":37250},{"style":1202},[37251],{"type":259,"value":37252},"winget install Microsoft.DotNet.SDK.",{"type":254,"tag":622,"props":37254,"children":37255},{"style":741},[37256],{"type":259,"value":37257},"7\n",{"type":254,"tag":255,"props":37259,"children":37260},{},[37261,37263,37269,37271,37278],{"type":259,"value":37262},"I like winget, I have already written a few articles about it (you can find them ",{"type":254,"tag":262,"props":37264,"children":37267},{"href":37265,"rel":37266},"https://www.techwatching.dev/tags/winget/",[266],[37268],{"type":259,"value":1414},{"type":259,"value":37270},") so I am really glad to see that we can now use winget to install .NET (whether it be the SDKs or the runtimes). You can check ",{"type":254,"tag":262,"props":37272,"children":37275},{"href":37273,"rel":37274},"https://devblogs.microsoft.com/dotnet/dotnet-now-on-windows-package-manager/",[266],[37276],{"type":259,"value":37277},"Microsoft's article",{"type":259,"value":37279}," announcing it for more information.",{"type":254,"tag":282,"props":37281,"children":37283},{"id":37282},"tool-of-the-week-pnpm",[37284],{"type":259,"value":37285},"Tool of the week: pnpm",{"type":254,"tag":255,"props":37287,"children":37288},{},[37289,37291,37296,37298,37305,37307,37314],{"type":259,"value":37290},"I don't know which JavaScript package manager you are using but since I tried ",{"type":254,"tag":262,"props":37292,"children":37294},{"href":32016,"rel":37293},[266],[37295],{"type":259,"value":24281},{"type":259,"value":37297}," I don't want to use anything else because it's so fast! If you are interested to know why it's so fast and better than npm for instance, you can watch the talk ",{"type":254,"tag":262,"props":37299,"children":37302},{"href":37300,"rel":37301},"https://viteconf.org/2022/replay/pnpm",[266],[37303],{"type":259,"value":37304},"\"What makes pnpm performant\"",{"type":259,"value":37306}," that Zoltan Kochan gave at Vite Conf. Many ",{"type":254,"tag":262,"props":37308,"children":37311},{"href":37309,"rel":37310},"https://pnpm.io/workspaces#usage-examples",[266],[37312],{"type":259,"value":37313},"popular open-source projects",{"type":259,"value":37315}," like Vite and Vue are using pnpm.",{"type":254,"tag":255,"props":37317,"children":37318},{},[37319],{"type":259,"value":37320},"Here are some tips about pnpm:",{"type":254,"tag":476,"props":37322,"children":37323},{},[37324],{"type":254,"tag":336,"props":37325,"children":37326},{},[37327],{"type":259,"value":37328},"You can use pnpm to manage Node.js versions on your machine",{"type":254,"tag":255,"props":37330,"children":37331},{},[37332,37334,37339],{"type":259,"value":37333},"Previously, I was using ",{"type":254,"tag":262,"props":37335,"children":37337},{"href":32160,"rel":37336},[266],[37338],{"type":259,"value":32164},{"type":259,"value":37340}," to manage multiple installation of Node.js on my laptop and it worked fine. Yet I can now do that directly using pnpm env command:",{"type":254,"tag":255,"props":37342,"children":37343},{},[37344],{"type":254,"tag":1161,"props":37345,"children":37349},{"alt":37346,"className":37347,"src":37348},"Output of the pnpm env command in a terminal.",[1165,1166],"/posts/images/w462022tips_pnpm_env.png",[],{"type":254,"tag":476,"props":37351,"children":37352},{"start":635},[37353],{"type":254,"tag":336,"props":37354,"children":37355},{},[37356],{"type":259,"value":37357},"You can configure vscode to run npm scripts using pnpm",{"type":254,"tag":255,"props":37359,"children":37360},{},[37361],{"type":259,"value":37362},"A lot of the people I know don't use the scripts explorer of vscode to run the scripts contained in the package.json file of the project opened in vscode. It's a pity because it is an handy feature. And you can configure it in your settings to run scripts using a specific package manager, pnpm in my case.",{"type":254,"tag":255,"props":37364,"children":37365},{},[37366],{"type":254,"tag":1161,"props":37367,"children":37371},{"alt":37368,"className":37369,"src":37370,"width":32967},"Npm scripts view in vscode editor.",[1165,1166],"/posts/images/w462022tips_pnpm_scripts.png",[],{"type":254,"tag":476,"props":37373,"children":37374},{"start":679},[37375],{"type":254,"tag":336,"props":37376,"children":37377},{},[37378],{"type":259,"value":37379},"With pnpm, you can use aliases for packages you install",{"type":254,"tag":255,"props":37381,"children":37382},{},[37383,37385,37391],{"type":259,"value":37384},"Check the ",{"type":254,"tag":262,"props":37386,"children":37389},{"href":37387,"rel":37388},"https://pnpm.io/aliases",[266],[37390],{"type":259,"value":6340},{"type":259,"value":37392}," to see why and how to use this feature.",{"type":254,"tag":282,"props":37394,"children":37396},{"id":37395},"the-gitlensazure-devops-tip-you-did-not-know-about-autolinks",[37397],{"type":259,"value":37398},"The GitLens/Azure DevOps tip you did not know about: autolinks",{"type":254,"tag":255,"props":37400,"children":37401},{},[37402],{"type":259,"value":37403},"GitLens, the awesome extension for vscode has a nice feature called \"autolinks\" that can make external references in your commit messages clickable links.",{"type":254,"tag":255,"props":37405,"children":37406},{},[37407],{"type":254,"tag":1161,"props":37408,"children":37412},{"alt":37409,"className":37410,"src":37411,"width":19735},"Autolinks GitLens settings view in vscode.",[1165,1166],"/posts/images/w462022tips_gitlens_autolink_1.png",[],{"type":254,"tag":255,"props":37414,"children":37415},{},[37416,37418,37425],{"type":259,"value":37417},"If you are using Azure DevOps, this feature can become very handy for you commit messages that contain references to work items (usually an hasjtag followed by the work item number). You just have to configure # as the prefix and ",{"type":254,"tag":262,"props":37419,"children":37422},{"href":37420,"rel":37421},"https://dev.azure.com/%7BorganizationName%7D/%7BprojectName%7D/_workitems/edit/",[266],[37423],{"type":259,"value":37424},"https://dev.azure.com/{organizationName}/{projectName}/_workitems/edit/",{"type":254,"tag":37426,"props":37427,"children":37428},"num",{},[37429],{"type":259,"value":37430}," as the URL) to make it work.",{"type":254,"tag":255,"props":37432,"children":37433},{},[37434],{"type":254,"tag":1161,"props":37435,"children":37439},{"alt":37436,"className":37437,"src":37438,"width":19735},"A commit GitLens popin in vscode with an Azure DevOps link.",[1165,1166],"/posts/images/w462022tips_gitlens_autolink_2.png",[],{"type":254,"tag":255,"props":37441,"children":37442},{},[37443],{"type":259,"value":10040},{"type":254,"tag":1260,"props":37445,"children":37446},{},[37447],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":37449},[37450,37451,37452],{"id":37220,"depth":635,"text":37223},{"id":37282,"depth":635,"text":37285},{"id":37395,"depth":635,"text":37398},"content:1.posts:44.w46-2022-tips-learned-this-week.md","1.posts/44.w46-2022-tips-learned-this-week.md",{"_path":136,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":135,"description":37456,"lead":37457,"date":37458,"image":37459,"badge":37461,"tags":37462,"body":37464,"_type":1275,"_id":38101,"_source":231,"_file":38102,"_extension":1278},"This article is a discussion about API clients. Without being a comparison between the best API clients, this article talks about the pros and cons of some popular tools to send HTTP requests to an API. The goal is not to elect the best one, but rather to try to answer the following question: what we should consider when choosing an API client, and what are the challenges when using one?","What should you consider when choosing an API client?","2022-09-08T00:00:00.000Z",{"src":37460},"/images/tools_1.jpg",{"label":243},[245,246,10061,37463],"rest",{"type":251,"children":37465,"toc":38083},[37466,37470,37474,37488,37558,37586,37591,37597,37602,37611,37617,37622,37627,37636,37641,37659,37664,37670,37675,37688,37694,37699,37704,37725,37734,37752,37795,37801,37806,37815,37821,37843,37848,37854,37859,37864,37872,37878,37883,37888,37906,37915,37927,37932,37937,37943,37948,37953,37958,37963,37968,37974,37987,37992,38005,38033,38047,38055,38072,38078],{"type":254,"tag":255,"props":37467,"children":37468},{},[37469],{"type":259,"value":37456},{"type":254,"tag":282,"props":37471,"children":37472},{"id":1299},[37473],{"type":259,"value":1302},{"type":254,"tag":255,"props":37475,"children":37476},{},[37477,37479,37486],{"type":259,"value":37478},"I like discussing tooling because as a developer choosing the right tool is often what makes me more productive in my job. I am talking about the \"right tool\" because I am not necessarily looking for the best one but the most appropriate one for my needs in a given context. The topic of API clients is not new to me, indeed the first post I wrote on this blog in March 2019 (and my most-read article on DEV.to where I re-posted it) was about using the vscode extension \"REST Client\" instead of Postman. ",{"type":254,"tag":262,"props":37480,"children":37483},{"href":37481,"rel":37482},"https://www.techwatching.dev/posts/testing-your-api-with-rest-client",[266],[37484],{"type":259,"value":37485},"This article",{"type":259,"value":37487}," is still relevant and in fact, I have been using REST Client as my main API client for a few years on different projects and in different teams.",{"type":254,"tag":255,"props":37489,"children":37490},{},[37491,37493,37500,37501,37507,37508,37515,37516,37523,37524,37531,37532,37539,37540,37547,37549,37556],{"type":259,"value":37492},"Yet I am not writing now to convince you to use \"REST Client\" or any other tool. Besides, you probably already have a favorite tool you are using to send HTTP requests. Indeed, there are many options: ",{"type":254,"tag":262,"props":37494,"children":37497},{"href":37495,"rel":37496},"https://www.postman.com",[266],[37498],{"type":259,"value":37499},"Postman",{"type":259,"value":16280},{"type":254,"tag":262,"props":37502,"children":37504},{"href":10849,"rel":37503},[266],[37505],{"type":259,"value":37506},"REST Client",{"type":259,"value":16280},{"type":254,"tag":262,"props":37509,"children":37512},{"href":37510,"rel":37511},"https://www.thunderclient.com/",[266],[37513],{"type":259,"value":37514},"Thunder Client",{"type":259,"value":16280},{"type":254,"tag":262,"props":37517,"children":37520},{"href":37518,"rel":37519},"https://nightingale.rest/",[266],[37521],{"type":259,"value":37522},"Nightingale",{"type":259,"value":16280},{"type":254,"tag":262,"props":37525,"children":37528},{"href":37526,"rel":37527},"https://insomnia.rest/",[266],[37529],{"type":259,"value":37530},"Insomnia",{"type":259,"value":16280},{"type":254,"tag":262,"props":37533,"children":37536},{"href":37534,"rel":37535},"https://rapidapi.com/products/api-design/",[266],[37537],{"type":259,"value":37538},"RapidAPI Client",{"type":259,"value":16280},{"type":254,"tag":262,"props":37541,"children":37544},{"href":37542,"rel":37543},"https://hoppscotch.io/fr/",[266],[37545],{"type":259,"value":37546},"Hoppscotch",{"type":259,"value":37548},", and ",{"type":254,"tag":262,"props":37550,"children":37553},{"href":37551,"rel":37552},"https://httpie.io/",[266],[37554],{"type":259,"value":37555},"HTTPie",{"type":259,"value":37557}," just to name a few. There are many reasons why people choose one tool over another:",{"type":254,"tag":332,"props":37559,"children":37560},{},[37561,37566,37571,37576,37581],{"type":254,"tag":336,"props":37562,"children":37563},{},[37564],{"type":259,"value":37565},"it has more features than others",{"type":254,"tag":336,"props":37567,"children":37568},{},[37569],{"type":259,"value":37570},"some colleagues suggested it",{"type":254,"tag":336,"props":37572,"children":37573},{},[37574],{"type":259,"value":37575},"it's the latest tool featured on ProductHunt",{"type":254,"tag":336,"props":37577,"children":37578},{},[37579],{"type":259,"value":37580},"everyone in the company uses this API client",{"type":254,"tag":336,"props":37582,"children":37583},{},[37584],{"type":259,"value":37585},"it would be time-consuming to learn how to use another API Client",{"type":254,"tag":255,"props":37587,"children":37588},{},[37589],{"type":259,"value":37590},"I don't think these are good enough reasons, so I will talk to you about what matters to me when using an API Client.",{"type":254,"tag":282,"props":37592,"children":37594},{"id":37593},"ease-of-use",[37595],{"type":259,"value":37596},"Ease of use",{"type":254,"tag":255,"props":37598,"children":37599},{},[37600],{"type":259,"value":37601},"When I am using an API client, it's to make requests to an API and get responses. It's often to test an API or to debug the API I am developing. Therefore what's important to me is to have a simple tool that makes it easy to write HTTP requests, and display the responses and that's it.",{"type":254,"tag":255,"props":37603,"children":37604},{},[37605],{"type":254,"tag":1161,"props":37606,"children":37610},{"alt":37607,"className":37608,"src":37609},"A scrabble tile that says keep things simple.",[1165,1166],"/posts/images/httpclients_1.jpg",[],{"type":254,"tag":455,"props":37612,"children":37614},{"id":37613},"all-conceivable-functionalities-vs-essentials-features",[37615],{"type":259,"value":37616},"All conceivable functionalities vs. essentials features",{"type":254,"tag":255,"props":37618,"children":37619},{},[37620],{"type":259,"value":37621},"A lot of tools try to compete with each other by providing more features or very advanced features. First, it's useless because you are not going to use most of them. Second, it's counterproductive because having too many features will make an API client more complicated to use.",{"type":254,"tag":255,"props":37623,"children":37624},{},[37625],{"type":259,"value":37626},"One good example is Postman which went from a simple tool that makes HTTP requests to a real white elephant. To be honest, I am quite impressed with all the features you can find in Postman: the company has done an amazing job to build a platform that helps you in each step of your API lifecycle (specifications, design, documentation, testing, monitoring, ...). However, the downside is that Postman became bigger, slower, and less easy to use. I have no doubt Postman brings a lot of value to many developers that are building their APIs, yet a lot of people (like me) don't need all these unnecessary features and would be better with a simpler tool. Postman is just an example, there are many other API clients that overwhelm you with concepts you don't need (workspace, collections, mocks, flows), that require you to sign in and configure a bunch of things before being able to write an HTTP request.",{"type":254,"tag":255,"props":37628,"children":37629},{},[37630],{"type":254,"tag":1161,"props":37631,"children":37635},{"alt":37632,"className":37633,"src":37634},"Logo of Postman.",[1165,1166],"/posts/images/httpclients_2.jpg",[],{"type":254,"tag":255,"props":37637,"children":37638},{},[37639],{"type":259,"value":37640},"So what are the essentials features an API Client should have besides sending requests to an API and displaying the response?",{"type":254,"tag":332,"props":37642,"children":37643},{},[37644,37649,37654],{"type":254,"tag":336,"props":37645,"children":37646},{},[37647],{"type":259,"value":37648},"First, it should support the \"protocol\" (REST, SOAP, GraphQL, gRPC) used by the API you want to query (pretty obvious but not all API clients support gRPC or GraphQL for instance).",{"type":254,"tag":336,"props":37650,"children":37651},{},[37652],{"type":259,"value":37653},"Second, it should support environment variables. You will need them to easily switch between environments: sending requests to an API in QA and to the same API in Production for instance. Variables can also be useful to store the result of a previous request and use it in another request.",{"type":254,"tag":336,"props":37655,"children":37656},{},[37657],{"type":259,"value":37658},"Third, it should allow version control of your request (we will come back to this later).",{"type":254,"tag":255,"props":37660,"children":37661},{},[37662],{"type":259,"value":37663},"I think that's all you need. In my opinion, testing and integration with CI/CD pipelines (to automate testing) are nice-to-have functionalities but not mandatory. Indeed, if you want to do some complex tests with big scenarios that run in parallel, you are probably not using an API Client for that and rather develop proper integration tests using more appropriate tools.",{"type":254,"tag":455,"props":37665,"children":37667},{"id":37666},"integrated-tools-vs-standalone-tools",[37668],{"type":259,"value":37669},"Integrated tools vs. standalone tools",{"type":254,"tag":255,"props":37671,"children":37672},{},[37673],{"type":259,"value":37674},"Some API Clients are standalone software, whether rich clients (like Postman, or Nightingale) or web applications (like Hoppscotch). Some are directly integrated with your developer tools and IDEs. I feel more productive when everything is in one place and I leave my IDE as little as possible, so that's why I prefer an API Client integrated with my IDE. I find it easier to use a familiar UI in a tool I already know and like rather than using a completely different tool. Everyone has his own way of working so you may prefer a standalone tool and that's fine as well. Just take the time to think about what's best for you.",{"type":254,"tag":255,"props":37676,"children":37677},{},[37678,37680,37686],{"type":259,"value":37679},"If you are fond of vscode, you have different API clients available as extensions, for instance: REST Client, Thunder Client, and RapidAPI Client for VS Code. If your everyday IDE is Visual Studio, there is an extension ",{"type":254,"tag":262,"props":37681,"children":37684},{"href":37682,"rel":37683},"https://marketplace.visualstudio.com/items?itemName=MadsKristensen.RestClient",[266],[37685],{"type":259,"value":37506},{"type":259,"value":37687}," based on the REST Client for vscode. If you only swear by IntelliJ IDEA or Rider, all JetBrains IDEs have a built-in HTTP Client.",{"type":254,"tag":455,"props":37689,"children":37691},{"id":37690},"gui-tools-vs-text-based-tools",[37692],{"type":259,"value":37693},"GUI tools vs. text-based tools",{"type":254,"tag":255,"props":37695,"children":37696},{},[37697],{"type":259,"value":37698},"There are 2 types of tools: GUI tools and text-based tools.",{"type":254,"tag":255,"props":37700,"children":37701},{},[37702],{"type":259,"value":37703},"Generally, GUI tools are easier to use than text-based tools because they offer a simple way to create and send an HTTP request. Of course, this is not always the case: I already talked about some API clients that have a complex UI because of their many features. Fortunately, other GUI tools have managed to keep a simple and clean user interface. That is precisely how Thunder Client has been designed: a lightweight API client with an easy-to-use UI; and it's a success. I particularly like the GUI-based tests of Thunder Client that allow you to quickly do basic tests on HTTP responses without scripting anything. Test scenarios will be quite limited, yet you probably don't want to use an API client for more complex scenarios. There are many other API clients with a great UI, take the time to test several, the choice will probably be a matter of personal preference.",{"type":254,"tag":255,"props":37705,"children":37706},{},[37707,37709,37714,37716,37723],{"type":259,"value":37708},"Then, why bother with a text-based tool instead? How could it be easier to manually write an HTTP request in a plain text file than using a GUI for that? Because when you use a text-based API client like ",{"type":254,"tag":389,"props":37710,"children":37712},{"className":37711},[],[37713],{"type":259,"value":37506},{"type":259,"value":37715}," you write your request following the standard ",{"type":254,"tag":262,"props":37717,"children":37720},{"href":37718,"rel":37719},"https://www.rfc-editor.org/rfc/rfc7230#section-3",[266],[37721],{"type":259,"value":37722},"RFC 2730",{"type":259,"value":37724},". It's a standard so you will find it everywhere to describe the requests, including in the documentation of most popular APIs. It's quite straightforward so you won't have a hard time writing the requests.",{"type":254,"tag":255,"props":37726,"children":37727},{},[37728],{"type":254,"tag":1161,"props":37729,"children":37733},{"alt":37730,"className":37731,"src":37732},"REST Client in vscode.",[1165,1166],"/posts/images/httpclients_3.png",[],{"type":254,"tag":255,"props":37735,"children":37736},{},[37737,37739,37744,37745,37750],{"type":259,"value":37738},"Moreover, these kinds of API clients (which are often IDEs extensions or built-in in an IDE) will understand the HTTP text files (with ",{"type":254,"tag":19837,"props":37740,"children":37741},{},[37742],{"type":259,"value":37743},".rest",{"type":259,"value":31561},{"type":254,"tag":19837,"props":37746,"children":37747},{},[37748],{"type":259,"value":37749},".http",{"type":259,"value":37751}," extension) and help you write the requests. You can write multiple requests in the same file and don't have to learn how to use a specific tool, just to write requests in plain text. Therefore, once you are used to the raw syntax of HTTP requests, using a text-based tool becomes even easier to use than the simplest GUI tool.",{"type":254,"tag":427,"props":37753,"children":37754},{"icon":567},[37755,37760],{"type":254,"tag":255,"props":37756,"children":37757},{},[37758],{"type":259,"value":37759},"I already mentioned them before but examples of such text-based tools are:",{"type":254,"tag":332,"props":37761,"children":37762},{},[37763,37773,37783],{"type":254,"tag":336,"props":37764,"children":37765},{},[37766,37771],{"type":254,"tag":262,"props":37767,"children":37769},{"href":10849,"rel":37768},[266],[37770],{"type":259,"value":37506},{"type":259,"value":37772}," in vscode",{"type":254,"tag":336,"props":37774,"children":37775},{},[37776,37781],{"type":254,"tag":262,"props":37777,"children":37779},{"href":37682,"rel":37778},[266],[37780],{"type":259,"value":37506},{"type":259,"value":37782}," in Visual Studio",{"type":254,"tag":336,"props":37784,"children":37785},{},[37786,37793],{"type":254,"tag":262,"props":37787,"children":37790},{"href":37788,"rel":37789},"https://www.jetbrains.com/help/rider/Http_client_in__product__code_editor.html",[266],[37791],{"type":259,"value":37792},"HTTP Client",{"type":259,"value":37794}," in Rider or any other JetBrains IDE",{"type":254,"tag":282,"props":37796,"children":37798},{"id":37797},"collaboration",[37799],{"type":259,"value":37800},"Collaboration",{"type":254,"tag":255,"props":37802,"children":37803},{},[37804],{"type":259,"value":37805},"The reason why most API clients are a no go for me is that they don't facilitate collaboration. Collaboration is an important part of my job so I need an API client that makes it easy to share requests with others.",{"type":254,"tag":255,"props":37807,"children":37808},{},[37809],{"type":254,"tag":1161,"props":37810,"children":37814},{"alt":37811,"className":37812,"src":37813},"A scrabble board with words spelling teamwork.",[1165,1166],"/posts/images/httpclients_4.jpg",[],{"type":254,"tag":455,"props":37816,"children":37818},{"id":37817},"are-you-ready-to-pay-the-price-for-team-collaboration",[37819],{"type":259,"value":37820},"Are you ready to pay the price for team collaboration?",{"type":254,"tag":255,"props":37822,"children":37823},{},[37824,37826,37832,37834,37841],{"type":259,"value":37825},"Although a few API clients totally lack collaboration features, many of them have nice features to collaborate with your team. However, that is also how the companies behind these tools make money: charging for collaboration features. Usually working with an API client is free for individuals but you have to pay when you are a company or a team. You can see for instance the pricing page of ",{"type":254,"tag":262,"props":37827,"children":37830},{"href":37828,"rel":37829},"https://www.postman.com/pricing/",[266],[37831],{"type":259,"value":37499},{"type":259,"value":37833}," or the one from ",{"type":254,"tag":262,"props":37835,"children":37838},{"href":37836,"rel":37837},"https://rapidapi.com/products/api-design",[266],[37839],{"type":259,"value":37840},"RapidAPI",{"type":259,"value":37842},". I have nothing against that: these companies put a lot of work and money into making these great tools, they need to be profitable and make money. Yet the question is: are you (or is your company) willing to pay for an API client?",{"type":254,"tag":255,"props":37844,"children":37845},{},[37846],{"type":259,"value":37847},"If the answer is yes, then fine, choose the best tool for your team, buy some licenses and enjoy. If the answer is no, then how do you intend to work with your colleagues without these collaboration features? Let me guess, you have found a way to export your collection of requests to a file. So each time a new colleague joins your team, you send him/her the file by mail; no one has the same version of the file but you don't care. Or maybe you have put it in a shared drive so everyone can edit the requests; you just have to hope that 2 people don't edit it at the same time. Why not put the file on a flash drive and pass it on to each other while you're at it? I think you understood what I was getting at: if you work with a team, pay for collaboration features, or choose a free API client suitable for teamwork.",{"type":254,"tag":455,"props":37849,"children":37851},{"id":37850},"version-control-is-the-key",[37852],{"type":259,"value":37853},"Version control is the key",{"type":254,"tag":255,"props":37855,"children":37856},{},[37857],{"type":259,"value":37858},"In my daily work as a developer, I work in a team and I want to share requests with them, allow them to edit them, and see the history of changes made. It's funny because it's the same kind of thing you want for your application code when developing. And what helps you with that? Version control. So if you use a git repository to version your application code, there is no reason why it would not be a good fit for your collection of HTTP requests.",{"type":254,"tag":255,"props":37860,"children":37861},{},[37862],{"type":259,"value":37863},"Besides, that is exactly what some API clients like Thunder Client or Postman offer: integration with external git repositories. Please note that is not necessary for your API client to have a git integration to share your requests through git, as long as your requests are available in a text file you can version it's fine. So why bother with an obscure \"cloud synchronization\" of your requests when you can share them with your team through a git repository?",{"type":254,"tag":427,"props":37865,"children":37866},{"icon":429},[37867],{"type":254,"tag":255,"props":37868,"children":37869},{},[37870],{"type":259,"value":37871},"The sweet spot when you are building an API is to store your API application code and your requests to call/test this API in the same git repository.",{"type":254,"tag":455,"props":37873,"children":37875},{"id":37874},"how-requests-are-stored-is-important",[37876],{"type":259,"value":37877},"How requests are stored is important",{"type":254,"tag":255,"props":37879,"children":37880},{},[37881],{"type":259,"value":37882},"I previously mentioned how some tools like Thunder Client offer a git integration with external git repositories. Requests and environment variables used by Thunder Client are stored in JSON files that you can add to your git repository. In a similar way, with Postman you can export requests and environment variables in JSON files and version them. There is a big problem with this way of doing things: using JSON files to version HTTP requests is not convenient at all! I have no problem with the JSON format itself, as a developer, it's something I often use. However, HTTP requests stored in a JSON file are not easily readable by a human. If you version HTTP requests, it is not to struggle to understand the history of changes or to have a hard time resolving conflicts between changes made by you and other developers of the team.",{"type":254,"tag":255,"props":37884,"children":37885},{},[37886],{"type":259,"value":37887},"I have seen some QA people using Postman for integration testing that were exporting their Postman data (collection, environments, ...) and versioning it in a git repository. What started out as a good intention turned out to be a very bad idea:",{"type":254,"tag":332,"props":37889,"children":37890},{},[37891,37896,37901],{"type":254,"tag":336,"props":37892,"children":37893},{},[37894],{"type":259,"value":37895},"the exported collection was just a huge JSON file that contained all the requests and tests, so each change was made to this same file with the inconveniences I mentioned previously",{"type":254,"tag":336,"props":37897,"children":37898},{},[37899],{"type":259,"value":37900},"editing the JSON file directly and especially the tests written in javascript is a nightmare because with the Postman export the code is stored as string in the JSON (forget about using code quality tools 🥲)",{"type":254,"tag":336,"props":37902,"children":37903},{},[37904],{"type":259,"value":37905},"the environment files versioned were containing secrets so secrets were committed in clear text in the git repository 😱",{"type":254,"tag":255,"props":37907,"children":37908},{},[37909],{"type":254,"tag":1161,"props":37910,"children":37914},{"alt":37911,"className":37912,"src":37913},"A silhouette of a man.",[1165,1166],"/posts/images/httpclients_5.jpg",[],{"type":254,"tag":255,"props":37916,"children":37917},{},[37918,37920,37925],{"type":259,"value":37919},"To avoid these caveats, I think the best way to store HTTP requests is to store them in HTTP text files following the standard ",{"type":254,"tag":262,"props":37921,"children":37923},{"href":37718,"rel":37922},[266],[37924],{"type":259,"value":37722},{"type":259,"value":37926},". I already talked about text-based tools that were following this approach, unfortunately, I did not find any GUI tool that was doing that (probably because GUI tools always have extra features with things that can't be easily stored in an HTTP file).",{"type":254,"tag":255,"props":37928,"children":37929},{},[37930],{"type":259,"value":37931},"If you pay for an API client with which your requests are synchronized in the cloud with your colleagues, you probably don't care how the requests are stored. However, I hope your API client knows how to handle merge and conflicts (1 or more requests edited by several colleagues at the same time for example). Even if it does, be aware it can be not that easy to manually resolve these conflicts in a GUI tool.",{"type":254,"tag":282,"props":37933,"children":37934},{"id":6186},[37935],{"type":259,"value":37936},"Final thoughts",{"type":254,"tag":455,"props":37938,"children":37940},{"id":37939},"using-the-right-tool-for-a-given-context",[37941],{"type":259,"value":37942},"Using the right tool for a given context",{"type":254,"tag":255,"props":37944,"children":37945},{},[37946],{"type":259,"value":37947},"I think you understood that there was no perfect API client. So once again, the answer to choosing the right one will be \"it depends\".",{"type":254,"tag":255,"props":37949,"children":37950},{},[37951],{"type":259,"value":37952},"If for your project, you need to choose an API client that will only be used by developers of your team that are coding in vscode, maybe REST Client, Thunder Client, or RapidAPI Client for VS Code are interesting choices.",{"type":254,"tag":255,"props":37954,"children":37955},{},[37956],{"type":259,"value":37957},"For a lot of reasons I explained previously I am not a big fan of big standalone tools like Postman. Yet, Postman is installed on my laptop, and if people send me their requests collection to test their API, I would be crazy not to take advantage of it and rewrite every request just because I prefer to use other tools.",{"type":254,"tag":255,"props":37959,"children":37960},{},[37961],{"type":259,"value":37962},"I like the HTTP request format and I think people using API clients should be familiar with it. Unfortunately, sometimes people that need to use an API client don't have a technical background. In that case, maybe GUI tools with a simple interface are better suited than a text-based tool. Thunder Client with its scriptless testing feature might be a good option (although it requires you to install vscode which is more intended for developers).",{"type":254,"tag":255,"props":37964,"children":37965},{},[37966],{"type":259,"value":37967},"There are many API clients that you can use. Without knowing all of them, it can be a good idea to know what are the alternatives to the most famous ones, in order to choose the right one for your current context.",{"type":254,"tag":455,"props":37969,"children":37971},{"id":37970},"what-am-i-currently-using",[37972],{"type":259,"value":37973},"What am I currently using?",{"type":254,"tag":255,"props":37975,"children":37976},{},[37977,37979,37985],{"type":259,"value":37978},"As far as I'm concerned, I prefer using HTTP requests in HTTP files that I edit in a text editor or an IDE and that I commit to a git repository. That way, I have several HTTP files with HTTP requests grouped by concern. When I or one of my colleagues make a change to a request, the others just have to do a ",{"type":254,"tag":389,"props":37980,"children":37982},{"className":37981},[],[37983],{"type":259,"value":37984},"git pull",{"type":259,"value":37986}," to get the change. We use something standard and we are not directly dependent on a specific tool because HTTP files are supported by several tools.",{"type":254,"tag":255,"props":37988,"children":37989},{},[37990],{"type":259,"value":37991},"I have used REST Client for quite a while now, and I am happy with it: simple but works fine, no need to pay a subscription, it's just a vscode extension to install.",{"type":254,"tag":255,"props":37993,"children":37994},{},[37995,37997,38003],{"type":259,"value":37996},"However, as I have started recently to use more and more Rider, I am also using more and more its built-in HTTP Client which is awesome. If you are using Rider or one of the other JetBrains IDEs I recommend you to try the ",{"type":254,"tag":262,"props":37998,"children":38000},{"href":37788,"rel":37999},[266],[38001],{"type":259,"value":38002},"built-in HTTP Client",{"type":259,"value":38004}," because:",{"type":254,"tag":332,"props":38006,"children":38007},{},[38008,38013,38018,38023,38028],{"type":254,"tag":336,"props":38009,"children":38010},{},[38011],{"type":259,"value":38012},"you don't have to leave your favorite IDE to make HTTP requests to an API",{"type":254,"tag":336,"props":38014,"children":38015},{},[38016],{"type":259,"value":38017},"it supports HTTP requests, gRPC requests, WebSocket requests, GraphQL",{"type":254,"tag":336,"props":38019,"children":38020},{},[38021],{"type":259,"value":38022},"it has many features to help you write the requests like converting cURL requests to the HTTP request format",{"type":254,"tag":336,"props":38024,"children":38025},{},[38026],{"type":259,"value":38027},"it supports environment variables that can be stored in a public environment file (stored in the git repository) or private environment file (local file containing sensitive information that will not be committed to the repository)",{"type":254,"tag":336,"props":38029,"children":38030},{},[38031],{"type":259,"value":38032},"it allows you to write response handler scripts in JavaScript (directly in the HTTP files or in external js files), which opens the door to many possibilities like writing tests",{"type":254,"tag":255,"props":38034,"children":38035},{},[38036,38038,38045],{"type":259,"value":38037},"And the icing on the cake, as you see on the tweet below there is a ",{"type":254,"tag":262,"props":38039,"children":38042},{"href":38040,"rel":38041},"https://github.com/restcli/restcli",[266],[38043],{"type":259,"value":38044},"CLI",{"type":259,"value":38046}," to execute these HTTP files from the command line. It means that if you have written tests for your HTTP requests with the HTTP Client, you can execute them in your CI/CD pipeline. It also means that even without a JetBrains' IDE you can execute HTTP request files.",{"type":254,"tag":31130,"props":38048,"children":38050},{"className":38049},[31133,31134],[38051],{"type":254,"tag":31137,"props":38052,"children":38054},{"id":38053},"1546831245274931201",[],{"type":254,"tag":427,"props":38056,"children":38057},{"icon":567},[38058],{"type":254,"tag":255,"props":38059,"children":38060},{},[38061,38063,38070],{"type":259,"value":38062},"Even if you are using a private environment file to store secrets environments variables, it's often not very convenient to share it with your news colleagues. I wrote ",{"type":254,"tag":262,"props":38064,"children":38067},{"href":38065,"rel":38066},"https://www.techwatching.dev/posts/http-clients-secrets",[266],[38068],{"type":259,"value":38069},"an article",{"type":259,"value":38071}," on the topic that showed how you can store secrets in Azure Key Vault and script their retrieval and the generation of the secret environment file using Azure CLI.",{"type":254,"tag":455,"props":38073,"children":38075},{"id":38074},"in-summary",[38076],{"type":259,"value":38077},"In summary",{"type":254,"tag":255,"props":38079,"children":38080},{},[38081],{"type":259,"value":38082},"I talked about a lot of things but in concrete terms what matters to me when using an API Client is ease of use and collaboration. You may not have exactly the same needs, yet I think these are 2 aspects you should pay attention to when choosing an API Client. As you have read in this article, each API client has its advantages and inconveniences so even if you favor simplicity and collaboration you won't find a perfect tool. So what should you choose? I don't have the answer to that, I think you should try different tools and find what is the most appropriate in your context. But you should definitely not take the first one that comes along just because others use it.",{"title":205,"searchDepth":635,"depth":635,"links":38084},[38085,38086,38091,38096],{"id":1299,"depth":635,"text":1302},{"id":37593,"depth":635,"text":37596,"children":38087},[38088,38089,38090],{"id":37613,"depth":679,"text":37616},{"id":37666,"depth":679,"text":37669},{"id":37690,"depth":679,"text":37693},{"id":37797,"depth":635,"text":37800,"children":38092},[38093,38094,38095],{"id":37817,"depth":679,"text":37820},{"id":37850,"depth":679,"text":37853},{"id":37874,"depth":679,"text":37877},{"id":6186,"depth":635,"text":37936,"children":38097},[38098,38099,38100],{"id":37939,"depth":679,"text":37942},{"id":37970,"depth":679,"text":37973},{"id":38074,"depth":679,"text":38077},"content:1.posts:43.http-clients.md","1.posts/43.http-clients.md",{"_path":133,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":132,"description":38104,"lead":38105,"date":38106,"badge":38107,"image":38108,"tags":38110,"body":38113,"_type":1275,"_id":39607,"_source":231,"_file":39608,"_extension":1278},"In this article, I talked about my latest project: how I built a script to automate the setup of my developer machine using Boxstarter, Chocolatey, Winget, and PowerShell... and how I learned a few things along the way.","A git repository, 2 packages managers, a little bit of scripting, and here is my new environment ready","2022-09-02T00:00:00.000Z",{"label":243},{"src":38109},"/images/laptop_2.jpg",[38111,38112,37214,31105,245],"development box setup","PowerShell",{"type":251,"children":38114,"toc":39594},[38115,38119,38123,38128,38133,38142,38147,38170,38176,38181,38204,38235,38249,38277,38286,38294,38314,38319,38436,38448,38454,38460,38480,38485,38500,38506,38511,38520,38525,38530,38612,38620,38625,38639,38652,38658,38678,38715,38720,38985,39007,39012,39060,39068,39074,39079,39260,39280,39286,39291,39300,39305,39355,39367,39443,39448,39453,39459,39464,39486,39500,39505,39532,39546,39550,39562,39567,39572,39585,39590],{"type":254,"tag":255,"props":38116,"children":38117},{},[38118],{"type":259,"value":38104},{"type":254,"tag":282,"props":38120,"children":38121},{"id":284},[38122],{"type":259,"value":287},{"type":254,"tag":255,"props":38124,"children":38125},{},[38126],{"type":259,"value":38127},"You have probably already faced this situation when you get a new personal or professional laptop, and you have to reinstall all the software you are using daily on this machine. Usually, you lose a few hours to a day to set up your developer environment and not everything is exactly as it was on your previous laptop because you always forget some things.",{"type":254,"tag":255,"props":38129,"children":38130},{},[38131],{"type":259,"value":38132},"It's not even only about installing software but also configuring your environment to meet your habits. And that's important because a properly configured developer environment with the right tools is what makes you productive in your everyday developer life.",{"type":254,"tag":255,"props":38134,"children":38135},{},[38136],{"type":254,"tag":1161,"props":38137,"children":38141},{"alt":38138,"className":38139,"src":38140},"A collection of office supplies laid out on a table.",[1165,1166],"/posts/images/automate_developer_machine_tool.jpg",[],{"type":254,"tag":255,"props":38143,"children":38144},{},[38145],{"type":259,"value":38146},"A lot of people have solved this issue by scripting their developer environment setup, and I decided it was time for me to do the same. And believe me, it was not for the beauty of having everything automatically installed but to stop wasting my time each time I change or reinstall my laptop.",{"type":254,"tag":255,"props":38148,"children":38149},{},[38150,38152,38159,38161,38168],{"type":259,"value":38151},"Please note that what I did to setup my developer machine was inspired by the ",{"type":254,"tag":262,"props":38153,"children":38156},{"href":38154,"rel":38155},"https://github.com/Microsoft/windows-dev-box-setup-scripts",[266],[38157],{"type":259,"value":38158},"Windows Dev Box setup scripts",{"type":259,"value":38160}," GitHub repository and other repositories using boxstarter (check ",{"type":254,"tag":262,"props":38162,"children":38165},{"href":38163,"rel":38164},"https://github.com/laurentkempe/Cacao",[266],[38166],{"type":259,"value":38167},"Laurent Kempé's repository",{"type":259,"value":38169}," for instance) to install their Windows environment.",{"type":254,"tag":282,"props":38171,"children":38173},{"id":38172},"how-does-it-work",[38174],{"type":259,"value":38175},"How does it work?",{"type":254,"tag":255,"props":38177,"children":38178},{},[38179],{"type":259,"value":38180},"There are many ways to automate the setup of a developer machine. Before choosing one, I had a few requirements/preferences:",{"type":254,"tag":332,"props":38182,"children":38183},{},[38184,38189,38194,38199],{"type":254,"tag":336,"props":38185,"children":38186},{},[38187],{"type":259,"value":38188},"installation of my machine should be simple to script and run",{"type":254,"tag":336,"props":38190,"children":38191},{},[38192],{"type":259,"value":38193},"no need to pre-install some software before launching the installation of my environment",{"type":254,"tag":336,"props":38195,"children":38196},{},[38197],{"type":259,"value":38198},"avoid manual steps or user interaction during the installation",{"type":254,"tag":336,"props":38200,"children":38201},{},[38202],{"type":259,"value":38203},"be able easily to share some software configurations between laptops",{"type":254,"tag":255,"props":38205,"children":38206},{},[38207,38209,38216,38218,38224,38226,38233],{"type":259,"value":38208},"Because of these requirements, I chose to use ",{"type":254,"tag":262,"props":38210,"children":38213},{"href":38211,"rel":38212},"https://boxstarter.org/",[266],[38214],{"type":259,"value":38215},"Boxstarter",{"type":259,"value":38217},". It has several interesting features (check the ",{"type":254,"tag":262,"props":38219,"children":38222},{"href":38220,"rel":38221},"https://boxstarter.org/whyboxstarter",[266],[38223],{"type":259,"value":1323},{"type":259,"value":38225},") but the one I like the most is you can launch your installation process directly by ",{"type":254,"tag":262,"props":38227,"children":38230},{"href":38228,"rel":38229},"https://boxstarter.org/weblauncher",[266],[38231],{"type":259,"value":38232},"clicking on a link",{"type":259,"value":38234},". You just need an URL like this one:",{"type":254,"tag":612,"props":38236,"children":38238},{"className":1184,"code":38237,"language":1186,"meta":205,"style":205},"https://boxstarter.org/package/nr/url?{urlToYourInstallationScript}\n",[38239],{"type":254,"tag":389,"props":38240,"children":38241},{"__ignoreMap":205},[38242],{"type":254,"tag":622,"props":38243,"children":38244},{"class":624,"line":625},[38245],{"type":254,"tag":622,"props":38246,"children":38247},{"style":1202},[38248],{"type":259,"value":38237},{"type":254,"tag":255,"props":38250,"children":38251},{},[38252,38253,38259,38261,38268,38270,38276],{"type":259,"value":10970},{"type":254,"tag":389,"props":38254,"children":38256},{"className":38255},[],[38257],{"type":259,"value":38258},"{urlToYourInstallationScript}",{"type":259,"value":38260}," part is the URL where is stored your installation script. I put mine in a ",{"type":254,"tag":262,"props":38262,"children":38265},{"href":38263,"rel":38264},"https://github.com/TechWatching/dotfiles/blob/main/boxstarter.ps1",[266],[38266],{"type":259,"value":38267},"public GitHub repository",{"type":259,"value":38269}," called ",{"type":254,"tag":389,"props":38271,"children":38273},{"className":38272},[],[38274],{"type":259,"value":38275},"dotfiles",{"type":259,"value":594},{"type":254,"tag":255,"props":38278,"children":38279},{},[38280],{"type":254,"tag":1161,"props":38281,"children":38285},{"alt":38282,"className":38283,"src":38284},"Boxstarter website.",[1165,1166],"/posts/images/automate_developer_machine_boxstarter.png",[],{"type":254,"tag":427,"props":38287,"children":38288},{"icon":429},[38289],{"type":254,"tag":255,"props":38290,"children":38291},{},[38292],{"type":259,"value":38293},"Boxstarter was a nice discovery, I barely scratched the surface but it can do interesting things like remote installations.",{"type":254,"tag":255,"props":38295,"children":38296},{},[38297,38299,38305,38307,38312],{"type":259,"value":38298},"In this repository, I also put different scripts (to separate the different steps instead of having one big installation script file), some config files I wanted to use in my setup (my ",{"type":254,"tag":389,"props":38300,"children":38302},{"className":38301},[],[38303],{"type":259,"value":38304},".gitconfig",{"type":259,"value":38306}," file for instance), and a readme with some explanations and the link to launch the installation. I did not reinvent the wheel as it's more or less how the ",{"type":254,"tag":262,"props":38308,"children":38310},{"href":38154,"rel":38309},[266],[38311],{"type":259,"value":38158},{"type":259,"value":38313}," work.",{"type":254,"tag":255,"props":38315,"children":38316},{},[38317],{"type":259,"value":38318},"Because I wanted to share some software configurations between laptops (or between installations), using a GitHub repository is interesting. The first thing my setup script does is to install Git, then it clones this repository to have access to the other scripts and configuration files.",{"type":254,"tag":612,"props":38320,"children":38322},{"className":10656,"code":38321,"language":10658,"meta":205,"style":205},"# Install git and clone repository containing scripts and config files\nchoco install -y git --params \"/GitOnlyOnPath /NoShellIntegration /WindowsTerminal\"\nRefreshEnv\ngit clone https://github.com/TechWatching/dotfiles.git \"$env:USERPROFILE\\dotfiles\"\n",[38323],{"type":254,"tag":389,"props":38324,"children":38325},{"__ignoreMap":205},[38326,38334,38373,38381],{"type":254,"tag":622,"props":38327,"children":38328},{"class":624,"line":625},[38329],{"type":254,"tag":622,"props":38330,"children":38331},{"style":3195},[38332],{"type":259,"value":38333},"# Install git and clone repository containing scripts and config files\n",{"type":254,"tag":622,"props":38335,"children":38336},{"class":624,"line":635},[38337,38342,38346,38351,38355,38360,38364,38369],{"type":254,"tag":622,"props":38338,"children":38339},{"style":1202},[38340],{"type":259,"value":38341},"choco install ",{"type":254,"tag":622,"props":38343,"children":38344},{"style":629},[38345],{"type":259,"value":17065},{"type":254,"tag":622,"props":38347,"children":38348},{"style":1202},[38349],{"type":259,"value":38350},"y git ",{"type":254,"tag":622,"props":38352,"children":38353},{"style":629},[38354],{"type":259,"value":10675},{"type":254,"tag":622,"props":38356,"children":38357},{"style":1202},[38358],{"type":259,"value":38359},"params ",{"type":254,"tag":622,"props":38361,"children":38362},{"style":629},[38363],{"type":259,"value":652},{"type":254,"tag":622,"props":38365,"children":38366},{"style":664},[38367],{"type":259,"value":38368},"/GitOnlyOnPath /NoShellIntegration /WindowsTerminal",{"type":254,"tag":622,"props":38370,"children":38371},{"style":629},[38372],{"type":259,"value":9909},{"type":254,"tag":622,"props":38374,"children":38375},{"class":624,"line":679},[38376],{"type":254,"tag":622,"props":38377,"children":38378},{"style":1202},[38379],{"type":259,"value":38380},"RefreshEnv\n",{"type":254,"tag":622,"props":38382,"children":38383},{"class":624,"line":705},[38384,38389,38394,38399,38403,38408,38412,38417,38422,38427,38432],{"type":254,"tag":622,"props":38385,"children":38386},{"style":1202},[38387],{"type":259,"value":38388},"git clone https:",{"type":254,"tag":622,"props":38390,"children":38391},{"style":629},[38392],{"type":259,"value":38393},"//",{"type":254,"tag":622,"props":38395,"children":38396},{"style":1735},[38397],{"type":259,"value":38398},"github.com",{"type":254,"tag":622,"props":38400,"children":38401},{"style":629},[38402],{"type":259,"value":9768},{"type":254,"tag":622,"props":38404,"children":38405},{"style":1202},[38406],{"type":259,"value":38407},"TechWatching",{"type":254,"tag":622,"props":38409,"children":38410},{"style":629},[38411],{"type":259,"value":9768},{"type":254,"tag":622,"props":38413,"children":38414},{"style":1202},[38415],{"type":259,"value":38416},"dotfiles.git ",{"type":254,"tag":622,"props":38418,"children":38419},{"style":629},[38420],{"type":259,"value":38421},"\"$",{"type":254,"tag":622,"props":38423,"children":38424},{"style":1202},[38425],{"type":259,"value":38426},"env:USERPROFILE",{"type":254,"tag":622,"props":38428,"children":38429},{"style":664},[38430],{"type":259,"value":38431},"\\dotfiles",{"type":254,"tag":622,"props":38433,"children":38434},{"style":629},[38435],{"type":259,"value":9909},{"type":254,"tag":255,"props":38437,"children":38438},{},[38439,38441,38447],{"type":259,"value":38440},"The scripts are written in ",{"type":254,"tag":262,"props":38442,"children":38445},{"href":38443,"rel":38444},"https://docs.microsoft.com/en-us/powershell/",[266],[38446],{"type":259,"value":38112},{"type":259,"value":594},{"type":254,"tag":282,"props":38449,"children":38451},{"id":38450},"installing-software",[38452],{"type":259,"value":38453},"Installing software",{"type":254,"tag":455,"props":38455,"children":38457},{"id":38456},"package-managers",[38458],{"type":259,"value":38459},"Package managers",{"type":254,"tag":255,"props":38461,"children":38462},{},[38463,38465,38471,38472,38479],{"type":259,"value":38464},"The easiest way to automate installing, upgrading, and configuring software is to use package managers. There are several available on Windows but I chose to use ",{"type":254,"tag":262,"props":38466,"children":38469},{"href":38467,"rel":38468},"https://docs.microsoft.com/en-us/windows/package-manager/",[266],[38470],{"type":259,"value":37235},{"type":259,"value":10347},{"type":254,"tag":262,"props":38473,"children":38476},{"href":38474,"rel":38475},"https://chocolatey.org/",[266],[38477],{"type":259,"value":38478},"Chocolatey",{"type":259,"value":594},{"type":254,"tag":255,"props":38481,"children":38482},{},[38483],{"type":259,"value":38484},"Windows Package Manager (aka winget) is relatively new so has fewer features and packages available than other package managers like Chocolatey. However it's very promising, it's getting better every day, it's built-in Windows, and above all, it supports installing Microsoft Store apps. So I chose to use winget by default and fallback to Chocolatey when a package I needed was not available or not up-to-date in winget.",{"type":254,"tag":427,"props":38486,"children":38487},{"icon":429},[38488],{"type":254,"tag":255,"props":38489,"children":38490},{},[38491,38493,38498],{"type":259,"value":38492},"I learned a few things about winget while starting to use it. You can find my articles talking about it ",{"type":254,"tag":262,"props":38494,"children":38496},{"href":37265,"rel":38495},[266],[38497],{"type":259,"value":1414},{"type":259,"value":38499}," if you are interested.",{"type":254,"tag":455,"props":38501,"children":38503},{"id":38502},"installing-integrated-development-environments-ides",[38504],{"type":259,"value":38505},"Installing Integrated Development Environments (IDEs)",{"type":254,"tag":255,"props":38507,"children":38508},{},[38509],{"type":259,"value":38510},"Visual Studio has been my main IDE for a while, but I have been started recently to use more and more Rider as well. Both are nice IDEs with many features particularly well suited for .NET development which is what I mostly do. I have been a Visual Studio Code long-time user too, mostly using it as a text editor or for front development. I also use vscode to work with Azure because it has nice extensions for a lot of Azure services.",{"type":254,"tag":255,"props":38512,"children":38513},{},[38514],{"type":254,"tag":1161,"props":38515,"children":38519},{"alt":38516,"className":38517,"src":38518},"Visual Studio logo.",[1165,1166],"/posts/images/automate_developer_machine_visualstudio.jpg",[],{"type":254,"tag":255,"props":38521,"children":38522},{},[38523],{"type":259,"value":38524},"You probably want to customize your Visual Studio installation to only include the workloads, components, and language packs you are using. You can specify them as parameters in the winget or chocolatey install command but that's a bit cumbersome. The easiest way is to export your configuration from the Visual Studio installer.",{"type":254,"tag":255,"props":38526,"children":38527},{},[38528],{"type":259,"value":38529},"You can then install Visual Studio with this kind of command:",{"type":254,"tag":612,"props":38531,"children":38533},{"className":10656,"code":38532,"language":10658,"meta":205,"style":205},"winget install -e -h --id Microsoft.VisualStudio.2022.Enterprise --silent --override \"--wait --quiet --addProductLang En-us --config .vsconfig\"\n",[38534],{"type":254,"tag":389,"props":38535,"children":38536},{"__ignoreMap":205},[38537],{"type":254,"tag":622,"props":38538,"children":38539},{"class":624,"line":625},[38540,38544,38548,38553,38557,38562,38566,38571,38576,38581,38585,38590,38594,38599,38603,38608],{"type":254,"tag":622,"props":38541,"children":38542},{"style":1202},[38543],{"type":259,"value":10670},{"type":254,"tag":622,"props":38545,"children":38546},{"style":629},[38547],{"type":259,"value":17065},{"type":254,"tag":622,"props":38549,"children":38550},{"style":1202},[38551],{"type":259,"value":38552},"e ",{"type":254,"tag":622,"props":38554,"children":38555},{"style":629},[38556],{"type":259,"value":17065},{"type":254,"tag":622,"props":38558,"children":38559},{"style":1202},[38560],{"type":259,"value":38561},"h ",{"type":254,"tag":622,"props":38563,"children":38564},{"style":629},[38565],{"type":259,"value":10675},{"type":254,"tag":622,"props":38567,"children":38568},{"style":1202},[38569],{"type":259,"value":38570},"id Microsoft.VisualStudio.",{"type":254,"tag":622,"props":38572,"children":38573},{"style":741},[38574],{"type":259,"value":38575},"2022.",{"type":254,"tag":622,"props":38577,"children":38578},{"style":1202},[38579],{"type":259,"value":38580},"Enterprise ",{"type":254,"tag":622,"props":38582,"children":38583},{"style":629},[38584],{"type":259,"value":10675},{"type":254,"tag":622,"props":38586,"children":38587},{"style":1202},[38588],{"type":259,"value":38589},"silent ",{"type":254,"tag":622,"props":38591,"children":38592},{"style":629},[38593],{"type":259,"value":10675},{"type":254,"tag":622,"props":38595,"children":38596},{"style":1202},[38597],{"type":259,"value":38598},"override ",{"type":254,"tag":622,"props":38600,"children":38601},{"style":629},[38602],{"type":259,"value":652},{"type":254,"tag":622,"props":38604,"children":38605},{"style":664},[38606],{"type":259,"value":38607},"--wait --quiet --addProductLang En-us --config .vsconfig",{"type":254,"tag":622,"props":38609,"children":38610},{"style":629},[38611],{"type":259,"value":9909},{"type":254,"tag":427,"props":38613,"children":38614},{"icon":429},[38615],{"type":254,"tag":255,"props":38616,"children":38617},{},[38618],{"type":259,"value":38619},"I did not know before trying to automate Visual Studio installation that you could specify parameters to its installer on the command line but that's very useful.",{"type":254,"tag":255,"props":38621,"children":38622},{},[38623],{"type":259,"value":38624},"I don't export Visual Studio settings or extensions as most of them can be synchronized through the personalization account (you just have to sign in to Visual Studio with the same account).",{"type":254,"tag":255,"props":38626,"children":38627},{},[38628,38630,38637],{"type":259,"value":38629},"In a similar way, I use ",{"type":254,"tag":262,"props":38631,"children":38634},{"href":38632,"rel":38633},"https://code.visualstudio.com/docs/editor/settings-sync",[266],[38635],{"type":259,"value":38636},"vscode built-in settings synchronization",{"type":259,"value":38638}," to share my setup (extensions, settings, keybindings) across machines so I don't have to do a custom installation.",{"type":254,"tag":255,"props":38640,"children":38641},{},[38642,38644,38650],{"type":259,"value":38643},"There is a Rider package available on chocolatey but it is not always up-to-date and I prefer to use the JetBrains ToolBox application to manage the installation and update of my JetBrains tools. Unfortunately, JetBrains ToolBox does not currently have a CLI to automate the installation of Rider and other useful .NET tools (there is an ",{"type":254,"tag":262,"props":38645,"children":38648},{"href":38646,"rel":38647},"https://youtrack.jetbrains.com/issue/TBX-653/toolbox-cli",[266],[38649],{"type":259,"value":8780},{"type":259,"value":38651}," opened though if you want to vote) so I only automated the installation of JetBrains ToolBox and will have to install it Rider manually from it.",{"type":254,"tag":455,"props":38653,"children":38655},{"id":38654},"tools-i-need",[38656],{"type":259,"value":38657},"Tools I need",{"type":254,"tag":255,"props":38659,"children":38660},{},[38661,38663,38669,38671,38676],{"type":259,"value":38662},"Of course, git is a must-have. I use ",{"type":254,"tag":262,"props":38664,"children":38666},{"href":32160,"rel":38665},[266],[38667],{"type":259,"value":38668},"NVM for windows",{"type":259,"value":38670}," to manage multiple installations of node.js on my laptop, and ",{"type":254,"tag":262,"props":38672,"children":38674},{"href":32016,"rel":38673},[266],[38675],{"type":259,"value":24281},{"type":259,"value":38677}," is my preferred package manager for Node.js.",{"type":254,"tag":255,"props":38679,"children":38680},{},[38681,38683,38689,38690,38695,38697,38704,38706,38713],{"type":259,"value":38682},"There are 2 shells I like to use (both of them are cross-platform): ",{"type":254,"tag":262,"props":38684,"children":38687},{"href":38685,"rel":38686},"https://docs.microsoft.com/en-us/powershell/scripting/overview",[266],[38688],{"type":259,"value":38112},{"type":259,"value":10347},{"type":254,"tag":262,"props":38691,"children":38693},{"href":23650,"rel":38692},[266],[38694],{"type":259,"value":23654},{"type":259,"value":38696},". I use ",{"type":254,"tag":262,"props":38698,"children":38701},{"href":38699,"rel":38700},"https://ohmyposh.dev/",[266],[38702],{"type":259,"value":38703},"Oh My Posh",{"type":259,"value":38705}," to configure an enhanced prompt for both shells. ",{"type":254,"tag":262,"props":38707,"children":38710},{"href":38708,"rel":38709},"https://docs.microsoft.com/en-us/windows/terminal/",[266],[38711],{"type":259,"value":38712},"Windows Terminal",{"type":259,"value":38714}," is my go-to terminal.",{"type":254,"tag":255,"props":38716,"children":38717},{},[38718],{"type":259,"value":38719},"As I work mainly on cloud projects, there are some Azure tools I need on my laptop:",{"type":254,"tag":612,"props":38721,"children":38723},{"className":10656,"code":38722,"language":10658,"meta":205,"style":205},"winget install -e -h --id Microsoft.AzureCLI\nwinget install -e -h --id Microsoft.AzureCosmosEmulator\nwinget install -e -h --id Microsoft.AzureDataStudio\nwinget install -e -h --id Microsoft.azure-iot-explorer\nwinget install -e -h --id Microsoft.AzureStorageExplorer\nwinget install -e -h --id Pulumi.Pulumi\nwinget install -e -h --id Microsoft.AzureFunctionsCoreTools\n# Azurite can be installed through vscode extension or as a global npm package\n# pnpm add -g azurite\n",[38724],{"type":254,"tag":389,"props":38725,"children":38726},{"__ignoreMap":205},[38727,38759,38791,38823,38873,38905,38937,38969,38977],{"type":254,"tag":622,"props":38728,"children":38729},{"class":624,"line":625},[38730,38734,38738,38742,38746,38750,38754],{"type":254,"tag":622,"props":38731,"children":38732},{"style":1202},[38733],{"type":259,"value":10670},{"type":254,"tag":622,"props":38735,"children":38736},{"style":629},[38737],{"type":259,"value":17065},{"type":254,"tag":622,"props":38739,"children":38740},{"style":1202},[38741],{"type":259,"value":38552},{"type":254,"tag":622,"props":38743,"children":38744},{"style":629},[38745],{"type":259,"value":17065},{"type":254,"tag":622,"props":38747,"children":38748},{"style":1202},[38749],{"type":259,"value":38561},{"type":254,"tag":622,"props":38751,"children":38752},{"style":629},[38753],{"type":259,"value":10675},{"type":254,"tag":622,"props":38755,"children":38756},{"style":1202},[38757],{"type":259,"value":38758},"id Microsoft.AzureCLI\n",{"type":254,"tag":622,"props":38760,"children":38761},{"class":624,"line":635},[38762,38766,38770,38774,38778,38782,38786],{"type":254,"tag":622,"props":38763,"children":38764},{"style":1202},[38765],{"type":259,"value":10670},{"type":254,"tag":622,"props":38767,"children":38768},{"style":629},[38769],{"type":259,"value":17065},{"type":254,"tag":622,"props":38771,"children":38772},{"style":1202},[38773],{"type":259,"value":38552},{"type":254,"tag":622,"props":38775,"children":38776},{"style":629},[38777],{"type":259,"value":17065},{"type":254,"tag":622,"props":38779,"children":38780},{"style":1202},[38781],{"type":259,"value":38561},{"type":254,"tag":622,"props":38783,"children":38784},{"style":629},[38785],{"type":259,"value":10675},{"type":254,"tag":622,"props":38787,"children":38788},{"style":1202},[38789],{"type":259,"value":38790},"id Microsoft.AzureCosmosEmulator\n",{"type":254,"tag":622,"props":38792,"children":38793},{"class":624,"line":679},[38794,38798,38802,38806,38810,38814,38818],{"type":254,"tag":622,"props":38795,"children":38796},{"style":1202},[38797],{"type":259,"value":10670},{"type":254,"tag":622,"props":38799,"children":38800},{"style":629},[38801],{"type":259,"value":17065},{"type":254,"tag":622,"props":38803,"children":38804},{"style":1202},[38805],{"type":259,"value":38552},{"type":254,"tag":622,"props":38807,"children":38808},{"style":629},[38809],{"type":259,"value":17065},{"type":254,"tag":622,"props":38811,"children":38812},{"style":1202},[38813],{"type":259,"value":38561},{"type":254,"tag":622,"props":38815,"children":38816},{"style":629},[38817],{"type":259,"value":10675},{"type":254,"tag":622,"props":38819,"children":38820},{"style":1202},[38821],{"type":259,"value":38822},"id Microsoft.AzureDataStudio\n",{"type":254,"tag":622,"props":38824,"children":38825},{"class":624,"line":705},[38826,38830,38834,38838,38842,38846,38850,38855,38859,38864,38868],{"type":254,"tag":622,"props":38827,"children":38828},{"style":1202},[38829],{"type":259,"value":10670},{"type":254,"tag":622,"props":38831,"children":38832},{"style":629},[38833],{"type":259,"value":17065},{"type":254,"tag":622,"props":38835,"children":38836},{"style":1202},[38837],{"type":259,"value":38552},{"type":254,"tag":622,"props":38839,"children":38840},{"style":629},[38841],{"type":259,"value":17065},{"type":254,"tag":622,"props":38843,"children":38844},{"style":1202},[38845],{"type":259,"value":38561},{"type":254,"tag":622,"props":38847,"children":38848},{"style":629},[38849],{"type":259,"value":10675},{"type":254,"tag":622,"props":38851,"children":38852},{"style":1202},[38853],{"type":259,"value":38854},"id Microsoft.azure",{"type":254,"tag":622,"props":38856,"children":38857},{"style":629},[38858],{"type":259,"value":17065},{"type":254,"tag":622,"props":38860,"children":38861},{"style":1202},[38862],{"type":259,"value":38863},"iot",{"type":254,"tag":622,"props":38865,"children":38866},{"style":629},[38867],{"type":259,"value":17065},{"type":254,"tag":622,"props":38869,"children":38870},{"style":1202},[38871],{"type":259,"value":38872},"explorer\n",{"type":254,"tag":622,"props":38874,"children":38875},{"class":624,"line":732},[38876,38880,38884,38888,38892,38896,38900],{"type":254,"tag":622,"props":38877,"children":38878},{"style":1202},[38879],{"type":259,"value":10670},{"type":254,"tag":622,"props":38881,"children":38882},{"style":629},[38883],{"type":259,"value":17065},{"type":254,"tag":622,"props":38885,"children":38886},{"style":1202},[38887],{"type":259,"value":38552},{"type":254,"tag":622,"props":38889,"children":38890},{"style":629},[38891],{"type":259,"value":17065},{"type":254,"tag":622,"props":38893,"children":38894},{"style":1202},[38895],{"type":259,"value":38561},{"type":254,"tag":622,"props":38897,"children":38898},{"style":629},[38899],{"type":259,"value":10675},{"type":254,"tag":622,"props":38901,"children":38902},{"style":1202},[38903],{"type":259,"value":38904},"id Microsoft.AzureStorageExplorer\n",{"type":254,"tag":622,"props":38906,"children":38907},{"class":624,"line":759},[38908,38912,38916,38920,38924,38928,38932],{"type":254,"tag":622,"props":38909,"children":38910},{"style":1202},[38911],{"type":259,"value":10670},{"type":254,"tag":622,"props":38913,"children":38914},{"style":629},[38915],{"type":259,"value":17065},{"type":254,"tag":622,"props":38917,"children":38918},{"style":1202},[38919],{"type":259,"value":38552},{"type":254,"tag":622,"props":38921,"children":38922},{"style":629},[38923],{"type":259,"value":17065},{"type":254,"tag":622,"props":38925,"children":38926},{"style":1202},[38927],{"type":259,"value":38561},{"type":254,"tag":622,"props":38929,"children":38930},{"style":629},[38931],{"type":259,"value":10675},{"type":254,"tag":622,"props":38933,"children":38934},{"style":1202},[38935],{"type":259,"value":38936},"id Pulumi.Pulumi\n",{"type":254,"tag":622,"props":38938,"children":38939},{"class":624,"line":798},[38940,38944,38948,38952,38956,38960,38964],{"type":254,"tag":622,"props":38941,"children":38942},{"style":1202},[38943],{"type":259,"value":10670},{"type":254,"tag":622,"props":38945,"children":38946},{"style":629},[38947],{"type":259,"value":17065},{"type":254,"tag":622,"props":38949,"children":38950},{"style":1202},[38951],{"type":259,"value":38552},{"type":254,"tag":622,"props":38953,"children":38954},{"style":629},[38955],{"type":259,"value":17065},{"type":254,"tag":622,"props":38957,"children":38958},{"style":1202},[38959],{"type":259,"value":38561},{"type":254,"tag":622,"props":38961,"children":38962},{"style":629},[38963],{"type":259,"value":10675},{"type":254,"tag":622,"props":38965,"children":38966},{"style":1202},[38967],{"type":259,"value":38968},"id Microsoft.AzureFunctionsCoreTools\n",{"type":254,"tag":622,"props":38970,"children":38971},{"class":624,"line":836},[38972],{"type":254,"tag":622,"props":38973,"children":38974},{"style":3195},[38975],{"type":259,"value":38976},"# Azurite can be installed through vscode extension or as a global npm package\n",{"type":254,"tag":622,"props":38978,"children":38979},{"class":624,"line":862},[38980],{"type":254,"tag":622,"props":38981,"children":38982},{"style":3195},[38983],{"type":259,"value":38984},"# pnpm add -g azurite\n",{"type":254,"tag":255,"props":38986,"children":38987},{},[38988,38990,38997,38998,39005],{"type":259,"value":38989},"My script also automates the installation of other tools, not related to software development but that I often use (like ",{"type":254,"tag":262,"props":38991,"children":38994},{"href":38992,"rel":38993},"https://www.7-zip.org/",[266],[38995],{"type":259,"value":38996},"7zip",{"type":259,"value":16280},{"type":254,"tag":262,"props":38999,"children":39002},{"href":39000,"rel":39001},"https://docs.microsoft.com/en-us/windows/powertoys/",[266],[39003],{"type":259,"value":39004},"Microsoft Power Toys",{"type":259,"value":39006}," or just browsers).",{"type":254,"tag":255,"props":39008,"children":39009},{},[39010],{"type":259,"value":39011},"Generally, when the applications I want to install are available in the Microsoft Store, I specify winget to install them from there.",{"type":254,"tag":612,"props":39013,"children":39015},{"className":10656,"code":39014,"language":10658,"meta":205,"style":205},"winget install -e -h --id Microsoft.WindowsTerminal -s msstore\n",[39016],{"type":254,"tag":389,"props":39017,"children":39018},{"__ignoreMap":205},[39019],{"type":254,"tag":622,"props":39020,"children":39021},{"class":624,"line":625},[39022,39026,39030,39034,39038,39042,39046,39051,39055],{"type":254,"tag":622,"props":39023,"children":39024},{"style":1202},[39025],{"type":259,"value":10670},{"type":254,"tag":622,"props":39027,"children":39028},{"style":629},[39029],{"type":259,"value":17065},{"type":254,"tag":622,"props":39031,"children":39032},{"style":1202},[39033],{"type":259,"value":38552},{"type":254,"tag":622,"props":39035,"children":39036},{"style":629},[39037],{"type":259,"value":17065},{"type":254,"tag":622,"props":39039,"children":39040},{"style":1202},[39041],{"type":259,"value":38561},{"type":254,"tag":622,"props":39043,"children":39044},{"style":629},[39045],{"type":259,"value":10675},{"type":254,"tag":622,"props":39047,"children":39048},{"style":1202},[39049],{"type":259,"value":39050},"id Microsoft.WindowsTerminal ",{"type":254,"tag":622,"props":39052,"children":39053},{"style":629},[39054],{"type":259,"value":17065},{"type":254,"tag":622,"props":39056,"children":39057},{"style":1202},[39058],{"type":259,"value":39059},"s msstore\n",{"type":254,"tag":427,"props":39061,"children":39062},{"icon":429},[39063],{"type":254,"tag":255,"props":39064,"children":39065},{},[39066],{"type":259,"value":39067},"Automating the installation of my developer machine was the opportunity to realize all the tools I was using daily, and which ones were really useful.",{"type":254,"tag":455,"props":39069,"children":39071},{"id":39070},"tools-i-dont-need",[39072],{"type":259,"value":39073},"Tools I don't need",{"type":254,"tag":255,"props":39075,"children":39076},{},[39077],{"type":259,"value":39078},"Unnecessary applications that come with Windows out of the box are uninstalled using this function:",{"type":254,"tag":612,"props":39080,"children":39082},{"className":10656,"code":39081,"language":10658,"meta":205,"style":205},"function removeApp {\n    Param ([string]$appName)\n    Write-Output \"Trying to remove $appName\"\n    Get-AppxPackage $appName -AllUsers | Remove-AppxPackage\n    Get-AppXProvisionedPackage -Online | Where DisplayName -like $appName | Remove-AppxProvisionedPackage -Online\n",[39083],{"type":254,"tag":389,"props":39084,"children":39085},{"__ignoreMap":205},[39086,39102,39133,39162,39198],{"type":254,"tag":622,"props":39087,"children":39088},{"class":624,"line":625},[39089,39093,39098],{"type":254,"tag":622,"props":39090,"children":39091},{"style":644},[39092],{"type":259,"value":21389},{"type":254,"tag":622,"props":39094,"children":39095},{"style":1735},[39096],{"type":259,"value":39097}," removeApp",{"type":254,"tag":622,"props":39099,"children":39100},{"style":629},[39101],{"type":259,"value":702},{"type":254,"tag":622,"props":39103,"children":39104},{"class":624,"line":635},[39105,39110,39115,39119,39124,39129],{"type":254,"tag":622,"props":39106,"children":39107},{"style":1196},[39108],{"type":259,"value":39109},"    Param",{"type":254,"tag":622,"props":39111,"children":39112},{"style":629},[39113],{"type":259,"value":39114}," ([",{"type":254,"tag":622,"props":39116,"children":39117},{"style":644},[39118],{"type":259,"value":2851},{"type":254,"tag":622,"props":39120,"children":39121},{"style":629},[39122],{"type":259,"value":39123},"]$",{"type":254,"tag":622,"props":39125,"children":39126},{"style":1202},[39127],{"type":259,"value":39128},"appName",{"type":254,"tag":622,"props":39130,"children":39131},{"style":629},[39132],{"type":259,"value":1685},{"type":254,"tag":622,"props":39134,"children":39135},{"class":624,"line":679},[39136,39141,39145,39150,39154,39158],{"type":254,"tag":622,"props":39137,"children":39138},{"style":1735},[39139],{"type":259,"value":39140},"    Write-Output",{"type":254,"tag":622,"props":39142,"children":39143},{"style":629},[39144],{"type":259,"value":661},{"type":254,"tag":622,"props":39146,"children":39147},{"style":664},[39148],{"type":259,"value":39149},"Trying to remove ",{"type":254,"tag":622,"props":39151,"children":39152},{"style":629},[39153],{"type":259,"value":16922},{"type":254,"tag":622,"props":39155,"children":39156},{"style":1202},[39157],{"type":259,"value":39128},{"type":254,"tag":622,"props":39159,"children":39160},{"style":629},[39161],{"type":259,"value":9909},{"type":254,"tag":622,"props":39163,"children":39164},{"class":624,"line":705},[39165,39170,39175,39180,39184,39189,39193],{"type":254,"tag":622,"props":39166,"children":39167},{"style":1735},[39168],{"type":259,"value":39169},"    Get-AppxPackage",{"type":254,"tag":622,"props":39171,"children":39172},{"style":629},[39173],{"type":259,"value":39174}," $",{"type":254,"tag":622,"props":39176,"children":39177},{"style":1202},[39178],{"type":259,"value":39179},"appName ",{"type":254,"tag":622,"props":39181,"children":39182},{"style":629},[39183],{"type":259,"value":17065},{"type":254,"tag":622,"props":39185,"children":39186},{"style":1202},[39187],{"type":259,"value":39188},"AllUsers ",{"type":254,"tag":622,"props":39190,"children":39191},{"style":629},[39192],{"type":259,"value":17875},{"type":254,"tag":622,"props":39194,"children":39195},{"style":1735},[39196],{"type":259,"value":39197}," Remove-AppxPackage\n",{"type":254,"tag":622,"props":39199,"children":39200},{"class":624,"line":732},[39201,39206,39210,39215,39219,39224,39229,39234,39238,39242,39246,39251,39255],{"type":254,"tag":622,"props":39202,"children":39203},{"style":1735},[39204],{"type":259,"value":39205},"    Get-AppXProvisionedPackage",{"type":254,"tag":622,"props":39207,"children":39208},{"style":629},[39209],{"type":259,"value":10733},{"type":254,"tag":622,"props":39211,"children":39212},{"style":1202},[39213],{"type":259,"value":39214},"Online ",{"type":254,"tag":622,"props":39216,"children":39217},{"style":629},[39218],{"type":259,"value":17875},{"type":254,"tag":622,"props":39220,"children":39221},{"style":1196},[39222],{"type":259,"value":39223}," Where",{"type":254,"tag":622,"props":39225,"children":39226},{"style":1202},[39227],{"type":259,"value":39228}," DisplayName ",{"type":254,"tag":622,"props":39230,"children":39231},{"style":629},[39232],{"type":259,"value":39233},"-like",{"type":254,"tag":622,"props":39235,"children":39236},{"style":629},[39237],{"type":259,"value":39174},{"type":254,"tag":622,"props":39239,"children":39240},{"style":1202},[39241],{"type":259,"value":39179},{"type":254,"tag":622,"props":39243,"children":39244},{"style":629},[39245],{"type":259,"value":17875},{"type":254,"tag":622,"props":39247,"children":39248},{"style":1735},[39249],{"type":259,"value":39250}," Remove-AppxProvisionedPackage",{"type":254,"tag":622,"props":39252,"children":39253},{"style":629},[39254],{"type":259,"value":10733},{"type":254,"tag":622,"props":39256,"children":39257},{"style":1202},[39258],{"type":259,"value":39259},"Online\n",{"type":254,"tag":255,"props":39261,"children":39262},{},[39263,39265,39271,39273,39279],{"type":259,"value":39264},"It allows me to get rid of pre-installed applications like ",{"type":254,"tag":389,"props":39266,"children":39268},{"className":39267},[],[39269],{"type":259,"value":39270},"Facebook",{"type":259,"value":39272},"or ",{"type":254,"tag":389,"props":39274,"children":39276},{"className":39275},[],[39277],{"type":259,"value":39278},"Bing News",{"type":259,"value":594},{"type":254,"tag":282,"props":39281,"children":39283},{"id":39282},"configuring-software",[39284],{"type":259,"value":39285},"Configuring software",{"type":254,"tag":255,"props":39287,"children":39288},{},[39289],{"type":259,"value":39290},"Installing software is one part, but configuring it is another. As I previously mentioned, for some applications in which you sign in with an account, settings are synchronized so you don't need to transfer the application settings from your previous laptop. For the others, I decided to use symbolic links.",{"type":254,"tag":255,"props":39292,"children":39293},{},[39294],{"type":254,"tag":1161,"props":39295,"children":39299},{"alt":39296,"className":39297,"src":39298},"A light bulb that is turned on.",[1165,1166],"/posts/images/automate_developer_machine_idea.jpg",[],{"type":254,"tag":255,"props":39301,"children":39302},{},[39303],{"type":259,"value":39304},"Here are some examples of settings I wanted to set up on a new laptop:",{"type":254,"tag":332,"props":39306,"children":39307},{},[39308,39320,39331,39342],{"type":254,"tag":336,"props":39309,"children":39310},{},[39311,39313],{"type":259,"value":39312},"my ",{"type":254,"tag":262,"props":39314,"children":39317},{"href":39315,"rel":39316},"https://github.com/TechWatching/dotfiles/blob/main/config/git/.gitconfig",[266],[39318],{"type":259,"value":39319},".gitconfig file",{"type":254,"tag":336,"props":39321,"children":39322},{},[39323,39324],{"type":259,"value":39312},{"type":254,"tag":262,"props":39325,"children":39328},{"href":39326,"rel":39327},"https://github.com/TechWatching/dotfiles/blob/main/config/windowsTerminal/settings.json",[266],[39329],{"type":259,"value":39330},"Windows Terminal settings",{"type":254,"tag":336,"props":39332,"children":39333},{},[39334,39335],{"type":259,"value":39312},{"type":254,"tag":262,"props":39336,"children":39339},{"href":39337,"rel":39338},"https://github.com/TechWatching/dotfiles/blob/main/config/powershell/Microsoft.PowerShell_profile.ps1",[266],[39340],{"type":259,"value":39341},"PowerShell profile",{"type":254,"tag":336,"props":39343,"children":39344},{},[39345,39346,39353],{"type":259,"value":39312},{"type":254,"tag":262,"props":39347,"children":39350},{"href":39348,"rel":39349},"https://github.com/TechWatching/dotfiles/blob/main/config/prompt/.oh-my-posh.omp.json",[266],[39351],{"type":259,"value":39352},"custom prompt configuration",{"type":259,"value":39354}," done using oh my posh",{"type":254,"tag":255,"props":39356,"children":39357},{},[39358,39360,39365],{"type":259,"value":39359},"To create a symbolic link at the Windows Terminal settings file pointing to the settings file in my ",{"type":254,"tag":389,"props":39361,"children":39363},{"className":39362},[],[39364],{"type":259,"value":38275},{"type":259,"value":39366}," cloned repository I can do the following command:",{"type":254,"tag":612,"props":39368,"children":39370},{"className":10656,"code":39369,"language":10658,"meta":205,"style":205},"New-Item -ItemType SymbolicLink -Path \"$env:USERPROFILE\\AppData\\Local\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json\" -Target \"$env:USERPROFILE\\dotfiles\\config\\windowsTerminal\\settings.json\"\"\n",[39371],{"type":254,"tag":389,"props":39372,"children":39373},{"__ignoreMap":205},[39374],{"type":254,"tag":622,"props":39375,"children":39376},{"class":624,"line":625},[39377,39382,39386,39391,39395,39400,39404,39408,39413,39417,39421,39425,39429,39433,39438],{"type":254,"tag":622,"props":39378,"children":39379},{"style":1735},[39380],{"type":259,"value":39381},"New-Item",{"type":254,"tag":622,"props":39383,"children":39384},{"style":629},[39385],{"type":259,"value":10733},{"type":254,"tag":622,"props":39387,"children":39388},{"style":1202},[39389],{"type":259,"value":39390},"ItemType SymbolicLink ",{"type":254,"tag":622,"props":39392,"children":39393},{"style":629},[39394],{"type":259,"value":17065},{"type":254,"tag":622,"props":39396,"children":39397},{"style":1202},[39398],{"type":259,"value":39399},"Path ",{"type":254,"tag":622,"props":39401,"children":39402},{"style":629},[39403],{"type":259,"value":38421},{"type":254,"tag":622,"props":39405,"children":39406},{"style":1202},[39407],{"type":259,"value":38426},{"type":254,"tag":622,"props":39409,"children":39410},{"style":664},[39411],{"type":259,"value":39412},"\\AppData\\Local\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json",{"type":254,"tag":622,"props":39414,"children":39415},{"style":629},[39416],{"type":259,"value":652},{"type":254,"tag":622,"props":39418,"children":39419},{"style":629},[39420],{"type":259,"value":10733},{"type":254,"tag":622,"props":39422,"children":39423},{"style":1202},[39424],{"type":259,"value":33360},{"type":254,"tag":622,"props":39426,"children":39427},{"style":629},[39428],{"type":259,"value":38421},{"type":254,"tag":622,"props":39430,"children":39431},{"style":1202},[39432],{"type":259,"value":38426},{"type":254,"tag":622,"props":39434,"children":39435},{"style":664},[39436],{"type":259,"value":39437},"\\dotfiles\\config\\windowsTerminal\\settings.json",{"type":254,"tag":622,"props":39439,"children":39440},{"style":1202},[39441],{"type":259,"value":39442},"\"\"\n",{"type":254,"tag":255,"props":39444,"children":39445},{},[39446],{"type":259,"value":39447},"Because I use symbolic links, when I modify the configuration of my Windows Terminal (event through the UI), the settings file in my git repository is the one being modified so I can commit it and push it so that my other machines have the latest version. And it's the same for all my settings files.",{"type":254,"tag":255,"props":39449,"children":39450},{},[39451],{"type":259,"value":39452},"There are others settings in my repository and I will probably continue to add some later.",{"type":254,"tag":282,"props":39454,"children":39456},{"id":39455},"what-i-did-not-automate-yet",[39457],{"type":259,"value":39458},"What I did not automate ... yet!",{"type":254,"tag":255,"props":39460,"children":39461},{},[39462],{"type":259,"value":39463},"There are many things I did not automate yet and that I will do bit by bit.  Nevertheless, I am happy with the current setup. There is much room for improvement but I've already won some time last time I changed my laptop, and that was the goal.",{"type":254,"tag":255,"props":39465,"children":39466},{},[39467,39469,39476,39478,39484],{"type":259,"value":39468},"As I've just said, there are other configurations I would probably need to store in my repository. ",{"type":254,"tag":262,"props":39470,"children":39473},{"href":39471,"rel":39472},"https://docs.microsoft.com/en-us/windows/powertoys",[266],[39474],{"type":259,"value":39475},"Power Toys",{"type":259,"value":39477}," settings might be one of them unless they add a settings sync (issue already opened ",{"type":254,"tag":262,"props":39479,"children":39482},{"href":39480,"rel":39481},"https://github.com/microsoft/PowerToys/issues/3381",[266],[39483],{"type":259,"value":1414},{"type":259,"value":39485},"). It would be better because there are many settings files so only using symbolic links could be cumbersome.",{"type":254,"tag":255,"props":39487,"children":39488},{},[39489,39491,39498],{"type":259,"value":39490},"Most of the software I need is installed by my setup script. One exception is JetBrains products but I hope it will be possible soon. I have some commented lines in my installation script about automating the setup of ",{"type":254,"tag":262,"props":39492,"children":39495},{"href":39493,"rel":39494},"https://docs.microsoft.com/en-us/windows/wsl/install",[266],[39496],{"type":259,"value":39497},"WSL2",{"type":259,"value":39499}," and docker (well installation of Rancher Desktop to be more precise as a replacement of Docker Desktop due to its new license model) that required some windows features to be installed.  I think these lines should work with some adjustments, it's just that I did that manually last time and did not take the time to test the corresponding commands properly.",{"type":254,"tag":255,"props":39501,"children":39502},{},[39503],{"type":259,"value":39504},"There are also some limitations to my current approach to installing my developer machine:",{"type":254,"tag":332,"props":39506,"children":39507},{},[39508,39522,39527],{"type":254,"tag":336,"props":39509,"children":39510},{},[39511,39513,39520],{"type":259,"value":39512},"As everything is contained in a public GitHub repository, I can't keep some files or secrets private obviously (it could be useful to configure a new machine to ",{"type":254,"tag":262,"props":39514,"children":39517},{"href":39515,"rel":39516},"https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits",[266],[39518],{"type":259,"value":39519},"sign commits",{"type":259,"value":39521}," for instance)",{"type":254,"tag":336,"props":39523,"children":39524},{},[39525],{"type":259,"value":39526},"The installation script is not idempotent so it will try to re-install and re-configure everything if I re-run it (not a big deal as I mainly use it to install a new machine, but it would be nice to be able to just re-run it to get on an existing installation all the latest improvements I made on another laptop)",{"type":254,"tag":336,"props":39528,"children":39529},{},[39530],{"type":259,"value":39531},"I did not set up anything to handle multiple configurations (one for my personal laptop and one for my professional laptop for example)",{"type":254,"tag":255,"props":39533,"children":39534},{},[39535,39537,39544],{"type":259,"value":39536},"I think the tool ",{"type":254,"tag":262,"props":39538,"children":39541},{"href":39539,"rel":39540},"https://www.chezmoi.io/",[266],[39542],{"type":259,"value":39543},"chezmoi",{"type":259,"value":39545}," might help to solve some of these limitations. Maybe one day I'll take the time to dig a little deeper to see if I can improve the whole process.",{"type":254,"tag":282,"props":39547,"children":39548},{"id":10572},[39549],{"type":259,"value":10575},{"type":254,"tag":255,"props":39551,"children":39552},{},[39553,39555,39561],{"type":259,"value":39554},"You can find the repository with all the code I use to automate my installation ",{"type":254,"tag":262,"props":39556,"children":39559},{"href":39557,"rel":39558},"https://github.com/TechWatching/dotfiles",[266],[39560],{"type":259,"value":1414},{"type":259,"value":594},{"type":254,"tag":255,"props":39563,"children":39564},{},[39565],{"type":259,"value":39566},"Automating my machine installation was not that fun, but I learned lots of things while doing it about package managers, windows, symbolic links, PowerShell... And in the end, it is very satisfying when everything starts to install automatically and you find a familiar environment on a new laptop.",{"type":254,"tag":255,"props":39568,"children":39569},{},[39570],{"type":259,"value":39571},"Whether you adopt this approach or another to automate the setup of your development machine, I think it's an important thing to do. It does not have to be complex, and you don't need to automate everything. But you should probably at least use a package manager for your software installations, have a list somewhere of the most useful tools you need, and keep a copy of the configuration files of your most important tools.",{"type":254,"tag":255,"props":39573,"children":39574},{},[39575,39577,39584],{"type":259,"value":39576},"This article is part of the ",{"type":254,"tag":262,"props":39578,"children":39581},{"href":39579,"rel":39580},"https://townhall.hashnode.com/4-articles-in-4-weeks-hashnode-writing-contest",[266],[39582],{"type":259,"value":39583},"4 articles in 4 weeks Hashnode writing contest",{"type":259,"value":594},{"type":254,"tag":255,"props":39586,"children":39587},{},[39588],{"type":259,"value":39589},"Happy learning.",{"type":254,"tag":1260,"props":39591,"children":39592},{},[39593],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":39595},[39596,39597,39598,39604,39605,39606],{"id":284,"depth":635,"text":287},{"id":38172,"depth":635,"text":38175},{"id":38450,"depth":635,"text":38453,"children":39599},[39600,39601,39602,39603],{"id":38456,"depth":679,"text":38459},{"id":38502,"depth":679,"text":38505},{"id":38654,"depth":679,"text":38657},{"id":39070,"depth":679,"text":39073},{"id":39282,"depth":635,"text":39285},{"id":39455,"depth":635,"text":39458},{"id":10572,"depth":635,"text":10575},"content:1.posts:42.automate-developer-machine.md","1.posts/42.automate-developer-machine.md",{"_path":130,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":129,"description":39610,"lead":39611,"date":39612,"badge":39613,"image":39614,"tags":39616,"body":39617,"_type":1275,"_id":39999,"_source":231,"_file":40000,"_extension":1278},"Like I wrote in my last article, as a developer sometimes you get stuck on a problem for a few hours or a few days, your code won't work, and you think nobody can help you 😿. This can be very frustrating, but fortunately, there are plenty of ways to look for a solution or to seek help. You are probably already using some of them with success, yet there may be some you don't think of and that could help you a lot. That is the topic of this article.","Some tips to get unstuck as a developer","2022-08-27T00:00:00.000Z",{"label":10059},{"src":39615},"/images/laptop_1.jpg",[10061],{"type":251,"children":39618,"toc":39991},[39619,39632,39638,39643,39657,39670,39675,39684,39689,39694,39699,39705,39710,39715,39724,39738,39744,39766,39775,39787,39792,39801,39807,39829,39834,39901,39906,39922,39928,39933,39941,39946,39955,39960,39968,39972,39977,39987],{"type":254,"tag":255,"props":39620,"children":39621},{},[39622,39624,39630],{"type":259,"value":39623},"Like I wrote in my last ",{"type":254,"tag":262,"props":39625,"children":39628},{"href":39626,"rel":39627},"https://www.techwatching.dev/posts/be-a-developer",[266],[39629],{"type":259,"value":36966},{"type":259,"value":39631},", as a developer sometimes you get stuck on a problem for a few hours or a few days, your code won't work, and you think nobody can help you 😿. This can be very frustrating, but fortunately, there are plenty of ways to look for a solution or to seek help. You are probably already using some of them with success, yet there may be some you don't think of and that could help you a lot. That is the topic of this article.",{"type":254,"tag":282,"props":39633,"children":39635},{"id":39634},"use-the-obvious-but-efficient-ways-to-overcome-your-programming-problem",[39636],{"type":259,"value":39637},"Use the obvious but efficient ways to overcome your programming problem",{"type":254,"tag":255,"props":39639,"children":39640},{},[39641],{"type":259,"value":39642},"There are many obvious ways to find a solution to a programming problem. And as you must be already familiar with them, I won't spend too much time talking to you about them.",{"type":254,"tag":255,"props":39644,"children":39645},{},[39646,39648,39655],{"type":259,"value":39647},"I guess every developer knows about \"",{"type":254,"tag":262,"props":39649,"children":39652},{"href":39650,"rel":39651},"https://en.wikipedia.org/wiki/Rubber_duck_debugging",[266],[39653],{"type":259,"value":39654},"rubber duck debugging",{"type":259,"value":39656},"\": sometimes just explaining your problem to someone or something 🦆 can help you understand what is not working correctly. I think there are two interesting ideas to draw from it:",{"type":254,"tag":332,"props":39658,"children":39659},{},[39660,39665],{"type":254,"tag":336,"props":39661,"children":39662},{},[39663],{"type":259,"value":39664},"it's important to clearly state what your problem is and what did you try to solve it 🔍",{"type":254,"tag":336,"props":39666,"children":39667},{},[39668],{"type":259,"value":39669},"sometimes you can find the solution to your problem just by looking at it from another perspective 🔭",{"type":254,"tag":255,"props":39671,"children":39672},{},[39673],{"type":259,"value":39674},"If you know how to quickly explain what your problem is about (and even better if you have written it down), what you tried, what worked, and what did not work, then it will be easy to ask for help from your colleagues. It will also help you if you need to ask your question on a Q&A website like StackOverflow. These are two common things people do to overcome their issues and they often work well.",{"type":254,"tag":255,"props":39676,"children":39677},{},[39678],{"type":254,"tag":1161,"props":39679,"children":39683},{"alt":39680,"className":39681,"src":39682},"A person making a Google search on a laptop computer at a desk.",[1165,1166],"/posts/images/getunstuck_google.jpg",[],{"type":254,"tag":255,"props":39685,"children":39686},{},[39687],{"type":259,"value":39688},"Another obvious one is to use your favorite search engine (Google, Bing, DuckDuckGo, Qwant, ...) to find a solution. I often think that part of being a developer is to know how to efficiently use a search engine (if you are interested, there are many articles on the internet that give tips about how to better use a search engine) and how to quickly identify the search results that can help you.",{"type":254,"tag":255,"props":39690,"children":39691},{},[39692],{"type":259,"value":39693},"If you have been stuck for a lot of time, just take a break. Go for a walk 🚶🏽‍♀️, go cook something, go play sports, in short, do something else to let your brain rest. Sometimes your brain needs time to process information 🧠. Maybe you just need a good night's sleep before you tackle your problem again. You might even be surprised to find the solution while doing something else. For example, I know that some people (myself included) get a lot of new ideas or find solutions when they take a shower (of course, think of the planet 🌍 and do not take big showers just because I said that).",{"type":254,"tag":255,"props":39695,"children":39696},{},[39697],{"type":259,"value":39698},"Now let's talk about less obvious tips to get unstuck.",{"type":254,"tag":282,"props":39700,"children":39702},{"id":39701},"look-for-help-in-your-company",[39703],{"type":259,"value":39704},"Look for help in your company",{"type":254,"tag":255,"props":39706,"children":39707},{},[39708],{"type":259,"value":39709},"I have already talked about asking for help from your colleagues. Usually, that is the first thing we do. But we often only ask our close colleagues or the colleagues in our team. If you are working in a big company, many people that might help you even if you don't know them personally. So how to contact them?",{"type":254,"tag":255,"props":39711,"children":39712},{},[39713],{"type":259,"value":39714},"If your issue concerns a specific topic and there is a Subject Matter Expert for this topic in your company, maybe you can contact him directly by email or by chat. There might even be a community of people interested in this topic that discuss it on Microsoft Teams or Slack channels (or whatever messaging application your company is using): join the conversation to raise your point, people are often happy to share their knowledge on a topic they like.",{"type":254,"tag":255,"props":39716,"children":39717},{},[39718],{"type":254,"tag":1161,"props":39719,"children":39723},{"alt":39720,"className":39721,"src":39722},"A scrabble tiles spelling ask for help on a white surface.",[1165,1166],"/posts/images/getunstuck_help.jpg",[],{"type":254,"tag":255,"props":39725,"children":39726},{},[39727,39729,39736],{"type":259,"value":39728},"I am sure there are many other ways to ask for help in your company that you know but don't think about using. For example, your company might have an enterprise social networking service like Yammer. In my current company, we have ",{"type":254,"tag":262,"props":39730,"children":39733},{"href":39731,"rel":39732},"https://stackoverflow.co/teams",[266],[39734],{"type":259,"value":39735},"Stack Overflow for Enterprise",{"type":259,"value":39737}," (it's like a private instance of Stack Overflow for a company) and it's great to discuss technical questions inside the company, even with colleagues from other countries. Don't hesitate to use your company communication tools to seek advice from experts, someone may be able to help you!",{"type":254,"tag":282,"props":39739,"children":39741},{"id":39740},"ask-questions-in-specialized-forums-and-qa-platforms",[39742],{"type":259,"value":39743},"Ask questions in specialized forums and Q&A platforms",{"type":254,"tag":255,"props":39745,"children":39746},{},[39747,39749,39756,39758,39765],{"type":259,"value":39748},"Yes, I know you already know and use StackOverflow (by the way, do not only search your solution in answers to existing questions, take the time to contribute too by asking questions and answering some). However, there are plenty of Q&A platforms, maybe less famous than StackOverflow but more specialized. For instance, if you are facing a problem with an Azure service you can use ",{"type":254,"tag":262,"props":39750,"children":39753},{"href":39751,"rel":39752},"https://docs.microsoft.com/en-us/answers/",[266],[39754],{"type":259,"value":39755},"Microsoft Q&A",{"type":259,"value":39757}," which is the official Microsoft site for technical questions on Microsoft products and services. People from the community will try to help you as well as people from Microsoft. If you have a question about Vue.js you can ask it on the ",{"type":254,"tag":262,"props":39759,"children":39762},{"href":39760,"rel":39761},"https://forum.vuejs.org/",[266],[39763],{"type":259,"value":39764},"Vue.js forum",{"type":259,"value":594},{"type":254,"tag":255,"props":39767,"children":39768},{},[39769],{"type":254,"tag":1161,"props":39770,"children":39774},{"alt":39771,"className":39772,"src":39773},"Microsoft Q&A website.",[1165,1166],"/posts/images/getunstuck_microsoft_qna.png",[],{"type":254,"tag":255,"props":39776,"children":39777},{},[39778,39785],{"type":254,"tag":262,"props":39779,"children":39782},{"href":39780,"rel":39781},"https://www.reddit.com/",[266],[39783],{"type":259,"value":39784},"Reddit",{"type":259,"value":39786}," is another interesting platform. It's kind of a collection of forums/communities, where each forum/community is about a topic. It's popular among developers so there is a community for almost every developer topic/framework/stack you could be dealing with. Therefore you should find a community where to report your problem and some people might give you some answers or some leads to exploring. Please note that it's sometimes complicated to get visibility for your question because its visibility will depend on whether it is upvoted a lot or not.",{"type":254,"tag":255,"props":39788,"children":39789},{},[39790],{"type":259,"value":39791},"I almost forgot to talk about GitHub Discussions. GitHub Discussions is a collaborative communication forum you enable or not on a GitHub repository so it might not be present all the time. But when your problem is related to an open source project on GitHub that has GitHub discussions enabled, it's a great place to ask questions. The issues tab of a GitHub repository are another place you can ask questions but it might be less suitable to discuss your problem, it depends on the repository and what the maintainers of the project prefer.",{"type":254,"tag":255,"props":39793,"children":39794},{},[39795],{"type":254,"tag":1161,"props":39796,"children":39800},{"alt":39797,"className":39798,"src":39799},"Pulumi discussions on GitHub.",[1165,1166],"/posts/images/getunstuck_pulumi.png",[],{"type":254,"tag":282,"props":39802,"children":39804},{"id":39803},"talk-to-community-members-where-they-met",[39805],{"type":259,"value":39806},"Talk to community members where they met",{"type":254,"tag":255,"props":39808,"children":39809},{},[39810,39812,39819,39820,39827],{"type":259,"value":39811},"When you are stuck with a programming problem it's often related to the software (framework, library, tool, ...) your code is using. Therefore the people best able to help you are the maintainers or other users of this software. And whether or not they are part of a company or an open source community, they often use communication tools like ",{"type":254,"tag":262,"props":39813,"children":39816},{"href":39814,"rel":39815},"https://slack.com/",[266],[39817],{"type":259,"value":39818},"Slack",{"type":259,"value":31561},{"type":254,"tag":262,"props":39821,"children":39824},{"href":39822,"rel":39823},"https://discord.com/",[266],[39825],{"type":259,"value":39826},"Discord",{"type":259,"value":39828}," to talk. So, a good idea 💡 would be to join the corresponding Slack workspace or Discord server (generally they are open to everyone to join) and talk to these people through the appropriate discussions/channels. Most of the time, channels/discussions are categorized by topic and there are sometimes some dedicated to people looking for help. Each community has its rules and ways of communicating so be sure to follow them but generally, people are welcoming and happy to help 😀.",{"type":254,"tag":255,"props":39830,"children":39831},{},[39832],{"type":259,"value":39833},"Just to give you a few examples of communities:",{"type":254,"tag":332,"props":39835,"children":39836},{},[39837,39848,39858,39869,39879,39890],{"type":254,"tag":336,"props":39838,"children":39839},{},[39840,39846],{"type":254,"tag":262,"props":39841,"children":39844},{"href":39842,"rel":39843},"http://aka.ms/functions/discord",[266],[39845],{"type":259,"value":1362},{"type":259,"value":39847}," has a discord community",{"type":254,"tag":336,"props":39849,"children":39850},{},[39851,39857],{"type":254,"tag":262,"props":39852,"children":39855},{"href":39853,"rel":39854},"https://discord.gg/NtAbbGn",[266],[39856],{"type":259,"value":23654},{"type":259,"value":39847},{"type":254,"tag":336,"props":39859,"children":39860},{},[39861,39867],{"type":254,"tag":262,"props":39862,"children":39865},{"href":39863,"rel":39864},"https://slack.pulumi.com/",[266],[39866],{"type":259,"value":10225},{"type":259,"value":39868}," has a slack community",{"type":254,"tag":336,"props":39870,"children":39871},{},[39872,39878],{"type":254,"tag":262,"props":39873,"children":39876},{"href":39874,"rel":39875},"https://communityinviter.com/apps/nukebuildnet/nuke",[266],[39877],{"type":259,"value":32881},{"type":259,"value":39868},{"type":254,"tag":336,"props":39880,"children":39881},{},[39882,39889],{"type":254,"tag":262,"props":39883,"children":39886},{"href":39884,"rel":39885},"https://discord.gg/redis",[266],[39887],{"type":259,"value":39888},"Redis",{"type":259,"value":39847},{"type":254,"tag":336,"props":39891,"children":39892},{},[39893,39900],{"type":254,"tag":262,"props":39894,"children":39897},{"href":39895,"rel":39896},"https://chat.vitest.dev/",[266],[39898],{"type":259,"value":39899},"Vitest",{"type":259,"value":39847},{"type":254,"tag":255,"props":39902,"children":39903},{},[39904],{"type":259,"value":39905},"You can see there are companies behind some communities (like Redis or Pulumi). In any case, communities backed by a company or not, Discord and Slack communities are a good place to discuss with people building the software you are using. There are a lot of communities whether it be on Discord, Slack, or on other tools. So I'm sure that whatever you need help with, you will find a community to join.",{"type":254,"tag":427,"props":39907,"children":39908},{"icon":567},[39909],{"type":254,"tag":255,"props":39910,"children":39911},{},[39912,39914,39921],{"type":259,"value":39913},"If you want to know more about that, you can check the third article of my series \"How to do a technology watch?\": ",{"type":254,"tag":262,"props":39915,"children":39918},{"href":39916,"rel":39917},"https://www.techwatching.dev/posts/technology-watch-part3",[266],[39919],{"type":259,"value":39920},"Be part of the community",{"type":259,"value":594},{"type":254,"tag":282,"props":39923,"children":39925},{"id":39924},"use-twitter-to-get-some-help",[39926],{"type":259,"value":39927},"Use Twitter to get some help",{"type":254,"tag":255,"props":39929,"children":39930},{},[39931],{"type":259,"value":39932},"I don't know if you use Twitter for technology content but it's an amazing platform for everything related to IT. Not because it's a better microblogging platform than others (I know that Twitter is quite criticized right now because of security and privacy concerns), but because of all the people and organizations that use it. Lots of software products or frameworks have their own Twitter account and there are even some dedicated support accounts.",{"type":254,"tag":31130,"props":39934,"children":39936},{"className":39935},[31133,31134],[39937],{"type":254,"tag":31137,"props":39938,"children":39940},{"id":39939},"1558416474565394434",[],{"type":254,"tag":255,"props":39942,"children":39943},{},[39944],{"type":259,"value":39945},"On Twitter, you can also join Communities which are spaces created by people to engage with others who share interests. For instance, if you are struggling with C# code, you could join the dotnet community and tweet your question, some might have encountered the same issue and know how to solve it.",{"type":254,"tag":255,"props":39947,"children":39948},{},[39949],{"type":254,"tag":1161,"props":39950,"children":39954},{"alt":39951,"className":39952,"src":39953,"width":19735},"Twitter website that displays several communities: Vue.js, Microsoft Azure, dotnet.",[1165,1166],"/posts/images/getunstuck_twitter_communities.png",[],{"type":254,"tag":255,"props":39956,"children":39957},{},[39958],{"type":259,"value":39959},"What I find extraordinary with Twitter is to be able to reach some people that I could not have reached otherwise. And I am not talking only about developer advocates who are easier to approach, but people building the products I am using. When someone from a team building Xamarin, Azure, winget, or GitHub takes the time to explain to you something you are struggling with just by answering your tweet it's always nice. Of course, sometimes people don't see your tweet or don't have the time to answer. However, by experience people on Twitter are usually very kind and answer. Obviously, I don't spend my time harassing developers on Twitter with my questions but I had interesting answers to my tweets and some nice exchanges by private message too.",{"type":254,"tag":31130,"props":39961,"children":39963},{"className":39962},[31133,31134],[39964],{"type":254,"tag":31137,"props":39965,"children":39967},{"id":39966},"1559993129603895297",[],{"type":254,"tag":282,"props":39969,"children":39970},{"id":10572},[39971],{"type":259,"value":10575},{"type":254,"tag":255,"props":39973,"children":39974},{},[39975],{"type":259,"value":39976},"I hope you learned something and that these tips will help you get unstuck next time you face a programming problem. To finish I want to give you one last tip when stuck with a difficult programming problem: once you find the solution write it down somewhere so that you can find it later when you face the same issue again (because you might not remember what was the solution). You can write it in your notes 🗒️ with links to articles that help you solve the problem, or even better you can write a blog post about it.",{"type":254,"tag":255,"props":39978,"children":39979},{},[39980,39981,39986],{"type":259,"value":39576},{"type":254,"tag":262,"props":39982,"children":39984},{"href":39579,"rel":39983},[266],[39985],{"type":259,"value":39583},{"type":259,"value":594},{"type":254,"tag":255,"props":39988,"children":39989},{},[39990],{"type":259,"value":39589},{"title":205,"searchDepth":635,"depth":635,"links":39992},[39993,39994,39995,39996,39997,39998],{"id":39634,"depth":635,"text":39637},{"id":39701,"depth":635,"text":39704},{"id":39740,"depth":635,"text":39743},{"id":39803,"depth":635,"text":39806},{"id":39924,"depth":635,"text":39927},{"id":10572,"depth":635,"text":10575},"content:1.posts:41.get-unstuck.md","1.posts/41.get-unstuck.md",{"_path":127,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":126,"description":40002,"lead":40003,"date":40004,"badge":40005,"image":40006,"tags":40008,"body":40009,"_type":1275,"_id":40193,"_source":231,"_file":40194,"_extension":1278},"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":10059},{"src":40007},"/images/everyone_can_code.jpg",[10061],{"type":251,"children":40010,"toc":40187},[40011,40015,40021,40026,40035,40040,40045,40051,40056,40065,40070,40075,40084,40089,40106,40111,40116,40122,40127,40135,40144,40149,40154,40163,40167,40172,40183],{"type":254,"tag":255,"props":40012,"children":40013},{},[40014],{"type":259,"value":40002},{"type":254,"tag":282,"props":40016,"children":40018},{"id":40017},"the-always-wanted-to-be-a-developer-fairy-tale",[40019],{"type":259,"value":40020},"The \"always wanted to be a developer\" fairy tale",{"type":254,"tag":255,"props":40022,"children":40023},{},[40024],{"type":259,"value":40025},"I often see people on social networks writing about how they have always wanted to be a developer, how they started programming as kids, and how they have built their first websites in high school or before. Sorry to disappoint you, but that's not me. I have never programmed anything when I was a kid 🧒. Yes, I used a computer but that was to play video games not something geeky. I only discovered computer science after high school when I was preparing for the selective entrance examination to French engineering schools. The computer science class was an optional class but I enjoyed it. I enjoyed it more than the advanced mathematics and physics courses that I liked but that were becoming too abstract for me. It convinced me that I should study in a software engineering school to become a developer. There I discovered what being a developer was really about, and became fond of it ❤️.",{"type":254,"tag":255,"props":40027,"children":40028},{},[40029],{"type":254,"tag":1161,"props":40030,"children":40034},{"alt":40031,"className":40032,"src":40033},"A pair of feet standing in front of a sign that says passion led us here.",[1165,1166],"/posts/images/be_a_developer_1.jpg",[],{"type":254,"tag":255,"props":40036,"children":40037},{},[40038],{"type":259,"value":40039},"Hence, no \"always wanted to be a developer\" fairy tale for me 📖. No years of practicing software development before studying it during higher education. And that's okay by me. I think you don't have to be passionate about development since your childhood to become a developer. Moreover, it's not because you did not build your first website before 15 years old that you are not going to be a good developer or that others will always be better than you. You don't even need to get a computer science degree to become a software developer. I graduated in computer science and I am happy with my education but there are many other ways to become a developer (self-learning, training programs, coding bootcamps, career change, ...) 👨‍💻.",{"type":254,"tag":255,"props":40041,"children":40042},{},[40043],{"type":259,"value":40044},"I explained how I became a developer but not exactly why, which brings us to the second part of this article.",{"type":254,"tag":282,"props":40046,"children":40048},{"id":40047},"why-because-its-fun",[40049],{"type":259,"value":40050},"Why? because it's fun!",{"type":254,"tag":255,"props":40052,"children":40053},{},[40054],{"type":259,"value":40055},"Lots of people want to become developers because they are lots of job opportunities, the salary is usually good 💶 and you can work for nice companies. That's true, but we often forget the main thing: coding is fun 😁! It's like playing a game in which you have to solve problems. I still like playing video games in my free time (I don't do it often though), but sometimes I can spend a better time coding than playing video games 🎮. It just feels great writing code and solving problems!",{"type":254,"tag":255,"props":40057,"children":40058},{},[40059],{"type":254,"tag":1161,"props":40060,"children":40064},{"alt":40061,"className":40062,"src":40063},"A close up of a white puzzle piece.",[1165,1166],"/posts/images/be_a_developer_2.jpg",[],{"type":254,"tag":255,"props":40066,"children":40067},{},[40068],{"type":259,"value":40069},"Moreover, when you develop, you are building something. Something that may be digital/virtual but that helps people in the real world. It's amazing when you realize that with coding skills you have the opportunity to build anything you like: applications that run on your laptop, on your smartphone, on your watch, in your browser, in the cloud, in your IoT device, in your car... Software runs the world and you can help build it 🌍.",{"type":254,"tag":255,"props":40071,"children":40072},{},[40073],{"type":259,"value":40074},"Nowadays, IT is everywhere so you can work in many different fields. Whether your work in the energy industry or e-commerce, you will still be coding but the software you are developing will have different business needs and constraints. It means, you can work for a variety of companies, and discover different businesses while being a developer. Being able to work for different businesses is the case in other professions but this is especially true for developers. And even if you only ever work for one business, IT is a big field so your job can be very different depending on what type of software you are working on 💻.",{"type":254,"tag":255,"props":40076,"children":40077},{},[40078],{"type":254,"tag":1161,"props":40079,"children":40083},{"alt":40080,"className":40081,"src":40082},"A laptop computer sitting on top of a white table.",[1165,1166],"/posts/images/be_a_developer_5.jpg",[],{"type":254,"tag":255,"props":40085,"children":40086},{},[40087],{"type":259,"value":40088},"Of course, it's hardly a bed of roses. Just because it's fun does not mean it's easy to be a developer. IT is a fast-moving industry, so it's very hard to keep up to date with new frameworks, libraries, language features, cloud components... 🚀 Everything is evolving rapidly so you never stop learning. But it's part of what makes software development great, you are always learning something new 👍. I must confess you can sometimes feel like a beginner when you start working on something new, and even with several years of experience, you can suffer from impostor syndrome. Yet, when you realize how far you have come, how much you have learned, it just feels nice working as a developer.",{"type":254,"tag":427,"props":40090,"children":40091},{"icon":567},[40092],{"type":254,"tag":255,"props":40093,"children":40094},{},[40095,40097,40104],{"type":259,"value":40096},"If you are interested in how to keep up to date with the latest things in IT, I wrote ",{"type":254,"tag":262,"props":40098,"children":40101},{"href":40099,"rel":40100},"https://www.techwatching.dev/posts/technology-watch-part1",[266],[40102],{"type":259,"value":40103},"a series of 3 articles about how to do a technology watch",{"type":259,"value":40105}," 📚.",{"type":254,"tag":255,"props":40107,"children":40108},{},[40109],{"type":259,"value":40110},"I think it's important not to speak only about the positive aspects of being a developer. I love my job but it's not always easy. Sometimes you get stuck on a problem for a few days, your code won't work, and you think nobody can help you 😿. Unless you are implementing complex algorithms, writing code is not hard but making it work is something else. Because being a developer is not only about writing code, there is much more to it than that: teamwork, understanding the business needs, designing software, debugging - building - packaging - deploying - testing - monitoring your code ... 🛠️ So yes it's a challenging job, but I like challenges. At the end, when things finally work, you ship your code to production, and the users are happy it's very rewarding 😀.",{"type":254,"tag":255,"props":40112,"children":40113},{},[40114],{"type":259,"value":40115},"All that is what made me want to be a developer. Nevertheless, the developer's job leads to many other professions, so after a few years should I stay a developer?",{"type":254,"tag":282,"props":40117,"children":40119},{"id":40118},"the-real-question-what-makes-me-want-to-stay-a-developer",[40120],{"type":259,"value":40121},"The real question: what makes me want to stay a developer?",{"type":254,"tag":255,"props":40123,"children":40124},{},[40125],{"type":259,"value":40126},"I don't know if it's the same in other countries, but in France, developer is often seen as an entry-level job. According to many companies, as you gain in experience, you should either become a technical expert in a specific field or a project manager leading a team. Being a \"simple\" yet experienced developer is often seen as a failure when you grow \"old\". Your experience and knowledge as a developer are not so much valued 💶, and if you wish to develop your career you should be managing projects better than programming. So what makes me want to stay a developer then?",{"type":254,"tag":427,"props":40128,"children":40129},{"icon":429},[40130],{"type":254,"tag":255,"props":40131,"children":40132},{},[40133],{"type":259,"value":40134},"It's weird to encourage experienced developers to become project manager because it's not the same job and it does not require the same skills. Therefore a well experienced developer can easily become a bad project manager. I have nothing against developers becoming project managers, if you want to, do it, it's great but you should not have to do it just to get promoted or have a better salary. Hopefully, not all the companies think like that but still, that's a problem in France.",{"type":254,"tag":255,"props":40136,"children":40137},{},[40138],{"type":254,"tag":1161,"props":40139,"children":40143},{"alt":40140,"className":40141,"src":40142},"A heart is shown on a computer screen with 0s an 1s.",[1165,1166],"/posts/images/be_a_developer_4.jpg",[],{"type":254,"tag":255,"props":40145,"children":40146},{},[40147],{"type":259,"value":40148},"I want to stay a developer because I love programming 💖. I don't want to be a technical expert or a project manager telling people what to code but hardly coding at all himself. I like programming, it's what I currently do, and it's what I want to keep doing. And I don't just want to stay a developer, I want to become a better developer. I want to continue to grow and develop new skills.",{"type":254,"tag":255,"props":40150,"children":40151},{},[40152],{"type":259,"value":40153},"As a developer, there are so many things to learn, so many technologies to discover, and IT keeps evolving. For the years to come, I want to continue learning new languages and frameworks, use the latest trendy tools, be amazed by new technologies, build software that helps people, and share with others what I will have learned along the journey.",{"type":254,"tag":255,"props":40155,"children":40156},{},[40157],{"type":254,"tag":1161,"props":40158,"children":40162},{"alt":40159,"className":40160,"src":40161},"A man walking past a sign that says love to learn.",[1165,1166],"/posts/images/be_a_developer_3.jpg",[],{"type":254,"tag":282,"props":40164,"children":40165},{"id":6186},[40166],{"type":259,"value":37936},{"type":254,"tag":255,"props":40168,"children":40169},{},[40170],{"type":259,"value":40171},"Many things made me want to be a developer and stay one. Being a developer is awesome, I am grateful to do a job that I am passionate about.",{"type":254,"tag":255,"props":40173,"children":40174},{},[40175,40177,40182],{"type":259,"value":40176},"I don't often write non-technical articles and even fewer articles that talk about me. I hope you enjoyed reading this article anyway. This article is part of the ",{"type":254,"tag":262,"props":40178,"children":40180},{"href":39579,"rel":40179},[266],[40181],{"type":259,"value":39583},{"type":259,"value":594},{"type":254,"tag":255,"props":40184,"children":40185},{},[40186],{"type":259,"value":39589},{"title":205,"searchDepth":635,"depth":635,"links":40188},[40189,40190,40191,40192],{"id":40017,"depth":635,"text":40020},{"id":40047,"depth":635,"text":40050},{"id":40118,"depth":635,"text":40121},{"id":6186,"depth":635,"text":37936},"content:1.posts:40.be-a-developer.md","1.posts/40.be-a-developer.md",{"_path":124,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":123,"description":40196,"lead":40197,"date":40198,"image":40199,"badge":40201,"tags":40202,"body":40205,"_type":1275,"_id":41190,"_source":231,"_file":41191,"_extension":1278},"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":40200},"/images/padlock_1.jpg",{"label":243},[245,246,40203,16351,40204],"nushell","Azure Key Vault",{"type":251,"children":40206,"toc":41184},[40207,40240,40246,40278,40317,40367,40376,40381,40402,40408,40413,40421,40426,40432,40451,40464,40473,40484,40647,40652,40661,40666,40675,40687,40696,40711,40723,40732,40751,40760,40773,40782,40810,40815,40824,40837,40845,40850,40859,40871,40880,40892,40897,41152,41156,41161,41175,41180],{"type":254,"tag":255,"props":40208,"children":40209},{},[40210,40212,40217,40219,40225,40227,40233,40234,40239],{"type":259,"value":40211},"When using some API Clients (like ",{"type":254,"tag":262,"props":40213,"children":40215},{"href":10849,"rel":40214},[266],[40216],{"type":259,"value":37506},{"type":259,"value":40218}," or the ",{"type":254,"tag":262,"props":40220,"children":40222},{"href":37788,"rel":40221},[266],[40223],{"type":259,"value":40224},"HTTP Client of JetBrains' IDEs",{"type":259,"value":40226},"), 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":254,"tag":262,"props":40228,"children":40231},{"href":40229,"rel":40230},"https://docs.microsoft.com/en-us/cli/azure/",[266],[40232],{"type":259,"value":16351},{"type":259,"value":10347},{"type":254,"tag":262,"props":40235,"children":40237},{"href":23650,"rel":40236},[266],[40238],{"type":259,"value":23654},{"type":259,"value":594},{"type":254,"tag":282,"props":40241,"children":40243},{"id":40242},"the-problem-keep-secrets-secure-while-making-http-requests",[40244],{"type":259,"value":40245},"The problem: keep secrets secure while making HTTP requests",{"type":254,"tag":255,"props":40247,"children":40248},{},[40249,40251,40256,40258,40263,40265,40270,40271,40276],{"type":259,"value":40250},"If you have read my article \"",{"type":254,"tag":262,"props":40252,"children":40254},{"href":37481,"rel":40253},[266],[40255],{"type":259,"value":9},{"type":259,"value":40257},"\", you know I am a big fan of using the vscode extension ",{"type":254,"tag":389,"props":40259,"children":40261},{"className":40260},[],[40262],{"type":259,"value":37506},{"type":259,"value":40264}," 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":254,"tag":389,"props":40266,"children":40268},{"className":40267},[],[40269],{"type":259,"value":37749},{"type":259,"value":31561},{"type":254,"tag":389,"props":40272,"children":40274},{"className":40273},[],[40275],{"type":259,"value":37743},{"type":259,"value":40277}," 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":254,"tag":612,"props":40279,"children":40281},{"className":1184,"code":40280,"language":1186,"meta":205,"style":205},"### Get Luke Skywalker\nGET https://swapi.co/api/people/?search=Luke HTTP/1.1\n",[40282],{"type":254,"tag":389,"props":40283,"children":40284},{"__ignoreMap":205},[40285,40293],{"type":254,"tag":622,"props":40286,"children":40287},{"class":624,"line":625},[40288],{"type":254,"tag":622,"props":40289,"children":40290},{"style":3195},[40291],{"type":259,"value":40292},"### Get Luke Skywalker\n",{"type":254,"tag":622,"props":40294,"children":40295},{"class":624,"line":635},[40296,40300,40305,40309,40313],{"type":254,"tag":622,"props":40297,"children":40298},{"style":1196},[40299],{"type":259,"value":1199},{"type":254,"tag":622,"props":40301,"children":40302},{"style":1202},[40303],{"type":259,"value":40304}," https://swapi.co/api/people/?search=Luke ",{"type":254,"tag":622,"props":40306,"children":40307},{"style":741},[40308],{"type":259,"value":246},{"type":254,"tag":622,"props":40310,"children":40311},{"style":1202},[40312],{"type":259,"value":9768},{"type":254,"tag":622,"props":40314,"children":40315},{"style":741},[40316],{"type":259,"value":9773},{"type":254,"tag":255,"props":40318,"children":40319},{},[40320,40322,40329,40331,40336,40338,40343,40344,40349,40351,40357,40359,40365],{"type":259,"value":40321},"I have been using recently the IDE ",{"type":254,"tag":262,"props":40323,"children":40326},{"href":40324,"rel":40325},"https://www.jetbrains.com/fr-fr/rider/",[266],[40327],{"type":259,"value":40328},"Rider",{"type":259,"value":40330},", which has (like all the other JetBrains' IDEs) an integrated ",{"type":254,"tag":262,"props":40332,"children":40334},{"href":37788,"rel":40333},[266],[40335],{"type":259,"value":37792},{"type":259,"value":40337},". It's very similar to REST Client (same syntax for the requests that are written in ",{"type":254,"tag":389,"props":40339,"children":40341},{"className":40340},[],[40342],{"type":259,"value":37749},{"type":259,"value":31561},{"type":254,"tag":389,"props":40345,"children":40347},{"className":40346},[],[40348],{"type":259,"value":37743},{"type":259,"value":40350}," files) with some extra features. With this HTTP Client, environment variables are stored in a public JSON environment file ",{"type":254,"tag":389,"props":40352,"children":40354},{"className":40353},[],[40355],{"type":259,"value":40356},"http-client.env.json",{"type":259,"value":40358}," that can be committed. However, secrets can be stored in a private JSON environment file ",{"type":254,"tag":389,"props":40360,"children":40362},{"className":40361},[],[40363],{"type":259,"value":40364},"http-client.private.env.json",{"type":259,"value":40366}," 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":254,"tag":255,"props":40368,"children":40369},{},[40370],{"type":254,"tag":1161,"props":40371,"children":40375},{"alt":40372,"className":40373,"src":40374},"HTTP file, HTTP environment file, and HTTP private environment opnened file in Rider.",[1165,1166],"/posts/images/httpclientssecrets_rider_1.png",[],{"type":254,"tag":255,"props":40377,"children":40378},{},[40379],{"type":259,"value":40380},"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":254,"tag":427,"props":40382,"children":40383},{"icon":429},[40384],{"type":254,"tag":255,"props":40385,"children":40386},{},[40387,40389,40394,40395,40400],{"type":259,"value":40388},"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":254,"tag":389,"props":40390,"children":40392},{"className":40391},[],[40393],{"type":259,"value":37506},{"type":259,"value":31561},{"type":254,"tag":389,"props":40396,"children":40398},{"className":40397},[],[40399],{"type":259,"value":37792},{"type":259,"value":40401}," from JetBrains was managing secrets.",{"type":254,"tag":282,"props":40403,"children":40405},{"id":40404},"the-solution-use-azure-key-vault-and-scripting",[40406],{"type":259,"value":40407},"The solution: use Azure Key Vault and scripting",{"type":254,"tag":255,"props":40409,"children":40410},{},[40411],{"type":259,"value":40412},"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":254,"tag":427,"props":40414,"children":40415},{"icon":567},[40416],{"type":254,"tag":255,"props":40417,"children":40418},{},[40419],{"type":259,"value":40420},"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":254,"tag":255,"props":40422,"children":40423},{},[40424],{"type":259,"value":40425},"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":254,"tag":282,"props":40427,"children":40429},{"id":40428},"lets-script-that-with-azure-cli-and-nushell",[40430],{"type":259,"value":40431},"Let's script that with Azure CLI and Nushell!",{"type":254,"tag":255,"props":40433,"children":40434},{},[40435,40437,40442,40444,40449],{"type":259,"value":40436},"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":254,"tag":262,"props":40438,"children":40440},{"href":17271,"rel":40439},[266],[40441],{"type":259,"value":15},{"type":259,"value":40443},"\". If you don't know Nushell you can check its ",{"type":254,"tag":262,"props":40445,"children":40447},{"href":23650,"rel":40446},[266],[40448],{"type":259,"value":1323},{"type":259,"value":40450}," or just continue reading this article to see how nice this shell is.",{"type":254,"tag":255,"props":40452,"children":40453},{},[40454,40456,40462],{"type":259,"value":40455},"I have already created an Azure Key Vault named ",{"type":254,"tag":389,"props":40457,"children":40459},{"className":40458},[],[40460],{"type":259,"value":40461},"httpclient-vault",{"type":259,"value":40463}," and set 3 secrets in it.",{"type":254,"tag":255,"props":40465,"children":40466},{},[40467],{"type":254,"tag":1161,"props":40468,"children":40472},{"alt":40469,"className":40470,"src":40471},"The Secrets view of an Azure Key Vault resource in Azure Portal",[1165,1166],"/posts/images/httpclientssecrets_keyvault_1.png",[],{"type":254,"tag":255,"props":40474,"children":40475},{},[40476,40478,40483],{"type":259,"value":40477},"What I am trying to achieve is to produce the following file ",{"type":254,"tag":389,"props":40479,"children":40481},{"className":40480},[],[40482],{"type":259,"value":40364},{"type":259,"value":474},{"type":254,"tag":612,"props":40485,"children":40487},{"className":614,"code":40486,"language":616,"meta":205,"style":205},"{\n  \"development\":\n  {\n    \"ApiKey\": \"12345678\",\n    \"Username\": \"admin\",\n    \"UserPassword\": \"Password\"\n  }\n}\n",[40488],{"type":254,"tag":389,"props":40489,"children":40490},{"__ignoreMap":205},[40491,40498,40518,40526,40563,40600,40633,40640],{"type":254,"tag":622,"props":40492,"children":40493},{"class":624,"line":625},[40494],{"type":254,"tag":622,"props":40495,"children":40496},{"style":629},[40497],{"type":259,"value":632},{"type":254,"tag":622,"props":40499,"children":40500},{"class":624,"line":635},[40501,40505,40510,40514],{"type":254,"tag":622,"props":40502,"children":40503},{"style":629},[40504],{"type":259,"value":641},{"type":254,"tag":622,"props":40506,"children":40507},{"style":644},[40508],{"type":259,"value":40509},"development",{"type":254,"tag":622,"props":40511,"children":40512},{"style":629},[40513],{"type":259,"value":652},{"type":254,"tag":622,"props":40515,"children":40516},{"style":629},[40517],{"type":259,"value":16416},{"type":254,"tag":622,"props":40519,"children":40520},{"class":624,"line":679},[40521],{"type":254,"tag":622,"props":40522,"children":40523},{"style":629},[40524],{"type":259,"value":40525},"  {\n",{"type":254,"tag":622,"props":40527,"children":40528},{"class":624,"line":705},[40529,40533,40538,40542,40546,40550,40555,40559],{"type":254,"tag":622,"props":40530,"children":40531},{"style":629},[40532],{"type":259,"value":711},{"type":254,"tag":622,"props":40534,"children":40535},{"style":714},[40536],{"type":259,"value":40537},"ApiKey",{"type":254,"tag":622,"props":40539,"children":40540},{"style":629},[40541],{"type":259,"value":652},{"type":254,"tag":622,"props":40543,"children":40544},{"style":629},[40545],{"type":259,"value":474},{"type":254,"tag":622,"props":40547,"children":40548},{"style":629},[40549],{"type":259,"value":661},{"type":254,"tag":622,"props":40551,"children":40552},{"style":664},[40553],{"type":259,"value":40554},"12345678",{"type":254,"tag":622,"props":40556,"children":40557},{"style":629},[40558],{"type":259,"value":652},{"type":254,"tag":622,"props":40560,"children":40561},{"style":629},[40562],{"type":259,"value":676},{"type":254,"tag":622,"props":40564,"children":40565},{"class":624,"line":732},[40566,40570,40575,40579,40583,40587,40592,40596],{"type":254,"tag":622,"props":40567,"children":40568},{"style":629},[40569],{"type":259,"value":711},{"type":254,"tag":622,"props":40571,"children":40572},{"style":714},[40573],{"type":259,"value":40574},"Username",{"type":254,"tag":622,"props":40576,"children":40577},{"style":629},[40578],{"type":259,"value":652},{"type":254,"tag":622,"props":40580,"children":40581},{"style":629},[40582],{"type":259,"value":474},{"type":254,"tag":622,"props":40584,"children":40585},{"style":629},[40586],{"type":259,"value":661},{"type":254,"tag":622,"props":40588,"children":40589},{"style":664},[40590],{"type":259,"value":40591},"admin",{"type":254,"tag":622,"props":40593,"children":40594},{"style":629},[40595],{"type":259,"value":652},{"type":254,"tag":622,"props":40597,"children":40598},{"style":629},[40599],{"type":259,"value":676},{"type":254,"tag":622,"props":40601,"children":40602},{"class":624,"line":759},[40603,40607,40612,40616,40620,40624,40629],{"type":254,"tag":622,"props":40604,"children":40605},{"style":629},[40606],{"type":259,"value":711},{"type":254,"tag":622,"props":40608,"children":40609},{"style":714},[40610],{"type":259,"value":40611},"UserPassword",{"type":254,"tag":622,"props":40613,"children":40614},{"style":629},[40615],{"type":259,"value":652},{"type":254,"tag":622,"props":40617,"children":40618},{"style":629},[40619],{"type":259,"value":474},{"type":254,"tag":622,"props":40621,"children":40622},{"style":629},[40623],{"type":259,"value":661},{"type":254,"tag":622,"props":40625,"children":40626},{"style":664},[40627],{"type":259,"value":40628},"Password",{"type":254,"tag":622,"props":40630,"children":40631},{"style":629},[40632],{"type":259,"value":9909},{"type":254,"tag":622,"props":40634,"children":40635},{"class":624,"line":798},[40636],{"type":254,"tag":622,"props":40637,"children":40638},{"style":629},[40639],{"type":259,"value":1102},{"type":254,"tag":622,"props":40641,"children":40642},{"class":624,"line":836},[40643],{"type":254,"tag":622,"props":40644,"children":40645},{"style":629},[40646],{"type":259,"value":1111},{"type":254,"tag":255,"props":40648,"children":40649},{},[40650],{"type":259,"value":40651},"First, let's list the secrets in the Key Vault:",{"type":254,"tag":255,"props":40653,"children":40654},{},[40655],{"type":254,"tag":1161,"props":40656,"children":40660},{"alt":40657,"className":40658,"src":40659},"An Azure CLI command that lists Key Vault secrets in terminal.",[1165,1166],"/posts/images/httpclientssecrets_script_1.png",[],{"type":254,"tag":255,"props":40662,"children":40663},{},[40664],{"type":259,"value":40665},"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":254,"tag":255,"props":40667,"children":40668},{},[40669],{"type":254,"tag":1161,"props":40670,"children":40674},{"alt":40671,"className":40672,"src":40673},"An Azure CLI command using JMESPath that lists Key Vault secrets in terminal.",[1165,1166],"/posts/images/httpclientssecrets_script_2.png",[],{"type":254,"tag":255,"props":40676,"children":40677},{},[40678,40680,40686],{"type":259,"value":40679},"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":254,"tag":389,"props":40681,"children":40683},{"className":40682},[],[40684],{"type":259,"value":40685},"from json",{"type":259,"value":32093},{"type":254,"tag":255,"props":40688,"children":40689},{},[40690],{"type":254,"tag":1161,"props":40691,"children":40695},{"alt":40692,"className":40693,"src":40694},"The output of the \"az keyvault secret list --vault-name httpclient-vault | from json\" command in terminal.",[1165,1166],"/posts/images/httpclientssecrets_script_3.png",[],{"type":254,"tag":427,"props":40697,"children":40698},{"icon":567},[40699],{"type":254,"tag":255,"props":40700,"children":40701},{},[40702,40704,40709],{"type":259,"value":40703},"Nu has many ",{"type":254,"tag":389,"props":40705,"children":40707},{"className":40706},[],[40708],{"type":259,"value":24466},{"type":259,"value":40710}," commands to convert data from different formats to structured data/table.",{"type":254,"tag":255,"props":40712,"children":40713},{},[40714,40716,40722],{"type":259,"value":40715},"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":254,"tag":389,"props":40717,"children":40719},{"className":40718},[],[40720],{"type":259,"value":40721},"az keyvault secret show --id $secretId",{"type":259,"value":594},{"type":254,"tag":255,"props":40724,"children":40725},{},[40726],{"type":254,"tag":1161,"props":40727,"children":40731},{"alt":40728,"className":40729,"src":40730},"An Azure CLI command that get a secret from Key Vault in terminal.",[1165,1166],"/posts/images/httpclientssecrets_script_4.png",[],{"type":254,"tag":255,"props":40733,"children":40734},{},[40735,40737,40742,40744,40749],{"type":259,"value":40736},"Again we can use the ",{"type":254,"tag":389,"props":40738,"children":40740},{"className":40739},[],[40741],{"type":259,"value":40685},{"type":259,"value":40743}," command, and the ",{"type":254,"tag":389,"props":40745,"children":40747},{"className":40746},[],[40748],{"type":259,"value":25785},{"type":259,"value":40750}," command to only retrieve the value of a secret.",{"type":254,"tag":255,"props":40752,"children":40753},{},[40754],{"type":254,"tag":1161,"props":40755,"children":40759},{"alt":40756,"className":40757,"src":40758},"The output of the nushell script retrieving a secret value from keyvault.",[1165,1166],"/posts/images/httpclientssecrets_script_5.png",[],{"type":254,"tag":255,"props":40761,"children":40762},{},[40763,40765,40771],{"type":259,"value":40764},"Now that we know how to retrieve the value of a secret, we can insert a new column ",{"type":254,"tag":389,"props":40766,"children":40768},{"className":40767},[],[40769],{"type":259,"value":40770},"value",{"type":259,"value":40772}," into our table that will be filled with the value of each secret retrieved   using the previous command:",{"type":254,"tag":255,"props":40774,"children":40775},{},[40776],{"type":254,"tag":1161,"props":40777,"children":40781},{"alt":40778,"className":40779,"src":40780},"The output of the nushell script retrieving a list of secrets from keyvault.",[1165,1166],"/posts/images/httpclientssecrets_script_6.png",[],{"type":254,"tag":255,"props":40783,"children":40784},{},[40785,40786,40792,40794,40800,40802,40808],{"type":259,"value":10970},{"type":254,"tag":389,"props":40787,"children":40789},{"className":40788},[],[40790],{"type":259,"value":40791},"{|secret| (az keyvault secret show --id $secret.id | from json | get value)}",{"type":259,"value":40793}," part is a block that is executed for each row. The ",{"type":254,"tag":389,"props":40795,"children":40797},{"className":40796},[],[40798],{"type":259,"value":40799},"secret",{"type":259,"value":40801}," 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":254,"tag":389,"props":40803,"children":40805},{"className":40804},[],[40806],{"type":259,"value":40807},"$secret",{"type":259,"value":40809},". 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":254,"tag":255,"props":40811,"children":40812},{},[40813],{"type":259,"value":40814},"As we are only interested in the columns \"name\" and \"value\", we only select them.",{"type":254,"tag":255,"props":40816,"children":40817},{},[40818],{"type":254,"tag":1161,"props":40819,"children":40823},{"alt":40820,"className":40821,"src":40822},"The output of the nushell script retrieving Azure Key Vault secrets (name and value).",[1165,1166],"/posts/images/httpclientssecrets_script_7.png",[],{"type":254,"tag":255,"props":40825,"children":40826},{},[40827,40829,40835],{"type":259,"value":40828},"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":254,"tag":389,"props":40830,"children":40832},{"className":40831},[],[40833],{"type":259,"value":40834},"transpose",{"type":259,"value":40836}," with the proper flags to do that:",{"type":254,"tag":255,"props":40838,"children":40839},{},[40840],{"type":254,"tag":1161,"props":40841,"children":40844},{"alt":40820,"className":40842,"src":40843},[1165,1166],"/posts/images/httpclientssecrets_script_8.png",[],{"type":254,"tag":255,"props":40846,"children":40847},{},[40848],{"type":259,"value":40849},"Then we wrap the key-value pairs in a JSON object corresponding to the development environment:",{"type":254,"tag":255,"props":40851,"children":40852},{},[40853],{"type":254,"tag":1161,"props":40854,"children":40858},{"alt":40855,"className":40856,"src":40857},"The output of the nushell script creating a JSON object from Azure Key Vault secrets.",[1165,1166],"/posts/images/httpclientssecrets_script_9.png",[],{"type":254,"tag":255,"props":40860,"children":40861},{},[40862,40864,40870],{"type":259,"value":40863},"We can check we get the JSON we want with the ",{"type":254,"tag":389,"props":40865,"children":40867},{"className":40866},[],[40868],{"type":259,"value":40869},"to json",{"type":259,"value":32093},{"type":254,"tag":255,"props":40872,"children":40873},{},[40874],{"type":254,"tag":1161,"props":40875,"children":40879},{"alt":40876,"className":40877,"src":40878},"The output of the nushell script creating a JSON string from Azure Key Vault secrets.",[1165,1166],"/posts/images/httpclientssecrets_script_10.png",[],{"type":254,"tag":255,"props":40881,"children":40882},{},[40883,40885,40890],{"type":259,"value":40884},"And finally, we can save the data in a ",{"type":254,"tag":389,"props":40886,"children":40888},{"className":40887},[],[40889],{"type":259,"value":40364},{"type":259,"value":40891}," file using the save command.",{"type":254,"tag":255,"props":40893,"children":40894},{},[40895],{"type":259,"value":40896},"Here is the final script 🔽:",{"type":254,"tag":612,"props":40898,"children":40901},{"className":40899,"code":40900,"language":40203,"meta":205,"style":205},"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",[40902],{"type":254,"tag":389,"props":40903,"children":40904},{"__ignoreMap":205},[40905,40912,40954,40966,41061,41081,41102,41132,41145],{"type":254,"tag":622,"props":40906,"children":40907},{"class":624,"line":625},[40908],{"type":254,"tag":622,"props":40909,"children":40910},{"style":1202},[40911],{"type":259,"value":1452},{"type":254,"tag":622,"props":40913,"children":40914},{"class":624,"line":635},[40915,40920,40925,40930,40935,40939,40945,40950],{"type":254,"tag":622,"props":40916,"children":40917},{"style":714},[40918],{"type":259,"value":40919},"  az",{"type":254,"tag":622,"props":40921,"children":40922},{"style":664},[40923],{"type":259,"value":40924}," keyvault",{"type":254,"tag":622,"props":40926,"children":40927},{"style":664},[40928],{"type":259,"value":40929}," secret",{"type":254,"tag":622,"props":40931,"children":40932},{"style":664},[40933],{"type":259,"value":40934}," list",{"type":254,"tag":622,"props":40936,"children":40937},{"style":1196},[40938],{"type":259,"value":16988},{"type":254,"tag":622,"props":40940,"children":40942},{"style":40941},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8;--shiki-light-font-style:italic;--shiki-default-font-style:italic;--shiki-dark-font-style:italic",[40943],{"type":259,"value":40944},"vault-name",{"type":254,"tag":622,"props":40946,"children":40947},{"style":664},[40948],{"type":259,"value":40949}," httpclient-vault",{"type":254,"tag":622,"props":40951,"children":40952},{"style":1196},[40953],{"type":259,"value":16698},{"type":254,"tag":622,"props":40955,"children":40956},{"class":624,"line":679},[40957,40962],{"type":254,"tag":622,"props":40958,"children":40959},{"style":741},[40960],{"type":259,"value":40961},"  from json",{"type":254,"tag":622,"props":40963,"children":40964},{"style":1196},[40965],{"type":259,"value":16698},{"type":254,"tag":622,"props":40967,"children":40968},{"class":624,"line":705},[40969,40974,40979,40983,40987,40991,40996,41001,41005,41009,41014,41018,41022,41027,41031,41036,41041,41045,41049,41053,41057],{"type":254,"tag":622,"props":40970,"children":40971},{"style":741},[40972],{"type":259,"value":40973},"  insert",{"type":254,"tag":622,"props":40975,"children":40976},{"style":664},[40977],{"type":259,"value":40978}," value",{"type":254,"tag":622,"props":40980,"children":40981},{"style":629},[40982],{"type":259,"value":1990},{"type":254,"tag":622,"props":40984,"children":40985},{"style":1202},[40986],{"type":259,"value":17875},{"type":254,"tag":622,"props":40988,"children":40989},{"style":40941},[40990],{"type":259,"value":40799},{"type":254,"tag":622,"props":40992,"children":40993},{"style":1202},[40994],{"type":259,"value":40995},"| (",{"type":254,"tag":622,"props":40997,"children":40998},{"style":714},[40999],{"type":259,"value":41000},"az",{"type":254,"tag":622,"props":41002,"children":41003},{"style":664},[41004],{"type":259,"value":40924},{"type":254,"tag":622,"props":41006,"children":41007},{"style":664},[41008],{"type":259,"value":40929},{"type":254,"tag":622,"props":41010,"children":41011},{"style":664},[41012],{"type":259,"value":41013}," show",{"type":254,"tag":622,"props":41015,"children":41016},{"style":1196},[41017],{"type":259,"value":16988},{"type":254,"tag":622,"props":41019,"children":41020},{"style":40941},[41021],{"type":259,"value":17175},{"type":254,"tag":622,"props":41023,"children":41024},{"style":1202},[41025],{"type":259,"value":41026}," $secret.id ",{"type":254,"tag":622,"props":41028,"children":41029},{"style":1196},[41030],{"type":259,"value":17875},{"type":254,"tag":622,"props":41032,"children":41033},{"style":741},[41034],{"type":259,"value":41035}," from json",{"type":254,"tag":622,"props":41037,"children":41038},{"style":1196},[41039],{"type":259,"value":41040}," |",{"type":254,"tag":622,"props":41042,"children":41043},{"style":741},[41044],{"type":259,"value":1995},{"type":254,"tag":622,"props":41046,"children":41047},{"style":664},[41048],{"type":259,"value":40978},{"type":254,"tag":622,"props":41050,"children":41051},{"style":1202},[41052],{"type":259,"value":2309},{"type":254,"tag":622,"props":41054,"children":41055},{"style":629},[41056],{"type":259,"value":2421},{"type":254,"tag":622,"props":41058,"children":41059},{"style":1196},[41060],{"type":259,"value":16698},{"type":254,"tag":622,"props":41062,"children":41063},{"class":624,"line":732},[41064,41069,41073,41077],{"type":254,"tag":622,"props":41065,"children":41066},{"style":741},[41067],{"type":259,"value":41068},"  select",{"type":254,"tag":622,"props":41070,"children":41071},{"style":664},[41072],{"type":259,"value":16547},{"type":254,"tag":622,"props":41074,"children":41075},{"style":664},[41076],{"type":259,"value":40978},{"type":254,"tag":622,"props":41078,"children":41079},{"style":1196},[41080],{"type":259,"value":16698},{"type":254,"tag":622,"props":41082,"children":41083},{"class":624,"line":759},[41084,41089,41093,41098],{"type":254,"tag":622,"props":41085,"children":41086},{"style":741},[41087],{"type":259,"value":41088},"  transpose",{"type":254,"tag":622,"props":41090,"children":41091},{"style":1196},[41092],{"type":259,"value":10733},{"type":254,"tag":622,"props":41094,"children":41095},{"style":40941},[41096],{"type":259,"value":41097},"rd",{"type":254,"tag":622,"props":41099,"children":41100},{"style":1196},[41101],{"type":259,"value":16698},{"type":254,"tag":622,"props":41103,"children":41104},{"class":624,"line":798},[41105,41110,41115,41119,41124,41128],{"type":254,"tag":622,"props":41106,"children":41107},{"style":629},[41108],{"type":259,"value":41109},"  {",{"type":254,"tag":622,"props":41111,"children":41112},{"style":1202},[41113],{"type":259,"value":41114}," development",{"type":254,"tag":622,"props":41116,"children":41117},{"style":1196},[41118],{"type":259,"value":474},{"type":254,"tag":622,"props":41120,"children":41121},{"style":1202},[41122],{"type":259,"value":41123}," $in ",{"type":254,"tag":622,"props":41125,"children":41126},{"style":629},[41127],{"type":259,"value":2421},{"type":254,"tag":622,"props":41129,"children":41130},{"style":1196},[41131],{"type":259,"value":16698},{"type":254,"tag":622,"props":41133,"children":41134},{"class":624,"line":836},[41135,41140],{"type":254,"tag":622,"props":41136,"children":41137},{"style":741},[41138],{"type":259,"value":41139},"  save",{"type":254,"tag":622,"props":41141,"children":41142},{"style":664},[41143],{"type":259,"value":41144}," http-client.private.env.json\n",{"type":254,"tag":622,"props":41146,"children":41147},{"class":624,"line":862},[41148],{"type":254,"tag":622,"props":41149,"children":41150},{"style":1202},[41151],{"type":259,"value":1685},{"type":254,"tag":282,"props":41153,"children":41154},{"id":6186},[41155],{"type":259,"value":37936},{"type":254,"tag":255,"props":41157,"children":41158},{},[41159],{"type":259,"value":41160},"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":254,"tag":255,"props":41162,"children":41163},{},[41164,41166,41173],{"type":259,"value":41165},"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":254,"tag":262,"props":41167,"children":41170},{"href":41168,"rel":41169},"https://www.doppler.com/",[266],[41171],{"type":259,"value":41172},"Doppler",{"type":259,"value":41174}," which seems nice) that can help you manage secrets securely.",{"type":254,"tag":255,"props":41176,"children":41177},{},[41178],{"type":259,"value":41179},"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":254,"tag":1260,"props":41181,"children":41182},{},[41183],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":41185},[41186,41187,41188,41189],{"id":40242,"depth":635,"text":40245},{"id":40404,"depth":635,"text":40407},{"id":40428,"depth":635,"text":40431},{"id":6186,"depth":635,"text":37936},"content:1.posts:39.http-clients-secrets.md","1.posts/39.http-clients-secrets.md",{"_path":121,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":120,"description":41193,"lead":41193,"date":41194,"image":41195,"badge":41196,"tags":41197,"body":41199,"_type":1275,"_id":42350,"_source":231,"_file":42351,"_extension":1278},"The \"this\" in TypeScript, a must-have tool for Durable Functions, and a new git alias.","2022-05-23T00:00:00.000Z",{"src":9258},{"label":9260},[9262,41198,16850,1362,245],"TypeScript",{"type":251,"children":41200,"toc":42344},[41201,41213,41234,41239,41454,41488,41514,41619,41628,41648,41761,41793,42193,42199,42213,42222,42234,42242,42248,42262,42305,42336,42340],{"type":254,"tag":282,"props":41202,"children":41204},{"id":41203},"reminder-for-my-future-self-dont-forget-about-this-in-ts",[41205,41207],{"type":259,"value":41206},"Reminder for my future self: don't forget about \"this\" in ",{"type":254,"tag":389,"props":41208,"children":41210},{"className":41209},[],[41211],{"type":259,"value":41212},"ts",{"type":254,"tag":255,"props":41214,"children":41215},{},[41216,41218,41224,41226,41232],{"type":259,"value":41217},"Once again, I found myself forgetting that ",{"type":254,"tag":389,"props":41219,"children":41221},{"className":41220},[],[41222],{"type":259,"value":41223},"this",{"type":259,"value":41225}," can lose context in JavaScript/TypeScript which results in exceptions because ",{"type":254,"tag":389,"props":41227,"children":41229},{"className":41228},[],[41230],{"type":259,"value":41231},"this ",{"type":259,"value":41233}," is undefined. This is probably obvious for most developers but this is not a case I come across often so it's better to write it down so that I have something to refer to next time.",{"type":254,"tag":255,"props":41235,"children":41236},{},[41237],{"type":259,"value":41238},"Let's take an example:",{"type":254,"tag":612,"props":41240,"children":41243},{"className":41241,"code":41242,"language":41212,"meta":205,"style":205},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","class AudioBook {\n    private isStarted = false;\n\n    constructor(public title: string) {\n    }\n    \n    play() {\n        this.isStarted = true;\n    }\n\n    stop() {\n        this.isStarted = false;\n    }\n}\n",[41244],{"type":254,"tag":389,"props":41245,"children":41246},{"__ignoreMap":205},[41247,41264,41288,41295,41332,41339,41346,41362,41387,41394,41401,41417,41440,41447],{"type":254,"tag":622,"props":41248,"children":41249},{"class":624,"line":625},[41250,41255,41260],{"type":254,"tag":622,"props":41251,"children":41252},{"style":644},[41253],{"type":259,"value":41254},"class",{"type":254,"tag":622,"props":41256,"children":41257},{"style":714},[41258],{"type":259,"value":41259}," AudioBook",{"type":254,"tag":622,"props":41261,"children":41262},{"style":629},[41263],{"type":259,"value":702},{"type":254,"tag":622,"props":41265,"children":41266},{"class":624,"line":635},[41267,41271,41276,41280,41284],{"type":254,"tag":622,"props":41268,"children":41269},{"style":644},[41270],{"type":259,"value":6790},{"type":254,"tag":622,"props":41272,"children":41273},{"style":768},[41274],{"type":259,"value":41275}," isStarted",{"type":254,"tag":622,"props":41277,"children":41278},{"style":629},[41279],{"type":259,"value":1619},{"type":254,"tag":622,"props":41281,"children":41282},{"style":7715},[41283],{"type":259,"value":7794},{"type":254,"tag":622,"props":41285,"children":41286},{"style":629},[41287],{"type":259,"value":2350},{"type":254,"tag":622,"props":41289,"children":41290},{"class":624,"line":679},[41291],{"type":254,"tag":622,"props":41292,"children":41293},{"emptyLinePlaceholder":1699},[41294],{"type":259,"value":1702},{"type":254,"tag":622,"props":41296,"children":41297},{"class":624,"line":705},[41298,41303,41307,41311,41316,41320,41324,41328],{"type":254,"tag":622,"props":41299,"children":41300},{"style":644},[41301],{"type":259,"value":41302},"    constructor",{"type":254,"tag":622,"props":41304,"children":41305},{"style":629},[41306],{"type":259,"value":1668},{"type":254,"tag":622,"props":41308,"children":41309},{"style":644},[41310],{"type":259,"value":1437},{"type":254,"tag":622,"props":41312,"children":41313},{"style":40941},[41314],{"type":259,"value":41315}," title",{"type":254,"tag":622,"props":41317,"children":41318},{"style":629},[41319],{"type":259,"value":474},{"type":254,"tag":622,"props":41321,"children":41322},{"style":714},[41323],{"type":259,"value":1975},{"type":254,"tag":622,"props":41325,"children":41326},{"style":629},[41327],{"type":259,"value":2309},{"type":254,"tag":622,"props":41329,"children":41330},{"style":629},[41331],{"type":259,"value":702},{"type":254,"tag":622,"props":41333,"children":41334},{"class":624,"line":732},[41335],{"type":254,"tag":622,"props":41336,"children":41337},{"style":629},[41338],{"type":259,"value":1093},{"type":254,"tag":622,"props":41340,"children":41341},{"class":624,"line":759},[41342],{"type":254,"tag":622,"props":41343,"children":41344},{"style":1202},[41345],{"type":259,"value":7336},{"type":254,"tag":622,"props":41347,"children":41348},{"class":624,"line":798},[41349,41354,41358],{"type":254,"tag":622,"props":41350,"children":41351},{"style":768},[41352],{"type":259,"value":41353},"    play",{"type":254,"tag":622,"props":41355,"children":41356},{"style":629},[41357],{"type":259,"value":2724},{"type":254,"tag":622,"props":41359,"children":41360},{"style":629},[41361],{"type":259,"value":702},{"type":254,"tag":622,"props":41363,"children":41364},{"class":624,"line":836},[41365,41370,41375,41379,41383],{"type":254,"tag":622,"props":41366,"children":41367},{"style":629},[41368],{"type":259,"value":41369},"        this.",{"type":254,"tag":622,"props":41371,"children":41372},{"style":1202},[41373],{"type":259,"value":41374},"isStarted",{"type":254,"tag":622,"props":41376,"children":41377},{"style":629},[41378],{"type":259,"value":1619},{"type":254,"tag":622,"props":41380,"children":41381},{"style":7715},[41382],{"type":259,"value":7718},{"type":254,"tag":622,"props":41384,"children":41385},{"style":629},[41386],{"type":259,"value":2350},{"type":254,"tag":622,"props":41388,"children":41389},{"class":624,"line":862},[41390],{"type":254,"tag":622,"props":41391,"children":41392},{"style":629},[41393],{"type":259,"value":1093},{"type":254,"tag":622,"props":41395,"children":41396},{"class":624,"line":900},[41397],{"type":254,"tag":622,"props":41398,"children":41399},{"emptyLinePlaceholder":1699},[41400],{"type":259,"value":1702},{"type":254,"tag":622,"props":41402,"children":41403},{"class":624,"line":938},[41404,41409,41413],{"type":254,"tag":622,"props":41405,"children":41406},{"style":768},[41407],{"type":259,"value":41408},"    stop",{"type":254,"tag":622,"props":41410,"children":41411},{"style":629},[41412],{"type":259,"value":2724},{"type":254,"tag":622,"props":41414,"children":41415},{"style":629},[41416],{"type":259,"value":702},{"type":254,"tag":622,"props":41418,"children":41419},{"class":624,"line":976},[41420,41424,41428,41432,41436],{"type":254,"tag":622,"props":41421,"children":41422},{"style":629},[41423],{"type":259,"value":41369},{"type":254,"tag":622,"props":41425,"children":41426},{"style":1202},[41427],{"type":259,"value":41374},{"type":254,"tag":622,"props":41429,"children":41430},{"style":629},[41431],{"type":259,"value":1619},{"type":254,"tag":622,"props":41433,"children":41434},{"style":7715},[41435],{"type":259,"value":7794},{"type":254,"tag":622,"props":41437,"children":41438},{"style":629},[41439],{"type":259,"value":2350},{"type":254,"tag":622,"props":41441,"children":41442},{"class":624,"line":1014},[41443],{"type":254,"tag":622,"props":41444,"children":41445},{"style":629},[41446],{"type":259,"value":1093},{"type":254,"tag":622,"props":41448,"children":41449},{"class":624,"line":1052},[41450],{"type":254,"tag":622,"props":41451,"children":41452},{"style":629},[41453],{"type":259,"value":1111},{"type":254,"tag":255,"props":41455,"children":41456},{},[41457,41459,41465,41467,41472,41474,41480,41481,41487],{"type":259,"value":41458},"This is a class ",{"type":254,"tag":389,"props":41460,"children":41462},{"className":41461},[],[41463],{"type":259,"value":41464},"AudioBook",{"type":259,"value":41466}," that has a private boolean field ",{"type":254,"tag":389,"props":41468,"children":41470},{"className":41469},[],[41471],{"type":259,"value":41374},{"type":259,"value":41473}," that is modified by the two methods ",{"type":254,"tag":389,"props":41475,"children":41477},{"className":41476},[],[41478],{"type":259,"value":41479},"play",{"type":259,"value":10347},{"type":254,"tag":389,"props":41482,"children":41484},{"className":41483},[],[41485],{"type":259,"value":41486},"stop",{"type":259,"value":594},{"type":254,"tag":255,"props":41489,"children":41490},{},[41491,41493,41498,41500,41505,41507,41512],{"type":259,"value":41492},"If I create an instance of ",{"type":254,"tag":389,"props":41494,"children":41496},{"className":41495},[],[41497],{"type":259,"value":41464},{"type":259,"value":41499}," and I want to assign the ",{"type":254,"tag":389,"props":41501,"children":41503},{"className":41502},[],[41504],{"type":259,"value":41479},{"type":259,"value":41506}," function to a variable, an exception will occur when the function is run because ",{"type":254,"tag":389,"props":41508,"children":41510},{"className":41509},[],[41511],{"type":259,"value":41223},{"type":259,"value":41513}," will be undefined.",{"type":254,"tag":612,"props":41515,"children":41517},{"className":41241,"code":41516,"language":41212,"meta":205,"style":205},"const audioBook = new AudioBook(\"The Unicorn Project\");\nconst listenDevOpsBook = audioBook.play;\nlistenDevOpsBook();\n",[41518],{"type":254,"tag":389,"props":41519,"children":41520},{"__ignoreMap":205},[41521,41570,41603],{"type":254,"tag":622,"props":41522,"children":41523},{"class":624,"line":625},[41524,41528,41533,41537,41541,41545,41549,41553,41558,41562,41566],{"type":254,"tag":622,"props":41525,"children":41526},{"style":644},[41527],{"type":259,"value":24497},{"type":254,"tag":622,"props":41529,"children":41530},{"style":1202},[41531],{"type":259,"value":41532}," audioBook ",{"type":254,"tag":622,"props":41534,"children":41535},{"style":629},[41536],{"type":259,"value":1654},{"type":254,"tag":622,"props":41538,"children":41539},{"style":629},[41540],{"type":259,"value":1624},{"type":254,"tag":622,"props":41542,"children":41543},{"style":1735},[41544],{"type":259,"value":41259},{"type":254,"tag":622,"props":41546,"children":41547},{"style":1202},[41548],{"type":259,"value":1668},{"type":254,"tag":622,"props":41550,"children":41551},{"style":629},[41552],{"type":259,"value":652},{"type":254,"tag":622,"props":41554,"children":41555},{"style":664},[41556],{"type":259,"value":41557},"The Unicorn Project",{"type":254,"tag":622,"props":41559,"children":41560},{"style":629},[41561],{"type":259,"value":652},{"type":254,"tag":622,"props":41563,"children":41564},{"style":1202},[41565],{"type":259,"value":2309},{"type":254,"tag":622,"props":41567,"children":41568},{"style":629},[41569],{"type":259,"value":2350},{"type":254,"tag":622,"props":41571,"children":41572},{"class":624,"line":635},[41573,41577,41582,41586,41591,41595,41599],{"type":254,"tag":622,"props":41574,"children":41575},{"style":644},[41576],{"type":259,"value":24497},{"type":254,"tag":622,"props":41578,"children":41579},{"style":1202},[41580],{"type":259,"value":41581}," listenDevOpsBook ",{"type":254,"tag":622,"props":41583,"children":41584},{"style":629},[41585],{"type":259,"value":1654},{"type":254,"tag":622,"props":41587,"children":41588},{"style":1202},[41589],{"type":259,"value":41590}," audioBook",{"type":254,"tag":622,"props":41592,"children":41593},{"style":629},[41594],{"type":259,"value":594},{"type":254,"tag":622,"props":41596,"children":41597},{"style":1202},[41598],{"type":259,"value":41479},{"type":254,"tag":622,"props":41600,"children":41601},{"style":629},[41602],{"type":259,"value":2350},{"type":254,"tag":622,"props":41604,"children":41605},{"class":624,"line":679},[41606,41611,41615],{"type":254,"tag":622,"props":41607,"children":41608},{"style":1735},[41609],{"type":259,"value":41610},"listenDevOpsBook",{"type":254,"tag":622,"props":41612,"children":41613},{"style":1202},[41614],{"type":259,"value":2724},{"type":254,"tag":622,"props":41616,"children":41617},{"style":629},[41618],{"type":259,"value":2350},{"type":254,"tag":255,"props":41620,"children":41621},{},[41622],{"type":254,"tag":1161,"props":41623,"children":41627},{"alt":41624,"className":41625,"src":41626},"JavaScript Failed error in console output.",[1165,1166],"/posts/images/w202022tips_this_1.png",[],{"type":254,"tag":255,"props":41629,"children":41630},{},[41631,41633,41640,41642,41647],{"type":259,"value":41632},"The solution to avoid that is to use the ",{"type":254,"tag":262,"props":41634,"children":41637},{"href":41635,"rel":41636},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind",[266],[41638],{"type":259,"value":41639},"bind",{"type":259,"value":41641}," function to specify the object to use as ",{"type":254,"tag":389,"props":41643,"children":41645},{"className":41644},[],[41646],{"type":259,"value":41223},{"type":259,"value":594},{"type":254,"tag":612,"props":41649,"children":41651},{"className":41241,"code":41650,"language":41212,"meta":205,"style":205},"const audioBook = new AudioBook(\"The Unicorn Project\");\nconst listenDevOpsBook = audioBook.play.bind(audioBook);\nlistenDevOpsBook();\n",[41652],{"type":254,"tag":389,"props":41653,"children":41654},{"__ignoreMap":205},[41655,41702,41746],{"type":254,"tag":622,"props":41656,"children":41657},{"class":624,"line":625},[41658,41662,41666,41670,41674,41678,41682,41686,41690,41694,41698],{"type":254,"tag":622,"props":41659,"children":41660},{"style":644},[41661],{"type":259,"value":24497},{"type":254,"tag":622,"props":41663,"children":41664},{"style":1202},[41665],{"type":259,"value":41532},{"type":254,"tag":622,"props":41667,"children":41668},{"style":629},[41669],{"type":259,"value":1654},{"type":254,"tag":622,"props":41671,"children":41672},{"style":629},[41673],{"type":259,"value":1624},{"type":254,"tag":622,"props":41675,"children":41676},{"style":1735},[41677],{"type":259,"value":41259},{"type":254,"tag":622,"props":41679,"children":41680},{"style":1202},[41681],{"type":259,"value":1668},{"type":254,"tag":622,"props":41683,"children":41684},{"style":629},[41685],{"type":259,"value":652},{"type":254,"tag":622,"props":41687,"children":41688},{"style":664},[41689],{"type":259,"value":41557},{"type":254,"tag":622,"props":41691,"children":41692},{"style":629},[41693],{"type":259,"value":652},{"type":254,"tag":622,"props":41695,"children":41696},{"style":1202},[41697],{"type":259,"value":2309},{"type":254,"tag":622,"props":41699,"children":41700},{"style":629},[41701],{"type":259,"value":2350},{"type":254,"tag":622,"props":41703,"children":41704},{"class":624,"line":635},[41705,41709,41713,41717,41721,41725,41729,41733,41737,41742],{"type":254,"tag":622,"props":41706,"children":41707},{"style":644},[41708],{"type":259,"value":24497},{"type":254,"tag":622,"props":41710,"children":41711},{"style":1202},[41712],{"type":259,"value":41581},{"type":254,"tag":622,"props":41714,"children":41715},{"style":629},[41716],{"type":259,"value":1654},{"type":254,"tag":622,"props":41718,"children":41719},{"style":1202},[41720],{"type":259,"value":41590},{"type":254,"tag":622,"props":41722,"children":41723},{"style":629},[41724],{"type":259,"value":594},{"type":254,"tag":622,"props":41726,"children":41727},{"style":1202},[41728],{"type":259,"value":41479},{"type":254,"tag":622,"props":41730,"children":41731},{"style":629},[41732],{"type":259,"value":594},{"type":254,"tag":622,"props":41734,"children":41735},{"style":1735},[41736],{"type":259,"value":41639},{"type":254,"tag":622,"props":41738,"children":41739},{"style":1202},[41740],{"type":259,"value":41741},"(audioBook)",{"type":254,"tag":622,"props":41743,"children":41744},{"style":629},[41745],{"type":259,"value":2350},{"type":254,"tag":622,"props":41747,"children":41748},{"class":624,"line":679},[41749,41753,41757],{"type":254,"tag":622,"props":41750,"children":41751},{"style":1735},[41752],{"type":259,"value":41610},{"type":254,"tag":622,"props":41754,"children":41755},{"style":1202},[41756],{"type":259,"value":2724},{"type":254,"tag":622,"props":41758,"children":41759},{"style":629},[41760],{"type":259,"value":2350},{"type":254,"tag":255,"props":41762,"children":41763},{},[41764,41766,41771,41772,41777,41779,41784,41786,41791],{"type":259,"value":41765},"To avoid having to use bind everywhere the ",{"type":254,"tag":389,"props":41767,"children":41769},{"className":41768},[],[41770],{"type":259,"value":41479},{"type":259,"value":40218},{"type":254,"tag":389,"props":41773,"children":41775},{"className":41774},[],[41776],{"type":259,"value":41486},{"type":259,"value":41778}," methods are used, we can do the ",{"type":254,"tag":389,"props":41780,"children":41782},{"className":41781},[],[41783],{"type":259,"value":41639},{"type":259,"value":41785}," thing directly in the constructor of the ",{"type":254,"tag":389,"props":41787,"children":41789},{"className":41788},[],[41790],{"type":259,"value":41464},{"type":259,"value":41792}," class.",{"type":254,"tag":612,"props":41794,"children":41796},{"className":41241,"code":41795,"language":41212,"meta":205,"style":205},"class AudioBook {\n    private isStarted = false;\n\n    constructor(public title: string) {\n        this.play = this.play.bind(this);\n        this.stop = this.stop.bind(this);\n    }\n    \n    play() {\n        this.isStarted = true;\n    }\n\n    stop() {\n        this.isStarted = false;\n    }\n}\n\nconst audioBook = new AudioBook(\"The Unicorn Project\");\nconst listenDevOpsBook = audioBook.play;\nlistenDevOpsBook();\n",[41797],{"type":254,"tag":389,"props":41798,"children":41799},{"__ignoreMap":205},[41800,41815,41838,41845,41880,41928,41975,41982,41989,42004,42027,42034,42041,42056,42079,42086,42093,42100,42147,42178],{"type":254,"tag":622,"props":41801,"children":41802},{"class":624,"line":625},[41803,41807,41811],{"type":254,"tag":622,"props":41804,"children":41805},{"style":644},[41806],{"type":259,"value":41254},{"type":254,"tag":622,"props":41808,"children":41809},{"style":714},[41810],{"type":259,"value":41259},{"type":254,"tag":622,"props":41812,"children":41813},{"style":629},[41814],{"type":259,"value":702},{"type":254,"tag":622,"props":41816,"children":41817},{"class":624,"line":635},[41818,41822,41826,41830,41834],{"type":254,"tag":622,"props":41819,"children":41820},{"style":644},[41821],{"type":259,"value":6790},{"type":254,"tag":622,"props":41823,"children":41824},{"style":768},[41825],{"type":259,"value":41275},{"type":254,"tag":622,"props":41827,"children":41828},{"style":629},[41829],{"type":259,"value":1619},{"type":254,"tag":622,"props":41831,"children":41832},{"style":7715},[41833],{"type":259,"value":7794},{"type":254,"tag":622,"props":41835,"children":41836},{"style":629},[41837],{"type":259,"value":2350},{"type":254,"tag":622,"props":41839,"children":41840},{"class":624,"line":679},[41841],{"type":254,"tag":622,"props":41842,"children":41843},{"emptyLinePlaceholder":1699},[41844],{"type":259,"value":1702},{"type":254,"tag":622,"props":41846,"children":41847},{"class":624,"line":705},[41848,41852,41856,41860,41864,41868,41872,41876],{"type":254,"tag":622,"props":41849,"children":41850},{"style":644},[41851],{"type":259,"value":41302},{"type":254,"tag":622,"props":41853,"children":41854},{"style":629},[41855],{"type":259,"value":1668},{"type":254,"tag":622,"props":41857,"children":41858},{"style":644},[41859],{"type":259,"value":1437},{"type":254,"tag":622,"props":41861,"children":41862},{"style":40941},[41863],{"type":259,"value":41315},{"type":254,"tag":622,"props":41865,"children":41866},{"style":629},[41867],{"type":259,"value":474},{"type":254,"tag":622,"props":41869,"children":41870},{"style":714},[41871],{"type":259,"value":1975},{"type":254,"tag":622,"props":41873,"children":41874},{"style":629},[41875],{"type":259,"value":2309},{"type":254,"tag":622,"props":41877,"children":41878},{"style":629},[41879],{"type":259,"value":702},{"type":254,"tag":622,"props":41881,"children":41882},{"class":624,"line":732},[41883,41887,41891,41895,41900,41904,41908,41912,41916,41920,41924],{"type":254,"tag":622,"props":41884,"children":41885},{"style":629},[41886],{"type":259,"value":41369},{"type":254,"tag":622,"props":41888,"children":41889},{"style":1202},[41890],{"type":259,"value":41479},{"type":254,"tag":622,"props":41892,"children":41893},{"style":629},[41894],{"type":259,"value":1619},{"type":254,"tag":622,"props":41896,"children":41897},{"style":629},[41898],{"type":259,"value":41899}," this.",{"type":254,"tag":622,"props":41901,"children":41902},{"style":1202},[41903],{"type":259,"value":41479},{"type":254,"tag":622,"props":41905,"children":41906},{"style":629},[41907],{"type":259,"value":594},{"type":254,"tag":622,"props":41909,"children":41910},{"style":1735},[41911],{"type":259,"value":41639},{"type":254,"tag":622,"props":41913,"children":41914},{"style":768},[41915],{"type":259,"value":1668},{"type":254,"tag":622,"props":41917,"children":41918},{"style":629},[41919],{"type":259,"value":41223},{"type":254,"tag":622,"props":41921,"children":41922},{"style":768},[41923],{"type":259,"value":2309},{"type":254,"tag":622,"props":41925,"children":41926},{"style":629},[41927],{"type":259,"value":2350},{"type":254,"tag":622,"props":41929,"children":41930},{"class":624,"line":759},[41931,41935,41939,41943,41947,41951,41955,41959,41963,41967,41971],{"type":254,"tag":622,"props":41932,"children":41933},{"style":629},[41934],{"type":259,"value":41369},{"type":254,"tag":622,"props":41936,"children":41937},{"style":1202},[41938],{"type":259,"value":41486},{"type":254,"tag":622,"props":41940,"children":41941},{"style":629},[41942],{"type":259,"value":1619},{"type":254,"tag":622,"props":41944,"children":41945},{"style":629},[41946],{"type":259,"value":41899},{"type":254,"tag":622,"props":41948,"children":41949},{"style":1202},[41950],{"type":259,"value":41486},{"type":254,"tag":622,"props":41952,"children":41953},{"style":629},[41954],{"type":259,"value":594},{"type":254,"tag":622,"props":41956,"children":41957},{"style":1735},[41958],{"type":259,"value":41639},{"type":254,"tag":622,"props":41960,"children":41961},{"style":768},[41962],{"type":259,"value":1668},{"type":254,"tag":622,"props":41964,"children":41965},{"style":629},[41966],{"type":259,"value":41223},{"type":254,"tag":622,"props":41968,"children":41969},{"style":768},[41970],{"type":259,"value":2309},{"type":254,"tag":622,"props":41972,"children":41973},{"style":629},[41974],{"type":259,"value":2350},{"type":254,"tag":622,"props":41976,"children":41977},{"class":624,"line":798},[41978],{"type":254,"tag":622,"props":41979,"children":41980},{"style":629},[41981],{"type":259,"value":1093},{"type":254,"tag":622,"props":41983,"children":41984},{"class":624,"line":836},[41985],{"type":254,"tag":622,"props":41986,"children":41987},{"style":1202},[41988],{"type":259,"value":7336},{"type":254,"tag":622,"props":41990,"children":41991},{"class":624,"line":862},[41992,41996,42000],{"type":254,"tag":622,"props":41993,"children":41994},{"style":768},[41995],{"type":259,"value":41353},{"type":254,"tag":622,"props":41997,"children":41998},{"style":629},[41999],{"type":259,"value":2724},{"type":254,"tag":622,"props":42001,"children":42002},{"style":629},[42003],{"type":259,"value":702},{"type":254,"tag":622,"props":42005,"children":42006},{"class":624,"line":900},[42007,42011,42015,42019,42023],{"type":254,"tag":622,"props":42008,"children":42009},{"style":629},[42010],{"type":259,"value":41369},{"type":254,"tag":622,"props":42012,"children":42013},{"style":1202},[42014],{"type":259,"value":41374},{"type":254,"tag":622,"props":42016,"children":42017},{"style":629},[42018],{"type":259,"value":1619},{"type":254,"tag":622,"props":42020,"children":42021},{"style":7715},[42022],{"type":259,"value":7718},{"type":254,"tag":622,"props":42024,"children":42025},{"style":629},[42026],{"type":259,"value":2350},{"type":254,"tag":622,"props":42028,"children":42029},{"class":624,"line":938},[42030],{"type":254,"tag":622,"props":42031,"children":42032},{"style":629},[42033],{"type":259,"value":1093},{"type":254,"tag":622,"props":42035,"children":42036},{"class":624,"line":976},[42037],{"type":254,"tag":622,"props":42038,"children":42039},{"emptyLinePlaceholder":1699},[42040],{"type":259,"value":1702},{"type":254,"tag":622,"props":42042,"children":42043},{"class":624,"line":1014},[42044,42048,42052],{"type":254,"tag":622,"props":42045,"children":42046},{"style":768},[42047],{"type":259,"value":41408},{"type":254,"tag":622,"props":42049,"children":42050},{"style":629},[42051],{"type":259,"value":2724},{"type":254,"tag":622,"props":42053,"children":42054},{"style":629},[42055],{"type":259,"value":702},{"type":254,"tag":622,"props":42057,"children":42058},{"class":624,"line":1052},[42059,42063,42067,42071,42075],{"type":254,"tag":622,"props":42060,"children":42061},{"style":629},[42062],{"type":259,"value":41369},{"type":254,"tag":622,"props":42064,"children":42065},{"style":1202},[42066],{"type":259,"value":41374},{"type":254,"tag":622,"props":42068,"children":42069},{"style":629},[42070],{"type":259,"value":1619},{"type":254,"tag":622,"props":42072,"children":42073},{"style":7715},[42074],{"type":259,"value":7794},{"type":254,"tag":622,"props":42076,"children":42077},{"style":629},[42078],{"type":259,"value":2350},{"type":254,"tag":622,"props":42080,"children":42081},{"class":624,"line":1078},[42082],{"type":254,"tag":622,"props":42083,"children":42084},{"style":629},[42085],{"type":259,"value":1093},{"type":254,"tag":622,"props":42087,"children":42088},{"class":624,"line":1087},[42089],{"type":254,"tag":622,"props":42090,"children":42091},{"style":629},[42092],{"type":259,"value":1111},{"type":254,"tag":622,"props":42094,"children":42095},{"class":624,"line":1096},[42096],{"type":254,"tag":622,"props":42097,"children":42098},{"emptyLinePlaceholder":1699},[42099],{"type":259,"value":1702},{"type":254,"tag":622,"props":42101,"children":42102},{"class":624,"line":1105},[42103,42107,42111,42115,42119,42123,42127,42131,42135,42139,42143],{"type":254,"tag":622,"props":42104,"children":42105},{"style":644},[42106],{"type":259,"value":24497},{"type":254,"tag":622,"props":42108,"children":42109},{"style":1202},[42110],{"type":259,"value":41532},{"type":254,"tag":622,"props":42112,"children":42113},{"style":629},[42114],{"type":259,"value":1654},{"type":254,"tag":622,"props":42116,"children":42117},{"style":629},[42118],{"type":259,"value":1624},{"type":254,"tag":622,"props":42120,"children":42121},{"style":1735},[42122],{"type":259,"value":41259},{"type":254,"tag":622,"props":42124,"children":42125},{"style":1202},[42126],{"type":259,"value":1668},{"type":254,"tag":622,"props":42128,"children":42129},{"style":629},[42130],{"type":259,"value":652},{"type":254,"tag":622,"props":42132,"children":42133},{"style":664},[42134],{"type":259,"value":41557},{"type":254,"tag":622,"props":42136,"children":42137},{"style":629},[42138],{"type":259,"value":652},{"type":254,"tag":622,"props":42140,"children":42141},{"style":1202},[42142],{"type":259,"value":2309},{"type":254,"tag":622,"props":42144,"children":42145},{"style":629},[42146],{"type":259,"value":2350},{"type":254,"tag":622,"props":42148,"children":42149},{"class":624,"line":4131},[42150,42154,42158,42162,42166,42170,42174],{"type":254,"tag":622,"props":42151,"children":42152},{"style":644},[42153],{"type":259,"value":24497},{"type":254,"tag":622,"props":42155,"children":42156},{"style":1202},[42157],{"type":259,"value":41581},{"type":254,"tag":622,"props":42159,"children":42160},{"style":629},[42161],{"type":259,"value":1654},{"type":254,"tag":622,"props":42163,"children":42164},{"style":1202},[42165],{"type":259,"value":41590},{"type":254,"tag":622,"props":42167,"children":42168},{"style":629},[42169],{"type":259,"value":594},{"type":254,"tag":622,"props":42171,"children":42172},{"style":1202},[42173],{"type":259,"value":41479},{"type":254,"tag":622,"props":42175,"children":42176},{"style":629},[42177],{"type":259,"value":2350},{"type":254,"tag":622,"props":42179,"children":42180},{"class":624,"line":4183},[42181,42185,42189],{"type":254,"tag":622,"props":42182,"children":42183},{"style":1735},[42184],{"type":259,"value":41610},{"type":254,"tag":622,"props":42186,"children":42187},{"style":1202},[42188],{"type":259,"value":2724},{"type":254,"tag":622,"props":42190,"children":42191},{"style":629},[42192],{"type":259,"value":2350},{"type":254,"tag":282,"props":42194,"children":42196},{"id":42195},"tool-of-the-week-durable-functions-monitor",[42197],{"type":259,"value":42198},"Tool of the week: Durable Functions Monitor ⚡",{"type":254,"tag":255,"props":42200,"children":42201},{},[42202,42204,42211],{"type":259,"value":42203},"If you are an Azure developer, you are probably already familiar with Azure Functions which is one of the solutions to do serverless in Azure. And, you may also have used Durable Functions to build serverless workflows. If I quote ",{"type":254,"tag":262,"props":42205,"children":42208},{"href":42206,"rel":42207},"https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-overview?tabs=csharp",[266],[42209],{"type":259,"value":42210},"Microsoft documentation",{"type":259,"value":42212},": \"Durable Functions is an extension of Azure Functions that lets you write stateful functions in a serverless compute environment.\" In concrete terms, if you are developing multiple Azure Functions and you want to orchestrate their execution while maintaining a state, Durable Functions are what you need. I like this technology a lot ❤️. When used correctly and for the right purpose, it can solve many issues you would face to implementing manually a workflow. Enough talk, let's go back to the tool of the week!",{"type":254,"tag":255,"props":42214,"children":42215},{},[42216],{"type":254,"tag":1161,"props":42217,"children":42221},{"alt":42218,"className":42219,"src":42220},"GitHub README of the Durable Functions Monitor project.",[1165,1166],"/posts/images/w202022tips_durablemonitor_1.png",[],{"type":254,"tag":255,"props":42223,"children":42224},{},[42225,42232],{"type":254,"tag":262,"props":42226,"children":42229},{"href":42227,"rel":42228},"https://github.com/microsoft/DurableFunctionsMonitor",[266],[42230],{"type":259,"value":42231},"Durable Functions Monitor",{"type":259,"value":42233}," is a UI tool that allows you to monitor, manage and debug your Azure Durable Functions. That's something very valuable because the tooling in Azure Portal is very poor for Durable Functions. Moreover, because a workflow can last for a long time and is often composed of many Azure Functions, it can be quite hard to understand at what stage the workflow is and what functions have already been executed 🤔. Durable Functions can help you with that and offers many other interesting features (functions graph, sequence diagram...). I heard about it a while ago but did not take the time to try it until recently and honestly it's too bad because it's a must-have to work with Azure Durable Functions 🚀!",{"type":254,"tag":427,"props":42235,"children":42236},{"icon":567},[42237],{"type":254,"tag":255,"props":42238,"children":42239},{},[42240],{"type":259,"value":42241},"It's worth noting that Durable Functions Monitor can be used as a vscode extension, as a Standalone service, or directly in your Function App.",{"type":254,"tag":282,"props":42243,"children":42245},{"id":42244},"git-tip-of-the-week-alias-to-force-push-commits",[42246],{"type":259,"value":42247},"Git tip of the week: alias to force push commits",{"type":254,"tag":255,"props":42249,"children":42250},{},[42251,42253,42260],{"type":259,"value":42252},"I often force push changes on my git branches. Indeed, I try to keep a clean and easy to read the history on my branches by using the ",{"type":254,"tag":262,"props":42254,"children":42257},{"href":42255,"rel":42256},"https://www.techwatching.dev/gitcheatsheet#when-you-want-to-have-a-clean-commit-history-on-your-branch-before-creating-your-pull-request",[266],[42258],{"type":259,"value":42259},"interactive rebase command",{"type":259,"value":42261}," so that it's easier for my colleagues to review my Pull Requests and I can use a rebase merging strategy instead of squashing my changes in a big commit when completing them.",{"type":254,"tag":255,"props":42263,"children":42264},{},[42265,42267,42273,42275,42281,42283,42289,42291,42297,42299,42304],{"type":259,"value":42266},"Before, I was using the ",{"type":254,"tag":389,"props":42268,"children":42270},{"className":42269},[],[42271],{"type":259,"value":42272},"git push --force",{"type":259,"value":42274}," command but reading a few articles on the topic convinced me that I should use the ",{"type":254,"tag":389,"props":42276,"children":42278},{"className":42277},[],[42279],{"type":259,"value":42280},"git push --force-with-lease",{"type":259,"value":42282}," command instead to avoid crushing commits colleagues could have done on my branch (even if there is little risk as I only rewrite the history of already pushed changes when working alone on a branch). You can read more about ",{"type":254,"tag":389,"props":42284,"children":42286},{"className":42285},[],[42287],{"type":259,"value":42288},"--force-with-lease",{"type":259,"value":42290}," on the git ",{"type":254,"tag":262,"props":42292,"children":42295},{"href":42293,"rel":42294},"https://git-scm.com/docs/git-push#Documentation/git-push.txt---force-with-leaseltrefnamegt",[266],[42296],{"type":259,"value":6340},{"type":259,"value":42298},". The only problem with this command is that it's a bit long to write so here is an alias to add to your ",{"type":254,"tag":389,"props":42300,"children":42302},{"className":42301},[],[42303],{"type":259,"value":38304},{"type":259,"value":474},{"type":254,"tag":612,"props":42306,"children":42308},{"className":16381,"code":42307,"language":230,"meta":205,"style":205},"[alias]\n    pf = push origin --force-with-lease\n",[42309],{"type":254,"tag":389,"props":42310,"children":42311},{"__ignoreMap":205},[42312,42328],{"type":254,"tag":622,"props":42313,"children":42314},{"class":624,"line":625},[42315,42319,42324],{"type":254,"tag":622,"props":42316,"children":42317},{"style":629},[42318],{"type":259,"value":10993},{"type":254,"tag":622,"props":42320,"children":42321},{"style":664},[42322],{"type":259,"value":42323},"alias",{"type":254,"tag":622,"props":42325,"children":42326},{"style":629},[42327],{"type":259,"value":11003},{"type":254,"tag":622,"props":42329,"children":42330},{"class":624,"line":635},[42331],{"type":254,"tag":622,"props":42332,"children":42333},{"style":664},[42334],{"type":259,"value":42335},"    pf = push origin --force-with-lease\n",{"type":254,"tag":255,"props":42337,"children":42338},{},[42339],{"type":259,"value":10040},{"type":254,"tag":1260,"props":42341,"children":42342},{},[42343],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":42345},[42346,42348,42349],{"id":41203,"depth":635,"text":42347},"Reminder for my future self: don't forget about \"this\" in ts",{"id":42195,"depth":635,"text":42198},{"id":42244,"depth":635,"text":42247},"content:1.posts:38.w20-2022-tips-learned-this-week.md","1.posts/38.w20-2022-tips-learned-this-week.md",{"_path":118,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":117,"description":42353,"lead":42353,"date":42354,"image":42355,"badge":42356,"tags":42357,"body":42359,"_type":1275,"_id":42570,"_source":231,"_file":42571,"_extension":1278},"Git commands in vscode, a nice tool for Vue developers and a must-have Visual Studio extension.","2022-05-14T00:00:00.000Z",{"src":9258},{"label":9260},[9262,37215,16850,31320,42358,245],"Visual Studio",{"type":251,"children":42360,"toc":42565},[42361,42367,42399,42408,42413,42422,42434,42440,42487,42496,42509,42518,42524,42538,42547,42552,42561],{"type":254,"tag":282,"props":42362,"children":42364},{"id":42363},"git-tip-of-the-week",[42365],{"type":259,"value":42366},"Git tip of the week",{"type":254,"tag":255,"props":42368,"children":42369},{},[42370,42372,42379,42381,42388,42390,42397],{"type":259,"value":42371},"If you have read my ",{"type":254,"tag":262,"props":42373,"children":42376},{"href":42374,"rel":42375},"https://www.techwatching.dev/gitcheatsheet",[266],[42377],{"type":259,"value":42378},"git cheat sheet",{"type":259,"value":42380},", you know that I am a big fan of the ",{"type":254,"tag":262,"props":42382,"children":42385},{"href":42383,"rel":42384},"https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens",[266],[42386],{"type":259,"value":42387},"GitLens",{"type":259,"value":42389}," vscode extension. I have been using it for a while now but just discovered recently that there is a ",{"type":254,"tag":262,"props":42391,"children":42394},{"href":42392,"rel":42393},"https://github.com/gitkraken/vscode-gitlens#git-command-palette-",[266],[42395],{"type":259,"value":42396},"Git Command Palette",{"type":259,"value":42398}," that gives access to most common Git commands.",{"type":254,"tag":255,"props":42400,"children":42401},{},[42402],{"type":254,"tag":1161,"props":42403,"children":42407},{"alt":42404,"className":42405,"src":42406,"width":19735},"GitLens Command Palette in vscode",[1165,1166],"/posts/images/w192022tips_gitlens_1.png",[],{"type":254,"tag":255,"props":42409,"children":42410},{},[42411],{"type":259,"value":42412},"Usually, I prefer typing the git commands rather than using a visual tool. This way, I know exactly what I am doing (no magic commands done by a tool behind the scene), and I improve my knowledge of git. However, I think that with Git Command Palette, I get the best of both worlds. The UI helps me to use the git command I need without having to type everything and remember the exact syntax of the command. Yet, this is not a UI with buttons that hide from me the git commands being run. I am still aware of the exact git commands I am using and how.",{"type":254,"tag":255,"props":42414,"children":42415},{},[42416],{"type":254,"tag":1161,"props":42417,"children":42421},{"alt":42418,"className":42419,"src":42420,"width":19735},"Doing a Git fetch in GitLens Command Palette.",[1165,1166],"/posts/images/w192022tips_gitlens_2.png",[],{"type":254,"tag":255,"props":42423,"children":42424},{},[42425,42427,42433],{"type":259,"value":42426},"On Windows, the default shortcut to use the Git Command Palette is ",{"type":254,"tag":389,"props":42428,"children":42430},{"className":42429},[],[42431],{"type":259,"value":42432},"Ctrl + Shift + G : ",{"type":259,"value":594},{"type":254,"tag":282,"props":42435,"children":42437},{"id":42436},"tool-of-the-week-vue-telescope",[42438],{"type":259,"value":42439},"Tool of the week: Vue Telescope",{"type":254,"tag":255,"props":42441,"children":42442},{},[42443,42445,42452,42454,42461,42463,42469,42471,42478,42480,42485],{"type":259,"value":42444},"If you are a Vue developer and don't know this tool yet, this is going to make your day! When browsing a website, you are probably wondering if it has been made with Vue.js and if so what is the technology stack behind it. Personally, I find it very interesting to know which frameworks, libraries, or plugins have been used to create a website in Vue. And that's what ",{"type":254,"tag":262,"props":42446,"children":42449},{"href":42447,"rel":42448},"https://vuetelescope.com/",[266],[42450],{"type":259,"value":42451},"Vue Telescope",{"type":259,"value":42453}," is about. It's an open source tool made by ",{"type":254,"tag":262,"props":42455,"children":42458},{"href":42456,"rel":42457},"https://nuxtlabs.com/",[266],[42459],{"type":259,"value":42460},"NuxtLabs",{"type":259,"value":42462}," (the team behind the ",{"type":254,"tag":262,"props":42464,"children":42467},{"href":42465,"rel":42466},"https://nuxtjs.org/",[266],[42468],{"type":259,"value":31235},{"type":259,"value":42470}," framework) that detects the Vue technologies used in a website. It can be used from a browser ",{"type":254,"tag":262,"props":42472,"children":42475},{"href":42473,"rel":42474},"https://chrome.google.com/webstore/detail/vue-telescope/neaebjphlfplgdhedjdhcnpjkndddbpd",[266],[42476],{"type":259,"value":42477},"extension",{"type":259,"value":42479}," or from Vue Telescope's ",{"type":254,"tag":262,"props":42481,"children":42483},{"href":42447,"rel":42482},[266],[42484],{"type":259,"value":1323},{"type":259,"value":42486}," to search a analyze a specific website.",{"type":254,"tag":255,"props":42488,"children":42489},{},[42490],{"type":254,"tag":1161,"props":42491,"children":42495},{"alt":42492,"className":42493,"src":42494,"width":19735},"Vue Telescope extension showing Vue Telescope stack on its website.",[1165,1166],"/posts/images/w192022tips_vuetelescope_1.png",[],{"type":254,"tag":255,"props":42497,"children":42498},{},[42499,42501,42507],{"type":259,"value":42500},"You can explore the Vue.js websites already scanned by VueTelescope ",{"type":254,"tag":262,"props":42502,"children":42505},{"href":42503,"rel":42504},"https://vuetelescope.com/explore",[266],[42506],{"type":259,"value":1414},{"type":259,"value":42508}," and filter on the frameworks, UI Frameworks you are interested in.",{"type":254,"tag":255,"props":42510,"children":42511},{},[42512],{"type":254,"tag":1161,"props":42513,"children":42517},{"alt":42514,"className":42515,"src":42516,"width":19735},"Vue Telescope search.",[1165,1166],"/posts/images/w192022tips_vuetelescope_2.png",[],{"type":254,"tag":282,"props":42519,"children":42521},{"id":42520},"the-visual-studio-extension-you-should-try-add-new-file",[42522],{"type":259,"value":42523},"The Visual Studio extension you should try: Add New File",{"type":254,"tag":255,"props":42525,"children":42526},{},[42527,42529,42536],{"type":259,"value":42528},"Sometimes the simplest IDE extensions are the best. That's the case for the ",{"type":254,"tag":262,"props":42530,"children":42533},{"href":42531,"rel":42532},"https://marketplace.visualstudio.com/items?itemName=MadsKristensen.AddNewFile64",[266],[42534],{"type":259,"value":42535},"\"Add New\"",{"type":259,"value":42537}," Visual Studio extension which allows you to quickly create a new file by hitting \"Shift+F2\" and writing the name of the file with its extension. Nothing fancy, but it saves you a lot of time compared to adding a new file using the default dialog.",{"type":254,"tag":255,"props":42539,"children":42540},{},[42541],{"type":254,"tag":1161,"props":42542,"children":42546},{"alt":42543,"className":42544,"src":42545,"width":19735},"Website of Add New File Visual Studio extension.",[1165,1166],"/posts/images/w192022tips_addnewfile_1.png",[],{"type":254,"tag":255,"props":42548,"children":42549},{},[42550],{"type":259,"value":42551},"As you can see you can even create the missing folders where the file is placed.",{"type":254,"tag":255,"props":42553,"children":42554},{},[42555],{"type":254,"tag":1161,"props":42556,"children":42560},{"alt":42557,"className":42558,"src":42559,"width":19735},"Usage of Add New File in Visual Studio.",[1165,1166],"/posts/images/w192022tips_addnewfile.gif",[],{"type":254,"tag":255,"props":42562,"children":42563},{},[42564],{"type":259,"value":10040},{"title":205,"searchDepth":635,"depth":635,"links":42566},[42567,42568,42569],{"id":42363,"depth":635,"text":42366},{"id":42436,"depth":635,"text":42439},{"id":42520,"depth":635,"text":42523},"content:1.posts:37.w19-2022-tips-learned-this-week.md","1.posts/37.w19-2022-tips-learned-this-week.md",{"_path":115,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":114,"description":42573,"lead":42574,"date":42575,"image":42576,"badge":42578,"tags":42579,"body":42580,"_type":1275,"_id":43844,"_source":231,"_file":43845,"_extension":1278},"In today's world of cloud-first applications, multi-cloud/hybrid cloud companies, and complex infrastructures, using infrastructure as code is essential. In recent years, Terraform has become one of the most popular IaC solutions, but its challenger Pulumi is quickly gaining traction. In this article, I will tell you why I think Pulumi is better and why I will choose it over Terraform for my next project.","My take on choosing an Infrastructure as solution","2022-05-02T00:00:00.000Z",{"src":42577},"/images/clouds_1.jpg",{"label":16349},[19569,10225,9968,16349],{"type":251,"children":42581,"toc":43813},[42582,42586,42591,42597,42602,42607,42680,42689,42694,42700,42705,42711,42733,42751,42760,42781,42797,42803,42808,42817,42829,42846,42859,42882,42891,42896,42914,42920,42949,42958,42980,42996,43010,43033,43042,43048,43053,43067,43072,43104,43124,43133,43139,43153,43159,43165,43170,43175,43184,43189,43195,43200,43209,43214,43230,43243,43256,43265,43271,43276,43281,43299,43310,43319,43324,43330,43344,43362,43370,43387,43392,43401,43406,43412,43426,43434,43439,43460,43466,43471,43476,43482,43487,43492,43501,43506,43512,43517,43522,43531,43536,43541,43549,43555,43560,43568,43573,43587,43599,43605,43610,43615,43621,43643,43647,43653,43658,43686,43695,43701,43706,43711,43716,43722,43727,43732,43737,43742,43748,43753,43758,43770,43775,43781,43786,43791,43796,43808],{"type":254,"tag":255,"props":42583,"children":42584},{},[42585],{"type":259,"value":42573},{"type":254,"tag":255,"props":42587,"children":42588},{},[42589],{"type":259,"value":42590},"But first, let's talk about what makes a good Infrastructure as Code solution.",{"type":254,"tag":282,"props":42592,"children":42594},{"id":42593},"what-makes-a-good-infrastructure-as-code-solution",[42595],{"type":259,"value":42596},"What makes a good Infrastructure as Code solution?",{"type":254,"tag":255,"props":42598,"children":42599},{},[42600],{"type":259,"value":42601},"There is no universal answer to this question, but I can give you the characteristics I am looking for in an IaC solution.",{"type":254,"tag":255,"props":42603,"children":42604},{},[42605],{"type":259,"value":42606},"In my opinion, an IaC solution should be:",{"type":254,"tag":332,"props":42608,"children":42609},{},[42610,42620,42630,42640,42650,42660,42670],{"type":254,"tag":336,"props":42611,"children":42612},{},[42613,42618],{"type":254,"tag":9718,"props":42614,"children":42615},{},[42616],{"type":259,"value":42617},"declarative",{"type":259,"value":42619},". I think it is important to focus on what infrastructure we want to provision rather than how to provision it",{"type":254,"tag":336,"props":42621,"children":42622},{},[42623,42628],{"type":254,"tag":9718,"props":42624,"children":42625},{},[42626],{"type":259,"value":42627},"open source",{"type":259,"value":42629},". Beyond being a good thing, open source favors the adoption of technology and makes an ecosystem healthy with contributions from the community",{"type":254,"tag":336,"props":42631,"children":42632},{},[42633,42638],{"type":254,"tag":9718,"props":42634,"children":42635},{},[42636],{"type":259,"value":42637},"multi providers",{"type":259,"value":42639},". Even if I work mainly with Azure, companies tend to be multi clouds; projects often involve provisioning resources in different cloud providers and services. Moreover, beyond cloud providers, I want to be able to automate the provisioning of many things like my Azure DevOps or GitHub projects.",{"type":254,"tag":336,"props":42641,"children":42642},{},[42643,42648],{"type":254,"tag":9718,"props":42644,"children":42645},{},[42646],{"type":259,"value":42647},"easy to learn, easy to use, and easy to be productive with",{"type":259,"value":42649},". I love learning new things but even more when it's easy and I can be quickly productive with new technology.",{"type":254,"tag":336,"props":42651,"children":42652},{},[42653,42658],{"type":254,"tag":9718,"props":42654,"children":42655},{},[42656],{"type":259,"value":42657},"up to date with main cloud providers' resources and features",{"type":259,"value":42659},". IT (and specifically the cloud) is evolving very quickly. The infrastructure we provision needs to be able to benefit from the latest resources, innovations, security improvements... I don't want to have to wait for novelties to be available",{"type":254,"tag":336,"props":42661,"children":42662},{},[42663,42668],{"type":254,"tag":9718,"props":42664,"children":42665},{},[42666],{"type":259,"value":42667},"flexible and customizable",{"type":259,"value":42669},". Cloud infrastructures are more and more complex, and each project has its specificities so flexibility and the ability to easily write custom code to address these specificities are important",{"type":254,"tag":336,"props":42671,"children":42672},{},[42673,42678],{"type":254,"tag":9718,"props":42674,"children":42675},{},[42676],{"type":259,"value":42677},"secure",{"type":259,"value":42679},". Security in IT is of paramount importance (especially when dealing with infrastructure) and therefore should be built-in",{"type":254,"tag":255,"props":42681,"children":42682},{},[42683],{"type":254,"tag":1161,"props":42684,"children":42688},{"alt":42685,"className":42686,"src":42687},"Sun shining through the clouds in the sky.",[1165,1166],"/posts/images/pulumivstf_cloud_1.jpg",[],{"type":254,"tag":255,"props":42690,"children":42691},{},[42692],{"type":259,"value":42693},"Terraform and Pulumi have many similarities and some of the characteristics (like being declarative, open source, or multi providers) I mentioned above are present in both solutions. However, that is not the case with all these characteristics. Furthermore, Terraform and Pulumi differ in many other aspects that we will talk about in this article.",{"type":254,"tag":282,"props":42695,"children":42697},{"id":42696},"pulumi-a-modern-iac-tool-with-many-built-in-features",[42698],{"type":259,"value":42699},"Pulumi, a modern IaC tool with many built-in features",{"type":254,"tag":255,"props":42701,"children":42702},{},[42703],{"type":259,"value":42704},"Usually, tools that have been there for quite some time have more features than new tools. New tools bring modernity and innovative features but need a bit of time to catch up with all the features. This is not at all the case with Pulumi, it is even the opposite. Although Terraform is older (created in 2014 vs. 2018 for Pulumi), Pulumi has more built-in features (including key features) than Terraform. Moreover, some of Terraform features are restricted to its paid version Terraform Cloud 💸.",{"type":254,"tag":455,"props":42706,"children":42708},{"id":42707},"state-backends-and-security",[42709],{"type":259,"value":42710},"State, backends, and security",{"type":254,"tag":255,"props":42712,"children":42713},{},[42714,42716,42723,42724,42731],{"type":259,"value":42715},"Both Terraform and Pulumi use code (HCL and programming languages respectively) to describe the desired state of an infrastructure that is compared to the current state of the infrastructure to know which operations (create, update, delete) to do on resources. The current state of the infrastructure is stored in a \"backend\" than can be for instance the local filesystem, AWS S3, Google Cloud Storage, Azure Blob Storage, or the SaaS offering of Terraform (Terraform Cloud)/Pulumi (Pulumi Service). ",{"type":254,"tag":262,"props":42717,"children":42720},{"href":42718,"rel":42719},"https://cloud.hashicorp.com/products/terraform",[266],[42721],{"type":259,"value":42722},"Terraform Cloud",{"type":259,"value":10347},{"type":254,"tag":262,"props":42725,"children":42728},{"href":42726,"rel":42727},"https://www.pulumi.com/docs/intro/pulumi-service/",[266],[42729],{"type":259,"value":42730},"Pulumi Service",{"type":259,"value":42732}," are self-managed backends that offer similar functionalities (deployment history, collaboration functionalities, RBAC for an organization...).",{"type":254,"tag":255,"props":42734,"children":42735},{},[42736,42738,42743,42745,42750],{"type":259,"value":42737},"The state stored in a backend contains sensitive data (secrets like connection strings 🔑) that you need to secure. Whatever the backend you choose, ",{"type":254,"tag":9718,"props":42739,"children":42740},{},[42741],{"type":259,"value":42742},"when using Pulumi the secrets in your state are always encrypted",{"type":259,"value":42744}," using an encryption provider. The default encryption provider depends on the backend but you can easily configure Pulumi to specify another encryption provider to use. The encryption provider can be a passphrase, AWS Key Management Service, Azure Key Vault, Google Cloud Key Management Service, HashiCorp Vault Transit Secrets Engine, or Pulumi Service. The ability to choose an encryption provider and to encrypt secrets in the state is not something Terraform supports. By default, Terraform will store the state in a local JSON file with the secrets in it 🙀. You probably won't run into security issues if you use Terraform Cloud, yet ",{"type":254,"tag":9718,"props":42746,"children":42747},{},[42748],{"type":259,"value":42749},"security should be built-in and not something you have to pay for",{"type":259,"value":594},{"type":254,"tag":255,"props":42752,"children":42753},{},[42754],{"type":254,"tag":1161,"props":42755,"children":42759},{"alt":42756,"className":42757,"src":42758},"A padlock sitting on top of a computer keyboard.",[1165,1166],"/posts/images/pulumivstf_security_1.jpg",[],{"type":254,"tag":255,"props":42761,"children":42762},{},[42763,42765,42772,42774,42779],{"type":259,"value":42764},"You may not see this as a big concern as you are probably not storing your state locally but on a cloud storage (like an Azure Blob Storage) where access is restricted to only a few people. But let's imagine one of your storage access keys gets compromised. If you are using Terraform, someone could have access to all the secrets of your infrastructure. If you are using Pulumi he will not because all secrets in the state file are encrypted. And he will have trouble decrypting them because he would have to gain access to the encryption provider. Whether you use Azure Key Vault, AWS Key Management Service, or any other encryption provider, these are components whose purpose is to keep data safe 🔐, and that require proper permissions to have access to encryption/decryption keys. I am not saying you can't make your infrastructure safe with Terraform. (In my example above with an Azure Storage Account, you could always prevent the ",{"type":254,"tag":262,"props":42766,"children":42769},{"href":42767,"rel":42768},"https://docs.microsoft.com/en-us/azure/storage/common/shared-key-authorization-prevent?tabs=portal",[266],[42770],{"type":259,"value":42771},"shared key authorization",{"type":259,"value":42773}," and use only Azure AD authorization to reduce the security risk.) I am just saying that ",{"type":254,"tag":9718,"props":42775,"children":42776},{},[42777],{"type":259,"value":42778},"Pulumi is secure by default, Terraform is not",{"type":259,"value":42780}," and extra work is required.",{"type":254,"tag":427,"props":42782,"children":42783},{"icon":567},[42784],{"type":254,"tag":255,"props":42785,"children":42786},{},[42787,42789,42795],{"type":259,"value":42788},"If you want to learn more about state, backend, security, and how Terraform handles state compared to Pulumi you can check this ",{"type":254,"tag":262,"props":42790,"children":42793},{"href":42791,"rel":42792},"https://www.techwatching.dev/posts/pulumi-azure-backend",[266],[42794],{"type":259,"value":36966},{"type":259,"value":42796}," where I talk about all. I also show how to use Azure Blob Storage as the backend and Azure Key Vault as the encryption provider for my infrastructure.",{"type":254,"tag":455,"props":42798,"children":42800},{"id":42799},"iac-brownfield-development",[42801],{"type":259,"value":42802},"IaC brownfield development",{"type":254,"tag":255,"props":42804,"children":42805},{},[42806],{"type":259,"value":42807},"Infrastructure as Code is not a new concept and before Terraform and Pulumi arrived, cloud providers' native solutions have been widely used to provision cloud infrastructure. Some infrastructures were also created manually. So today, there is a lot of existing infrastructure and most projects are not greenfield but brownfield projects. When choosing an IaC solution it is important to consider that and have the tools to integrate the existing infrastructure.",{"type":254,"tag":255,"props":42809,"children":42810},{},[42811],{"type":254,"tag":1161,"props":42812,"children":42816},{"alt":42813,"className":42814,"src":42815},"A view of the ocean from the top of a hill with different types of soils.",[1165,1166],"/posts/images/pulumivstf_brownfield.jpg",[],{"type":254,"tag":255,"props":42818,"children":42819},{},[42820,42822,42827],{"type":259,"value":42821},"Both Terraform and Pulumi have an ",{"type":254,"tag":389,"props":42823,"children":42825},{"className":42824},[],[42826],{"type":259,"value":24446},{"type":259,"value":42828},"  CLI command to import existing infrastructure. Currently, Terraform can only import one resource at a time and can only import it into the state without generating the corresponding configuration code. Pulumi supports bulk import operations (using a JSON file to specify the resources to import) and generates the corresponding infrastructure code to add. It may seem like anecdotal features but they become important when you have a lot of resources to import.",{"type":254,"tag":427,"props":42830,"children":42831},{"icon":429},[42832],{"type":254,"tag":255,"props":42833,"children":42834},{},[42835,42837,42844],{"type":259,"value":42836},"Besides, Microsoft has recently announced a new tool ",{"type":254,"tag":262,"props":42838,"children":42841},{"href":42839,"rel":42840},"https://github.com/Azure/aztfy",[266],[42842],{"type":259,"value":42843},"Azure Terrafy",{"type":259,"value":42845}," to \"quickly turn existing Azure infrastructure into Terraform HCL and import to Terraform state\". I guess they did not want to wait for Terraform to have this feature built-in. This won't help you if you are using Terraform with AWS or GCP though.",{"type":254,"tag":255,"props":42847,"children":42848},{},[42849,42851,42857],{"type":259,"value":42850},"Being able to import existing resources into Pulumi/Terraform is nice. However, for complex infrastructure, you will probably do it progressively or not at all if you want to keep some parts managed by other tools/teams. No matter the case, you will need your new infrastructure to coexist with the existing infrastructure not (yet) managed by Pulumi/Terraform. Both Terraform and Pulumi can reference existing infrastructure but Pulumi goes beyond that. First, it can reference other Pulumi stacks i.e Pulumi projects, which is especially useful when you are in a big organization or when your architecture is divided into microservices. Second, it can reference external states, i.e outputs from infrastructure created with other IaC tools than Pulumi. For instance, you could reference a Terraform state (whether it is a local ",{"type":254,"tag":389,"props":42852,"children":42854},{"className":42853},[],[42855],{"type":259,"value":42856},"tfstate",{"type":259,"value":42858}," or a remote state like a state in a Terraform Cloud workspace) or an AWS CloudFormation stack: you would get access to all the outputs of the corresponding provisioned infrastructure. That way, even if you have some existing infrastructure managed outside your Pulumi project, you can reference it and use it in your project without having to reference each resource with hard-coded names.",{"type":254,"tag":255,"props":42860,"children":42861},{},[42862,42864,42871,42873,42880],{"type":259,"value":42863},"If you have already spent time building a complex infrastructure with another IaC tool and want to migrate to Pulumi while preserving the organization of your code and without rewriting everything with Pulumi SDKs from scratch, Pulumi has some ",{"type":254,"tag":262,"props":42865,"children":42868},{"href":42866,"rel":42867},"https://www.pulumi.com/docs/converters/",[266],[42869],{"type":259,"value":42870},"conversion tools",{"type":259,"value":42872}," to help you with that. For instance, you can use the ",{"type":254,"tag":262,"props":42874,"children":42877},{"href":42875,"rel":42876},"https://www.pulumi.com/arm2pulumi/",[266],[42878],{"type":259,"value":42879},"arm2pulumi",{"type":259,"value":42881}," converter to convert your ARM templates to Pulumi code written in your preferred programming language. You can try it yourself on the website to see what it looks like.",{"type":254,"tag":255,"props":42883,"children":42884},{},[42885],{"type":254,"tag":1161,"props":42886,"children":42890},{"alt":42887,"className":42888,"src":42889},"Demo of using ARM to Pulumi converter on Pulumi website.",[1165,1166],"/posts/images/pulumivstf_converter_1.png",[],{"type":254,"tag":255,"props":42892,"children":42893},{},[42894],{"type":259,"value":42895},"I like how Pulumi has been designed to work with brownfield projects: it offers all that is needed to coexist with existing infrastructure, adopting it, and converting it to Pulumi code if desired. It goes far beyond the built-in capabilities of Terraform on the topic.",{"type":254,"tag":427,"props":42897,"children":42899},{"icon":42898},"i-heroicons-document-text",[42900],{"type":254,"tag":255,"props":42901,"children":42902},{},[42903,42905,42912],{"type":259,"value":42904},"Pulumi has a very ",{"type":254,"tag":262,"props":42906,"children":42909},{"href":42907,"rel":42908},"https://www.pulumi.com/docs/guides/adopting/",[266],[42910],{"type":259,"value":42911},"well-written documentation",{"type":259,"value":42913}," about \"adopting Pulumi\" if you want to deep dive into working with existing infrastructure.",{"type":254,"tag":455,"props":42915,"children":42917},{"id":42916},"environments-and-configuration",[42918],{"type":259,"value":42919},"Environments and configuration",{"type":254,"tag":255,"props":42921,"children":42922},{},[42923,42925,42932,42934,42940,42941,42947],{"type":259,"value":42924},"Thanks to its concept of ",{"type":254,"tag":262,"props":42926,"children":42929},{"href":42927,"rel":42928},"https://www.pulumi.com/docs/intro/concepts/stack/",[266],[42930],{"type":259,"value":42931},"stack",{"type":259,"value":42933},", Pulumi has built-in support for deploying the same infrastructure in multiple environments. You can use configuration files to have different settings depending on the environment. For example, if you have a development environment and a production environment that need to have different sizes of VMs to avoid paying too much for the development environment, you will have 2 configuration files: ",{"type":254,"tag":389,"props":42935,"children":42937},{"className":42936},[],[42938],{"type":259,"value":42939},"Pulumi.development.yaml",{"type":259,"value":27706},{"type":254,"tag":389,"props":42942,"children":42944},{"className":42943},[],[42945],{"type":259,"value":42946},"Pulumi.production.yaml",{"type":259,"value":42948},". These files will contain the same setting to specify the size of the VM with different values. This concept of having a configuration file by environment is quite similar to what you would use when developing an application. In addition to that, you can use the Pulumi CLI to set settings as secrets that will be encrypted in your configuration files (using the same encryption provider that encrypts your state). So Pulumi offers you everything you need to easily and safely provision infrastructure in different environments.",{"type":254,"tag":255,"props":42950,"children":42951},{},[42952],{"type":254,"tag":1161,"props":42953,"children":42957},{"alt":42954,"className":42955,"src":42956},"Pulumi.dev.yaml file with encrypted settings.",[1165,1166],"/posts/images/pulumivstf_configuration_1.png",[],{"type":254,"tag":255,"props":42959,"children":42960},{},[42961,42963,42970,42971,42978],{"type":259,"value":42962},"Of course, Terraform can also provision infrastructure in different environments. Having different folders for the different environments used to be the way of handling different environments in Terraform which meant a lot of code duplication (unfortunately some companies still do that today😕). Because of that and other deficiencies of Terraform, different Terraform wrappers and frameworks (like ",{"type":254,"tag":262,"props":42964,"children":42967},{"href":42965,"rel":42966},"https://terragrunt.gruntwork.io/",[266],[42968],{"type":259,"value":42969},"Terragrunt",{"type":259,"value":31561},{"type":254,"tag":262,"props":42972,"children":42975},{"href":42973,"rel":42974},"https://terraspace.cloud/",[266],[42976],{"type":259,"value":42977},"Terraspace",{"type":259,"value":42979},") were created by the community to keep the infrastructure code DRY and structured.",{"type":254,"tag":427,"props":42981,"children":42982},{"icon":42898},[42983],{"type":254,"tag":255,"props":42984,"children":42985},{},[42986,42988,42995],{"type":259,"value":42987},"If you want to know the differences between Terraform, Terragrunt, and Terraspace, BlogOps (the creator of Terraspace) has an interesting article on the ",{"type":254,"tag":262,"props":42989,"children":42992},{"href":42990,"rel":42991},"https://blog.boltops.com/2020/09/28/terraform-vs-terragrunt-vs-terraspace/",[266],[42993],{"type":259,"value":42994},"topic",{"type":259,"value":594},{"type":254,"tag":255,"props":42997,"children":42998},{},[42999,43001,43008],{"type":259,"value":43000},"In 2017, Terraform also introduced its built-in way of managing different environments with ",{"type":254,"tag":262,"props":43002,"children":43005},{"href":43003,"rel":43004},"https://www.terraform.io/language/state/workspaces",[266],[43006],{"type":259,"value":43007},"Terraform workspaces",{"type":259,"value":43009},". I think it's great to have different alternatives, but the drawback is to have the Terraform community divided between people that are using pure Terraform and people using other tools built on top of Terraform. Moreover, whatever the tool used I find managing environments and configuration a bit more complicated in Terraform than when you use Pulumi: a lot of documentation and blog posts to read before knowing how to manage environments the proper way.",{"type":254,"tag":255,"props":43011,"children":43012},{},[43013,43015,43022,43024,43031],{"type":259,"value":43014},"One important downside of Terraform concerning environments and configuration is that there is no built-in way of managing secrets. Unless you are a Terraform Cloud customer (in which case your secrets will be stored in workspaces as sensitive variables), you have to find a custom way to keep the secrets in your configuration safe. ",{"type":254,"tag":262,"props":43016,"children":43019},{"href":43017,"rel":43018},"https://gruntwork.io/",[266],[43020],{"type":259,"value":43021},"Gruntwork",{"type":259,"value":43023},", the company behind Terragrunt has a ",{"type":254,"tag":262,"props":43025,"children":43028},{"href":43026,"rel":43027},"https://blog.gruntwork.io/a-comprehensive-guide-to-managing-secrets-in-your-terraform-code-1d586955ace1",[266],[43029],{"type":259,"value":43030},"detailed article",{"type":259,"value":43032}," about managing secrets in Terraform that I suggest you read. It describes different techniques to keep secrets safe (so not hardcoded in plain text in Terraform code): using environment variables, encrypted files, or a secret store. As you can read in the conclusion of this article (and see in the screenshot of this article below), all these options have trade-offs and require extra work in comparison to Pulumi where secrets encryption is integrated. Moreover, even if you succeed in managing secrets properly in Terraform, they will end up in plain text in Terraform state as we previously mentioned.",{"type":254,"tag":255,"props":43034,"children":43035},{},[43036],{"type":254,"tag":1161,"props":43037,"children":43041},{"alt":43038,"className":43039,"src":43040},"Table showing options for managing secrets in Terraform from Gruntwork website.",[1165,1166],"/posts/images/pulumivstf_security_2.png",[],{"type":254,"tag":455,"props":43043,"children":43045},{"id":43044},"embedded-iac-through-an-api",[43046],{"type":259,"value":43047},"Embedded IaC through an API",{"type":254,"tag":255,"props":43049,"children":43050},{},[43051],{"type":259,"value":43052},"To provision infrastructure using Terraform or Pulumi, you can use their respective CLI.",{"type":254,"tag":255,"props":43054,"children":43055},{},[43056,43058,43065],{"type":259,"value":43057},"If you are using Terraform Cloud, you can also perform Terraform runs (called ",{"type":254,"tag":262,"props":43059,"children":43062},{"href":43060,"rel":43061},"https://www.terraform.io/cloud-docs/run#terraform-runs-and-remote-operations",[266],[43063],{"type":259,"value":43064},"remote operations",{"type":259,"value":43066}," in the documentation which are concepts specific to Terraform Cloud) from there by using an API, UI controls or webhooks of your Version Control System (GitHub, GitLab, BitBucket, Azure DevOps, ...). These are useful capabilities but once again these are only available if you are using Terraform SaaS product.",{"type":254,"tag":255,"props":43068,"children":43069},{},[43070],{"type":259,"value":43071},"Now, there are use cases where you would want to provision infrastructure programmatically instead of using a CLI. It can be for instance to:",{"type":254,"tag":332,"props":43073,"children":43074},{},[43075,43089,43094,43099],{"type":254,"tag":336,"props":43076,"children":43077},{},[43078,43080,43087],{"type":259,"value":43079},"provide self-service infrastructure to development teams in your organization (through ",{"type":254,"tag":262,"props":43081,"children":43084},{"href":43082,"rel":43083},"https://www.pulumi.com/blog/organizational-patterns-developer-portal/",[266],[43085],{"type":259,"value":43086},"developer portals",{"type":259,"value":43088}," for example)",{"type":254,"tag":336,"props":43090,"children":43091},{},[43092],{"type":259,"value":43093},"integrate provisioning of infrastructure into your platforms and tools (whether it is your custom framework, CLI, or CI/CD workflow)",{"type":254,"tag":336,"props":43095,"children":43096},{},[43097],{"type":259,"value":43098},"automate infrastructure provisioning for your custom needs",{"type":254,"tag":336,"props":43100,"children":43101},{},[43102],{"type":259,"value":43103},"do complex deployments that mix infrastructure and application code such as database migrations",{"type":254,"tag":255,"props":43105,"children":43106},{},[43107,43109,43114,43116,43122],{"type":259,"value":43108},"And for that, Pulumi has an ",{"type":254,"tag":262,"props":43110,"children":43112},{"href":37113,"rel":43111},[266],[43113],{"type":259,"value":37117},{"type":259,"value":43115}," which allows you to build, deploy, and manage infrastructure dynamically from your code thanks to an SDK instead of a CLI. I think it's awesome and unfortunately Terraform does not offer something like that. There would be much to say about Automation API (and that's something that will probably continue to evolve), yet the best is that you check the ",{"type":254,"tag":262,"props":43117,"children":43120},{"href":43118,"rel":43119},"https://www.pulumi.com/docs/guides/automation-api/",[266],[43121],{"type":259,"value":6340},{"type":259,"value":43123}," to learn more about it.",{"type":254,"tag":255,"props":43125,"children":43126},{},[43127],{"type":254,"tag":1161,"props":43128,"children":43132},{"alt":43129,"className":43130,"src":43131},"Diagram explaining Pulumi Automation API.",[1165,1166],"/posts/images/pulumivstf_automationapi.png",[],{"type":254,"tag":455,"props":43134,"children":43136},{"id":43135},"what-else",[43137],{"type":259,"value":43138},"What else?",{"type":254,"tag":255,"props":43140,"children":43141},{},[43142,43144,43151],{"type":259,"value":43143},"Pulumi has many more built-in features where it stands out compared to Terraform but I can't cover everything in this blog post. The ones I previously talked about were the main ones in my opinion but you can check the ",{"type":254,"tag":262,"props":43145,"children":43148},{"href":43146,"rel":43147},"https://www.pulumi.com/docs/intro/vs/terraform/",[266],[43149],{"type":259,"value":43150},"Pulumi vs. Terraform page",{"type":259,"value":43152}," in Pulumi's documentation to read about other differences between Pulumi and Terraform. Other interesting capabilities of Pulumi like dynamic providers or testing will be discussed later in this article.",{"type":254,"tag":282,"props":43154,"children":43156},{"id":43155},"providers",[43157],{"type":259,"value":43158},"Providers",{"type":254,"tag":455,"props":43160,"children":43162},{"id":43161},"why-providers",[43163],{"type":259,"value":43164},"Why providers?",{"type":254,"tag":255,"props":43166,"children":43167},{},[43168],{"type":259,"value":43169},"Providers are the reason why I think Terraform and Pulumi have a great future ahead. Instead of focusing on a specific cloud or platform (which is what Cloud Formation, Azure Bicep, or Google Cloud Deployment Manager do), Terraform and Pulumi allow you to provision resources in many different cloud providers and SaaS providers. That does not mean you will write the same infrastructure code to provision cloud resources on AWS and Azure. Of course not, each cloud provider has its specificities and the resources will be different on each platform so the code needs to be different as well. However, instead of having to use multiple IaC platforms (Cloud Formation and Azure Bicep for instance) if you need to provision resources from different clouds in your project, you will only use one (Terraform or Pulumi) with different providers.",{"type":254,"tag":255,"props":43171,"children":43172},{},[43173],{"type":259,"value":43174},"You can see how this works in Pulumi on this schema from the documentation:",{"type":254,"tag":255,"props":43176,"children":43177},{},[43178],{"type":254,"tag":1161,"props":43179,"children":43183},{"alt":43180,"className":43181,"src":43182},"Diagram of how Pulumi works.",[1165,1166],"/posts/images/pulumivstf_architecture.png",[],{"type":254,"tag":255,"props":43185,"children":43186},{},[43187],{"type":259,"value":43188},"Today lots of companies are using multiple clouds to meet their needs and to avoid putting all their eggs in the same basket. And beyond cloud providers, a lot of other SaaS products are used in an organization and you will probably automate the provisioning of resources for these products too to make them available to your teams (whether it is a VCS or a monitoring platform). So being able to provision and manage all these resources from the same tools, using the same concepts and processes is a must-have, and that is what Pulumi and Terraform offer by supporting many providers 👍.",{"type":254,"tag":455,"props":43190,"children":43192},{"id":43191},"how-does-pulumi-benefit-from-terraform-ecosystem",[43193],{"type":259,"value":43194},"How does Pulumi benefit from Terraform ecosystem?",{"type":254,"tag":255,"props":43196,"children":43197},{},[43198],{"type":259,"value":43199},"When Pulumi came out in 2018, instead of reinventing the wheel they choose to take advantage of Terraform Providers' mature ecosystem to build most of their own providers. Indeed, Pulumi created tools to adapt/bridge any existing Terraform provider.",{"type":254,"tag":255,"props":43201,"children":43202},{},[43203],{"type":254,"tag":1161,"props":43204,"children":43208},{"alt":43205,"className":43206,"src":43207,"width":19735},"Pulumi Terraform Bridge repository on GitHub.",[1165,1166],"/posts/images/pulumivstf_bridge.png",[],{"type":254,"tag":255,"props":43210,"children":43211},{},[43212],{"type":259,"value":43213},"To understand what it means, we have to talk about what is exactly a provider. According to Terraform's documentation, \"providers are a logical abstraction of an upstream API. They are responsible for understanding API interactions and exposing resources\". That means a provider defines a schema describing the resources available on a cloud provider API, and all the mappings (parameters, models, responses ...) needed to interact with this API. Instead of doing the same job of mapping everything, a Pulumi provider that is created by \"bridging\" a Terraform provider simply reuses the same schema, that's it. But, Pulumi itself does not use Terraform, they have a completely different engine.",{"type":254,"tag":427,"props":43215,"children":43216},{"icon":42898},[43217],{"type":254,"tag":255,"props":43218,"children":43219},{},[43220,43222,43228],{"type":259,"value":43221},"If you are looking for a better explanation about how Pulumi \"bridges\" Terraform providers you can look at this ",{"type":254,"tag":262,"props":43223,"children":43226},{"href":43224,"rel":43225},"https://www.leebriggs.co.uk/blog/2021/11/06/pulumi-faqs.html#doesnt-pulumi-use-terraform-under-the-hood-",[266],[43227],{"type":259,"value":36966},{"type":259,"value":43229}," from Lee Briggs who work at Pulumi.",{"type":254,"tag":255,"props":43231,"children":43232},{},[43233,43235,43242],{"type":259,"value":43234},"Why do I talk about all this? It's because I think it's great for Pulumi to be able to benefit from Terraform Providers ecosystem. Thanks to this, like Terraform, Pulumi supports lots of cloud providers and modern cloud SaaS offerings. And in the event there were a provider available in Terraform and not in Pulumi, it would always be possible for anyone to create a Pulumi provider out of this Terraform provider thanks to the ",{"type":254,"tag":262,"props":43236,"children":43239},{"href":43237,"rel":43238},"https://github.com/pulumi/pulumi-terraform-bridge",[266],[43240],{"type":259,"value":43241},"Pulumi Terraform Bridge",{"type":259,"value":594},{"type":254,"tag":255,"props":43244,"children":43245},{},[43246,43248,43255],{"type":259,"value":43247},"By the way, the fact that some Pulumi providers are created by adapting Terraform providers is completely assumed by Pulumi as you can read in ",{"type":254,"tag":262,"props":43249,"children":43252},{"href":43250,"rel":43251},"https://www.pulumi.com/docs/intro/vs/terraform/#providers-terraform",[266],[43253],{"type":259,"value":43254},"Pulumi documentation",{"type":259,"value":594},{"type":254,"tag":255,"props":43257,"children":43258},{},[43259],{"type":254,"tag":1161,"props":43260,"children":43264},{"alt":43261,"className":43262,"src":43263},"Pulumi documentation about using Terraform Providers.",[1165,1166],"/posts/images/pulumivstf_providers_1.png",[],{"type":254,"tag":455,"props":43266,"children":43268},{"id":43267},"the-problem-with-terraform-providers",[43269],{"type":259,"value":43270},"The problem with Terraform providers",{"type":254,"tag":255,"props":43272,"children":43273},{},[43274],{"type":259,"value":43275},"We have seen that Pulumi benefits from Terraform providers ecosystem. However, there is a major problem with Terraform providers (and so with corresponding Pulumi providers as well): they are implemented manually.",{"type":254,"tag":255,"props":43277,"children":43278},{},[43279],{"type":259,"value":43280},"It has the following inconveniences for a provider:",{"type":254,"tag":332,"props":43282,"children":43283},{},[43284,43289,43294],{"type":254,"tag":336,"props":43285,"children":43286},{},[43287],{"type":259,"value":43288},"it is more likely to contain bugs",{"type":254,"tag":336,"props":43290,"children":43291},{},[43292],{"type":259,"value":43293},"it doesn't have full coverage of its corresponding API",{"type":254,"tag":336,"props":43295,"children":43296},{},[43297],{"type":259,"value":43298},"it is always a little behind an API because it takes time for new resources or new features to be added",{"type":254,"tag":255,"props":43300,"children":43301},{},[43302,43304,43308],{"type":259,"value":43303},"At the beginning of the article I explain that I wanted my IaC solution should be ",{"type":254,"tag":19837,"props":43305,"children":43306},{},[43307],{"type":259,"value":42657},{"type":259,"value":43309}," and that is not the case with Terraform providers even if the community (Pulumi included) is doing a great job at contributing to Terraform providers to make new resources available.",{"type":254,"tag":255,"props":43311,"children":43312},{},[43313],{"type":254,"tag":1161,"props":43314,"children":43318},{"alt":43315,"className":43316,"src":43317},"A bunch of clocks that are sitting on a shelf.",[1165,1166],"/posts/images/pulumivstf_clocks.jpg",[],{"type":254,"tag":255,"props":43320,"children":43321},{},[43322],{"type":259,"value":43323},"Moreover, a version of a provider matches a version of the resources in the API. So you can't have 2 resources from different API versions coexist. For instance, let's say you need to use an old version of the Terraform provider for Azure because they are a lot of breaking changes in the latest version of the provider. You don't want to handle these changes yet as it involves some big changes in some of your resources. But in the same time, you want to use a resource that is only available in the latest version of the provider. Well in this case it's going to be complicated for you. The issue is that with Terraform providers you can't be very flexible with your resources and customize everything you want",{"type":254,"tag":455,"props":43325,"children":43327},{"id":43326},"what-are-pulumi-native-providers",[43328],{"type":259,"value":43329},"What are  Pulumi native providers?",{"type":254,"tag":255,"props":43331,"children":43332},{},[43333,43335,43342],{"type":259,"value":43334},"In order to solve the problem described in the previous section, Pulumi introduced the concept of ",{"type":254,"tag":262,"props":43336,"children":43339},{"href":43337,"rel":43338},"https://www.pulumi.com/blog/pulumiup-native-providers/",[266],[43340],{"type":259,"value":43341},"native providers",{"type":259,"value":43343}," for the providers Microsoft Azure, AWS, Google Cloud Platform, and Kubernetes. These providers are automatically built every day from the cloud providers' APIs 🎉. It has the following advantages:",{"type":254,"tag":332,"props":43345,"children":43346},{},[43347,43352,43357],{"type":254,"tag":336,"props":43348,"children":43349},{},[43350],{"type":259,"value":43351},"100% API coverage, so all resources available including the ones in preview",{"type":254,"tag":336,"props":43353,"children":43354},{},[43355],{"type":259,"value":43356},"Providers are always up-to-date with the APIs",{"type":254,"tag":336,"props":43358,"children":43359},{},[43360],{"type":259,"value":43361},"Access to all the versions of the APIs so that resources from different API versions can coexist in a project (as you would have with a cloud provider native solution like Azure Bicep)",{"type":254,"tag":255,"props":43363,"children":43364},{},[43365],{"type":254,"tag":9718,"props":43366,"children":43367},{},[43368],{"type":259,"value":43369},"Honestly, these are good enough reasons to choose Pulumi over Terraform. If you are developing cloud applications, you don't want to be limited to what you can do by your IaC solution, especially when it concerns the major cloud providers you are probably using",{"type":254,"tag":427,"props":43371,"children":43372},{"icon":429},[43373],{"type":254,"tag":255,"props":43374,"children":43375},{},[43376,43378,43385],{"type":259,"value":43377},"It's worth noting that people from Microsoft seem also concerned about these limitations of the Terraform Azure RM provider as they announced a new Azure provider ",{"type":254,"tag":262,"props":43379,"children":43382},{"href":43380,"rel":43381},"https://techcommunity.microsoft.com/t5/azure-tools-blog/announcing-azure-terrafy-and-azapi-terraform-provider-previews/ba-p/3270937",[266],[43383],{"type":259,"value":43384},"AzAPI",{"type":259,"value":43386},", built on top of the Azure ARM APIs that can be used to have access to Azure features and services in preview. However, it seems that for other resources, Microsoft expects people to continue using the existing Terraform provider with its limitations.",{"type":254,"tag":255,"props":43388,"children":43389},{},[43390],{"type":259,"value":43391},"I think generating the providers from the APIs is the right way of doing things. Unfortunately, it's only possible to create native providers for a cloud provider or a SaaS provider if its API exposes the necessary mapping. But if more APIs do that, more Pulumi native providers will probably be added in the future.",{"type":254,"tag":255,"props":43393,"children":43394},{},[43395],{"type":254,"tag":1161,"props":43396,"children":43400},{"alt":43397,"className":43398,"src":43399},"List of Pulumi native Providers on Pulumi website.",[1165,1166],"/posts/images/pulumivstf_providers_2.png",[],{"type":254,"tag":255,"props":43402,"children":43403},{},[43404],{"type":259,"value":43405},"Another thing I appreciate a lot with these native providers:  you work with the same \"models\" as the ones of the corresponding cloud. On the contrary, because a Terraform provider is hand-coded you will have a more or less thin abstraction layer and potential differences between the models. That's not a big deal but can sometimes complicate things because you have to understand how the Terraform models map to the cloud API models.",{"type":254,"tag":455,"props":43407,"children":43409},{"id":43408},"more-about-providers",[43410],{"type":259,"value":43411},"More about providers",{"type":254,"tag":255,"props":43413,"children":43414},{},[43415,43417,43424],{"type":259,"value":43416},"If you have looked at ",{"type":254,"tag":262,"props":43418,"children":43421},{"href":43419,"rel":43420},"https://registry.terraform.io/browse/providers",[266],[43422],{"type":259,"value":43423},"Terraform registry",{"type":259,"value":43425},", you may have seen that there is a huge amount of community providers. Does it mean Terraform is better than Pulumi? I don't think so: quantity does not mean quality.  That's not me saying that there are many bad quality Terraform providers, not at all. That's me saying that there are probably lots of providers you don't care about. For instance, I assume you don't care that there is a Terraform provider to order a Domino's Pizza 🍕. Okay, that may be fun, but that's it. And concerning the more serious providers, a lot of them are just there to overcome the inherent limitations of HCL (not being a programming language) such as not being able to make an HTTP call without using a provider. You don't have these limitations with Pulumi where you use programming languages and their libraries.",{"type":254,"tag":255,"props":43427,"children":43428},{},[43429],{"type":254,"tag":1161,"props":43430,"children":43433},{"alt":38138,"className":43431,"src":43432},[1165,1166],"/posts/images/pulumivstf_tools_1.jpg",[],{"type":254,"tag":255,"props":43435,"children":43436},{},[43437],{"type":259,"value":43438},"We will talk more about that in the next section but being able to use a programming language and all its ecosystem is a true advantage of Pulumi over Terraform. There are some times when you need to provision some cloud resources and there is no provider to manage them (whether it be Pulumi or Terraform). If you are using Terraform you will have to wait for the community to implement this provider, if it ever really happens. If you are using Pulumi, you are using programming languages and therefore can implement anything you need (by using SDKs or making the API calls yourself). And I am not talking about implementing yourself a whole provider covering a complete API (which would require some time), but just implementing the resources you need.",{"type":254,"tag":255,"props":43440,"children":43441},{},[43442,43444,43451,43453,43459],{"type":259,"value":43443},"For that, Pulumi has a concept of ",{"type":254,"tag":262,"props":43445,"children":43448},{"href":43446,"rel":43447},"https://www.pulumi.com/docs/intro/concepts/resources/dynamic-providers/",[266],[43449],{"type":259,"value":43450},"Dynamic Providers",{"type":259,"value":43452}," with which you implement the different CRUD operations for a resource so that you are still doing declarative infrastructure as code but with custom logic. It's like implementing on the fly a custom provider specific to your needs directly in your project. Dynamic providers' usage is not limited to supporting a cloud provider that does not yet exist in Pulumi, it is also to do any infrastructure task that no existing provider can help deliver (see examples in this ",{"type":254,"tag":262,"props":43454,"children":43457},{"href":43455,"rel":43456},"https://www.pulumi.com/blog/dynamic-providers/#sample-use-cases",[266],[43458],{"type":259,"value":36966},{"type":259,"value":2280},{"type":254,"tag":282,"props":43461,"children":43463},{"id":43462},"using-programming-languages-the-best-way-to-do-iac",[43464],{"type":259,"value":43465},"Using programming languages: the best way to do IaC",{"type":254,"tag":255,"props":43467,"children":43468},{},[43469],{"type":259,"value":43470},"Pulumi's approach is to use programming languages to write your infrastructure code. On the opposite, Terraform's approach is to uses a DSL called HCL (that stands for Hashicorp Configuration Language). It's worth reminding that both tools are declarative, even when using an imperative language.",{"type":254,"tag":255,"props":43472,"children":43473},{},[43474],{"type":259,"value":43475},"When comparing IaC, the big debate ⚡ is often: show we use Domain Specific Languages (DSL) or programming languages? If you have read the previous sections of this article you know this is just one of many questions, there are other important aspects to consider. Nevertheless, it's an important question because there is more to it than simply a question of personal preference. In my opinion, using programming languages to write your infrastructure code is the right way of doing things and is the future of infrastructure as code. Let me explain to you why and how it is particularly great to use programming languages with Pulumi.",{"type":254,"tag":455,"props":43477,"children":43479},{"id":43478},"better-aligned-with-the-devops-culture",[43480],{"type":259,"value":43481},"Better aligned with the DevOps culture",{"type":254,"tag":255,"props":43483,"children":43484},{},[43485],{"type":259,"value":43486},"DevOps aims at continuously delivering value to end-users by removing the barrier between software development and IT operations. As one of the practices of DevOps, Infrastructure as Code should help bring closer people from Development teams and people from Operations teams.",{"type":254,"tag":255,"props":43488,"children":43489},{},[43490],{"type":259,"value":43491},"Therefore I don't think using different languages, tools, and practices for the application development and the infrastructure development is the right approach. And yet, this is what you do when you write your infrastructure code in HCL while the application code is written in TypeScript or C# for instance. Unfortunately, in some companies, I think using Terraform reinforces the Development and Operations silos more than anything else. It's difficult to deliver value to a customer if that means for a developer to create a ticket to the Operations team just to add a new setting in a web app because only them have access and knowledge of the Terraform code 😿.",{"type":254,"tag":255,"props":43493,"children":43494},{},[43495],{"type":254,"tag":1161,"props":43496,"children":43500},{"alt":43497,"className":43498,"src":43499},"The words \"together is the way\" spelled with cubes.",[1165,1166],"/posts/images/pulumivstf_together.jpg",[],{"type":254,"tag":255,"props":43502,"children":43503},{},[43504],{"type":259,"value":43505},"I truly believe that the proper way of adopting DevOps practices is to have multidisciplinary self-organizing teams. And what a better way to make software developers, IT operations people, security engineers, ... collaborate in such teams than by making them \"speak\" the same language, use the same tools, and adopt the same engineering practices. That's what using programming languages for the infrastructure code is about and that's what Pulumi is about.",{"type":254,"tag":455,"props":43507,"children":43509},{"id":43508},"be-productive-faster-and-more",[43510],{"type":259,"value":43511},"Be productive faster and more",{"type":254,"tag":255,"props":43513,"children":43514},{},[43515],{"type":259,"value":43516},"Whatever the IaC solution you choose, you will have to learn new concepts that may not be obvious. Yet, writing the infrastructure code will be easy to learn if you are using a programming language you already know. On the contrary, if you use a DSL, it will add another thing for you to learn (especially HCL that I don't find very intuitive).",{"type":254,"tag":255,"props":43518,"children":43519},{},[43520],{"type":259,"value":43521},"Using Pulumi you will not only be able to use the programming language you know but also your favorite IDEs, as well as the libraries and the tools you are familiar with 🛠️. The programming languages supported by Pulumi (TypeScript/JavaScript, Python, Go, .NET languages, Java languages, and probably more to come) are used by lots of people for software development. Therefore IDE support (typing, static analysis, code completion....) is already great and tooling in general better than the tooling for an IaC DSL could ever be.",{"type":254,"tag":255,"props":43523,"children":43524},{},[43525],{"type":254,"tag":1161,"props":43526,"children":43530},{"alt":43527,"className":43528,"src":43529},"A black and white banner that says everyone can code.",[1165,1166],"/posts/images/pulumivstf_eveyonecancode.jpg",[],{"type":254,"tag":255,"props":43532,"children":43533},{},[43534],{"type":259,"value":43535},"So in addition to learning faster IaC using programming languages you will be more productive too. That is also true for people not coming from development but from ops, they probably already have some scripting knowledge (Bash, PowerShell, Python...), that's why programming languages can be a good fit for them as well.",{"type":254,"tag":255,"props":43537,"children":43538},{},[43539],{"type":259,"value":43540},"Making people quickly productive on an IaC solution is important because as everywhere in IT, in IaC field it's hard to find skilled people to recruit so it's sometimes easier to train people already in your company. Speaking of that, I hear sometimes people saying that: as Terraform is currently more used than Pulumi (due to Pulumi being more recent), it will be easier to hire people who know Terraform than people who know Pulumi. Yes, that's true, but it will be much harder to find people who know HCL than people who know TypeScript, Python, .NET, or Go.",{"type":254,"tag":427,"props":43542,"children":43543},{"icon":429},[43544],{"type":254,"tag":255,"props":43545,"children":43546},{},[43547],{"type":259,"value":43548},"Independently of HCL, I find Terraform to be more complex to learn than Pulumi, with a lot of different concepts to grasp (variables, local values,  data sources, configuration, workspaces, modules) that are unique to Terraform. Pulumi felt much easier. Of course, learning is unique to each individual so you may have a different opinion but this has been my experience.",{"type":254,"tag":455,"props":43550,"children":43552},{"id":43551},"a-better-developer-experience",[43553],{"type":259,"value":43554},"A better developer experience",{"type":254,"tag":255,"props":43556,"children":43557},{},[43558],{"type":259,"value":43559},"The developer experience is so much better when using programming languages instead of a DSL.",{"type":254,"tag":427,"props":43561,"children":43562},{"icon":567},[43563],{"type":254,"tag":255,"props":43564,"children":43565},{},[43566],{"type":259,"value":43567},"If you are not familiar with the term Developer Experience or DX, it's like User Experience but for software engineers using digital products (developer tools, IT solutions, platforms...).",{"type":254,"tag":255,"props":43569,"children":43570},{},[43571],{"type":259,"value":43572},"I already talked about the better code completion of Pulumi which is great to know which resource you can create, what properties can be set, which ones are required, and what are the possible values.",{"type":254,"tag":255,"props":43574,"children":43575},{},[43576,43578,43585],{"type":259,"value":43577},"There is also the fact that you can debug infrastructure code because it's just code in a program. With Automation API, Pulumi can be used from anywhere (a console application, a custom CLI, a web application, ...) and easily debugged. Good luck debugging Terraform code 😉. You even have something similar to hot reload for infrastructure with pulumi watch command (see ",{"type":254,"tag":262,"props":43579,"children":43582},{"href":43580,"rel":43581},"https://www.techwatching.dev/posts/pulumi-watch",[266],[43583],{"type":259,"value":43584},"my article",{"type":259,"value":43586}," on the topic).",{"type":254,"tag":255,"props":43588,"children":43589},{},[43590,43592,43598],{"type":259,"value":43591},"Another topic is testing. You can only do unit testing (understand the testing of components without deploying real infrastructure) with code written in a programming language. Pulumi allows you to write unit tests by mocking the external dependencies using your usual test and mock frameworks. Integration testing/end-to-end testing however can be done with both Pulumi and Terraform.  If you do a bit of search, you will find out that there are several frameworks to write end-to-end tests for Terraform and they require you to write your tests in Go or Ruby (so finally using a programming language after all 😀). You can read more about testing with Pulumi in the ",{"type":254,"tag":262,"props":43593,"children":43596},{"href":43594,"rel":43595},"https://www.pulumi.com/docs/guides/testing/",[266],[43597],{"type":259,"value":6340},{"type":259,"value":594},{"type":254,"tag":455,"props":43600,"children":43602},{"id":43601},"more-capabilities",[43603],{"type":259,"value":43604},"More capabilities",{"type":254,"tag":255,"props":43606,"children":43607},{},[43608],{"type":259,"value":43609},"Using a programming language you are not limited to what providers or modules can do. You have the power of a programming language to implement what you need (for example using the dynamic providers I talked about before). Some people are afraid of that, I don't think they should. Programming languages have static analysis mechanisms to ensure best practices are respected in the code. Pulumi also provides policy as code to let you write the rules you want your infrastructure to follow (it could be rules about security or cost for instance).",{"type":254,"tag":255,"props":43611,"children":43612},{},[43613],{"type":259,"value":43614},"Terraform has a great community that creates packages and tools for Terraform. As Pulumi is younger, the ecosystem is not as developed as Terraform's. However, when you use Pulumi you use programming languages that have a huge ecosystem from which you can benefit ❤️.",{"type":254,"tag":455,"props":43616,"children":43618},{"id":43617},"the-future",[43619],{"type":259,"value":43620},"The future",{"type":254,"tag":255,"props":43622,"children":43623},{},[43624,43626,43633,43635,43641],{"type":259,"value":43625},"I have given several reasons why I think programming languages is the best way to do IaC. Today, the majority of people are using DSL based on YAML or JSON to write infrastructure code but things are changing at a great speed. Following the example of Pulumi, there are more and more IaC tools that support programming languages to write infrastructure code, ",{"type":254,"tag":262,"props":43627,"children":43630},{"href":43628,"rel":43629},"https://aws.amazon.com/cdk/",[266],[43631],{"type":259,"value":43632},"AWS CDK",{"type":259,"value":43634}," for instance. Even Terraform itself is starting to see the limits of HCL and has launched its CDK in beta. Unfortunately at the time of writing it's still in preview since July 2020, has some limitations, and is not yet mature according to the ",{"type":254,"tag":262,"props":43636,"children":43639},{"href":43637,"rel":43638},"https://www.terraform.io/cdktf#project-maturity",[266],[43640],{"type":259,"value":6340},{"type":259,"value":43642},". Pulumi has a clear head start but it's very interesting to see the whole IaC ecosystem evolving in this direction.",{"type":254,"tag":282,"props":43644,"children":43645},{"id":6186},[43646],{"type":259,"value":37936},{"type":254,"tag":455,"props":43648,"children":43650},{"id":43649},"summary",[43651],{"type":259,"value":43652},"Summary",{"type":254,"tag":255,"props":43654,"children":43655},{},[43656],{"type":259,"value":43657},"To sum up, I will choose Pulumi over Terraform for my next project because:",{"type":254,"tag":332,"props":43659,"children":43660},{},[43661,43666,43671,43676,43681],{"type":254,"tag":336,"props":43662,"children":43663},{},[43664],{"type":259,"value":43665},"it has 100% resource coverage of my cloud provider and more flexibility",{"type":254,"tag":336,"props":43667,"children":43668},{},[43669],{"type":259,"value":43670},"it allows me to be more productive using languages, tools, and libraries I am already familiar with",{"type":254,"tag":336,"props":43672,"children":43673},{},[43674],{"type":259,"value":43675},"it has more built-in functionalities and is more modern",{"type":254,"tag":336,"props":43677,"children":43678},{},[43679],{"type":259,"value":43680},"it is easier to learn and use (something that my teammates will appreciate)",{"type":254,"tag":336,"props":43682,"children":43683},{},[43684],{"type":259,"value":43685},"it is secure by default",{"type":254,"tag":255,"props":43687,"children":43688},{},[43689],{"type":254,"tag":1161,"props":43690,"children":43694},{"alt":43691,"className":43692,"src":43693},"A hand moving a white chess piece to make the back king fall on a chess board.",[1165,1166],"/posts/images/pulumivstf_chess.jpg",[],{"type":254,"tag":455,"props":43696,"children":43698},{"id":43697},"disclaimers",[43699],{"type":259,"value":43700},"Disclaimers",{"type":254,"tag":255,"props":43702,"children":43703},{},[43704],{"type":259,"value":43705},"Just like any other article on my blog, \"the opinions expressed herein are my own and do not represent those of my employer or any other third-party views in any way\".",{"type":254,"tag":255,"props":43707,"children":43708},{},[43709],{"type":259,"value":43710},"This article is not sponsored, I don't own any shares in Pulumi (unfortunately 😉), and I don't work for Pulumi. That means the article just reflects my point of view, the point of view of a cloud developer interested in infrastructure who has worked with both Terraform and Pulumi, and much prefers Pulumi.",{"type":254,"tag":255,"props":43712,"children":43713},{},[43714],{"type":259,"value":43715},"Even if I like Pulumi a lot as it is very nice working with it, I have tried to remain as objective as possible in this article. You will probably not agree with everything I said, but I hope you will understand what are my reasons for choosing Pulumi for my Infrastructure as Code and that it will help you with your choice, whatever it is.",{"type":254,"tag":455,"props":43717,"children":43719},{"id":43718},"is-terraform-bad",[43720],{"type":259,"value":43721},"Is Terraform bad?",{"type":254,"tag":255,"props":43723,"children":43724},{},[43725],{"type":259,"value":43726},"Not at all. I would have a hard time saying Terraform is bad and Pulumi is awesome when they rely on similar concepts like declarative infrastructure as code, state file to store the current state of the infrastructure, and providers to support multiple cloud providers and services. In fact, I think Terraform is a great Infrastructure as Code solution, just not the best one in my opinion.",{"type":254,"tag":255,"props":43728,"children":43729},{},[43730],{"type":259,"value":43731},"I think Terraform has truly revolutionized infrastructure as code when it came out in 2014. At that time, cloud providers native solutions were too verbose and too complex, and there was no true alternative to them. Terraform came to simplify that and has enabled people to use the same IaC solution to deploy resources of different cloud providers. Today, things have changed: cloud providers' native solutions have evolved, there are new modern alternatives to Terraform (and I am not talking only about Pulumi), and Terraform has shown some limitations.",{"type":254,"tag":255,"props":43733,"children":43734},{},[43735],{"type":259,"value":43736},"Because Terraform is used in many companies, benefits from a rich ecosystem, and has a great community, you will probably not make a big mistake if you go with Terraform instead of Pulumi for your next project. However, if you do so, you have to be aware of the limitations and weaknesses of Terraform that I talked about in this article. This is something very important because each project has its needs and constraints, so maybe Terraform's limitations are not a big deal for your use case, maybe it is. Just make sure which one it is.",{"type":254,"tag":255,"props":43738,"children":43739},{},[43740],{"type":259,"value":43741},"Despite all I said, there is one thing I like very much about Terraform, it's its community. Because many people have been using it, and for some years now, there are a lot of examples on the internet and many interesting tools or frameworks have been created around it.",{"type":254,"tag":455,"props":43743,"children":43745},{"id":43744},"is-pulumi-perfect",[43746],{"type":259,"value":43747},"Is Pulumi perfect?",{"type":254,"tag":255,"props":43749,"children":43750},{},[43751],{"type":259,"value":43752},"No Pulumi is not perfect, no IaC solution is, there are always things to improve.",{"type":254,"tag":255,"props":43754,"children":43755},{},[43756],{"type":259,"value":43757},"For instance, a few Pulumi features are not yet available in .NET like Dynamic providers or policy as code (not a real problem as you can write them in TypeScript or Python even if your stack is in .NET).",{"type":254,"tag":255,"props":43759,"children":43760},{},[43761,43763,43769],{"type":259,"value":43762},"I also hope more native providers will be created (even if I know it's not only up to Pulumi) because I think native providers are a game-changer. In particular, I would want to have a native provider for Microsoft Graph (you can vote for the issue ",{"type":254,"tag":262,"props":43764,"children":43767},{"href":43765,"rel":43766},"https://github.com/pulumi/pulumi/issues/8963",[266],[43768],{"type":259,"value":1414},{"type":259,"value":2280},{"type":254,"tag":255,"props":43771,"children":43772},{},[43773],{"type":259,"value":43774},"More generally, I think Infrastructure as Code is evolving quickly and there are many things yet to come. But I am confident Pulumi will continue to bring more features and innovations to this space 🚀.",{"type":254,"tag":455,"props":43776,"children":43778},{"id":43777},"which-infrastructure-as-code-solution-should-you-choose",[43779],{"type":259,"value":43780},"Which Infrastructure as Code solution should you choose?",{"type":254,"tag":255,"props":43782,"children":43783},{},[43784],{"type":259,"value":43785},"To be honest, I can't think of any good reason why I would choose Terraform over Pulumi for a project. But that's just me.",{"type":254,"tag":255,"props":43787,"children":43788},{},[43789],{"type":259,"value":43790},"If you have already invested a lot of time learning HCL/Terraform and are happy with it, you should probably continue using Terraform.",{"type":254,"tag":255,"props":43792,"children":43793},{},[43794],{"type":259,"value":43795},"If you don't know either Terraform or Pulumi, I would suggest you use Pulumi even if you don't know any programming language. It's in my opinion the right way to do infrastructure as code. Furthermore, it is better to learn a programming language that could be useful somewhere else than HCL which you will only use in HashiCorp products.",{"type":254,"tag":255,"props":43797,"children":43798},{},[43799,43801,43806],{"type":259,"value":43800},"I would like to emphasize one thing: ",{"type":254,"tag":9718,"props":43802,"children":43803},{},[43804],{"type":259,"value":43805},"when choosing an IaC solution, make an informed decision.",{"type":259,"value":43807}," Do not choose Terraform because your favorite influencer promotes it. Do not choose Terraform because most people around you use it. The same is true for Pulumi. Do not choose Pulumi just because I say you should. Read articles from people with other points of view. Check what topics are important for you, and compare both solutions on these topics. Make up your own mind.",{"type":254,"tag":255,"props":43809,"children":43810},{},[43811],{"type":259,"value":43812},"In the end, you should choose the solution you feel is more appropriate to you and your project, and that you think will be easier for you to learn and use in the long term. If it's Terraform then go with it! If it's Pulumi, welcome to modern Infrastructure as Code!",{"title":205,"searchDepth":635,"depth":635,"links":43814},[43815,43816,43823,43830,43837],{"id":42593,"depth":635,"text":42596},{"id":42696,"depth":635,"text":42699,"children":43817},[43818,43819,43820,43821,43822],{"id":42707,"depth":679,"text":42710},{"id":42799,"depth":679,"text":42802},{"id":42916,"depth":679,"text":42919},{"id":43044,"depth":679,"text":43047},{"id":43135,"depth":679,"text":43138},{"id":43155,"depth":635,"text":43158,"children":43824},[43825,43826,43827,43828,43829],{"id":43161,"depth":679,"text":43164},{"id":43191,"depth":679,"text":43194},{"id":43267,"depth":679,"text":43270},{"id":43326,"depth":679,"text":43329},{"id":43408,"depth":679,"text":43411},{"id":43462,"depth":635,"text":43465,"children":43831},[43832,43833,43834,43835,43836],{"id":43478,"depth":679,"text":43481},{"id":43508,"depth":679,"text":43511},{"id":43551,"depth":679,"text":43554},{"id":43601,"depth":679,"text":43604},{"id":43617,"depth":679,"text":43620},{"id":6186,"depth":635,"text":37936,"children":43838},[43839,43840,43841,43842,43843],{"id":43649,"depth":679,"text":43652},{"id":43697,"depth":679,"text":43700},{"id":43718,"depth":679,"text":43721},{"id":43744,"depth":679,"text":43747},{"id":43777,"depth":679,"text":43780},"content:1.posts:36.pulumi-vs-terraform.md","1.posts/36.pulumi-vs-terraform.md",{"_path":112,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":111,"description":43847,"lead":43848,"date":43849,"image":43850,"badge":43852,"tags":43853,"body":43855,"_type":1275,"_id":46706,"_source":231,"_file":46707,"_extension":1278},"In this article, we will talk about how to provision an Azure SQL Database with authentication restricted to Active Directory users/groups/applications. We will use Pulumi to do that.","Using Pulumi and its command provider to grant database permissions","2022-02-22T00:00:00.000Z",{"src":43851},"/images/lockers_1.jpg",{"label":16349},[23935,43854,6260,1288,10225,19569],"Azure SQL Database",{"type":251,"children":43856,"toc":46688},[43857,43861,43867,43889,43902,43910,43915,43921,43926,44201,44231,44237,44242,44540,44552,44740,44746,44751,44763,44864,44884,44889,44894,44920,44925,44931,44945,44954,44984,44992,45008,45020,45026,45054,45059,45071,45187,45199,45228,45240,45279,45288,45315,45368,45374,45393,45399,45412,45487,45518,45523,45690,45696,45701,45878,45883,45901,45907,45926,46015,46026,46179,46198,46203,46583,46588,46620,46626,46644,46653,46657,46669,46674,46679,46684],{"type":254,"tag":255,"props":43858,"children":43859},{},[43860],{"type":259,"value":43847},{"type":254,"tag":282,"props":43862,"children":43864},{"id":43863},"why-this-article",[43865],{"type":259,"value":43866},"Why this article?",{"type":254,"tag":255,"props":43868,"children":43869},{},[43870,43872,43879,43881,43887],{"type":259,"value":43871},"In ",{"type":254,"tag":262,"props":43873,"children":43876},{"href":43874,"rel":43875},"https://www.techwatching.dev/posts/sqlclient-active-directory-authent",[266],[43877],{"type":259,"value":43878},"a previous article",{"type":259,"value":43880},", I already talked about connecting to an Azure SQL Database using Azure Active Directory authentication. However, my focus was on querying an Azure SQL Database from C# code (from an ASP.NET 6 Minimal API that was using ",{"type":254,"tag":389,"props":43882,"children":43884},{"className":43883},[],[43885],{"type":259,"value":43886},"Microsoft.Data.SqlClient",{"type":259,"value":43888}," 'Active Directory Default' authentication mode to be more precise), and not on the configuration of the Azure AD authentication itself.",{"type":254,"tag":255,"props":43890,"children":43891},{},[43892,43894,43900],{"type":259,"value":43893},"Still, in that article, I wrote an Azure CLI script that showed how to provision and configure the database with Azure AD authentication enabled. So why write another article about that? First because I did not show how to give an Azure AD entity (user, group, or managed identity) permission to access the database. (In my samples, to simplify things I was using the SQL server Azure AD administrator account to make my queries 🤫). Yet, it is something you will probably have to do if you want your App Service or Function App to query your database. Second because even if Azure CLI is great to handle Azure resources (if you are a reader of my blog, you probably know that I ",{"type":254,"tag":262,"props":43895,"children":43897},{"href":17271,"rel":43896},[266],[43898],{"type":259,"value":43899},"enjoy very much Azure CLI",{"type":259,"value":43901},"), in a real project I would probably use a more advanced Infrastructure as Code solution like Pulumi. And that is what we will show here.",{"type":254,"tag":427,"props":43903,"children":43904},{"icon":429},[43905],{"type":254,"tag":255,"props":43906,"children":43907},{},[43908],{"type":259,"value":43909},"If you are not familiar with Pulumi, it is an IaC solution similar to Terraform but using programming languages like C#. Speaking of C#, that is what I will use to write my infrastructure code but you can easily do the same in another language supported by Pulumi (TypeScript, Go, Python,... choose the one you are used to), the concepts stay relevant and the code will be similar.",{"type":254,"tag":255,"props":43911,"children":43912},{},[43913],{"type":259,"value":43914},"Now, let's get to the heart of the matter.",{"type":254,"tag":282,"props":43916,"children":43918},{"id":43917},"an-azure-ad-user-as-our-sql-server-administrator",[43919],{"type":259,"value":43920},"An Azure AD user as our SQL Server administrator",{"type":254,"tag":255,"props":43922,"children":43923},{},[43924],{"type":259,"value":43925},"Usually, when you create an Azure SQL Server, you have to provide an administrator login and an administrator password. But I said I wanted to limit the authentication to Azure Active Directory authentication only. So we will only need an Azure AD account to set as the administrator of our SQL Server. We could use an existing Azure AD account, but let's create a new Azure AD user just for that:",{"type":254,"tag":612,"props":43927,"children":43929},{"className":1423,"code":43928,"language":1425,"meta":205,"style":205},"var config = new Config();\nvar sqlAdAdminLogin = config.Require(\"sqlAdAdmin\");\nvar sqlAdAdminPassword = config.RequireSecret(\"sqlAdPassword\");\n\nvar sqlAdAdmin = new User(\"sqlAdmin\", new UserArgs\n{\n    UserPrincipalName = sqlAdAdminLogin,\n    Password = sqlAdAdminPassword,\n    DisplayName = \"Global SQL Admin\"\n});\n",[43930],{"type":254,"tag":389,"props":43931,"children":43932},{"__ignoreMap":205},[43933,43961,44011,44061,44068,44123,44130,44150,44170,44194],{"type":254,"tag":622,"props":43934,"children":43935},{"class":624,"line":625},[43936,43940,43944,43948,43952,43957],{"type":254,"tag":622,"props":43937,"children":43938},{"style":714},[43939],{"type":259,"value":1710},{"type":254,"tag":622,"props":43941,"children":43942},{"style":714},[43943],{"type":259,"value":20010},{"type":254,"tag":622,"props":43945,"children":43946},{"style":629},[43947],{"type":259,"value":1619},{"type":254,"tag":622,"props":43949,"children":43950},{"style":629},[43951],{"type":259,"value":1624},{"type":254,"tag":622,"props":43953,"children":43954},{"style":714},[43955],{"type":259,"value":43956}," Config",{"type":254,"tag":622,"props":43958,"children":43959},{"style":629},[43960],{"type":259,"value":2798},{"type":254,"tag":622,"props":43962,"children":43963},{"class":624,"line":635},[43964,43968,43973,43977,43981,43985,43990,43994,43998,44003,44007],{"type":254,"tag":622,"props":43965,"children":43966},{"style":714},[43967],{"type":259,"value":1710},{"type":254,"tag":622,"props":43969,"children":43970},{"style":714},[43971],{"type":259,"value":43972}," sqlAdAdminLogin",{"type":254,"tag":622,"props":43974,"children":43975},{"style":629},[43976],{"type":259,"value":1619},{"type":254,"tag":622,"props":43978,"children":43979},{"style":1202},[43980],{"type":259,"value":20010},{"type":254,"tag":622,"props":43982,"children":43983},{"style":629},[43984],{"type":259,"value":594},{"type":254,"tag":622,"props":43986,"children":43987},{"style":1735},[43988],{"type":259,"value":43989},"Require",{"type":254,"tag":622,"props":43991,"children":43992},{"style":629},[43993],{"type":259,"value":1668},{"type":254,"tag":622,"props":43995,"children":43996},{"style":629},[43997],{"type":259,"value":652},{"type":254,"tag":622,"props":43999,"children":44000},{"style":664},[44001],{"type":259,"value":44002},"sqlAdAdmin",{"type":254,"tag":622,"props":44004,"children":44005},{"style":629},[44006],{"type":259,"value":652},{"type":254,"tag":622,"props":44008,"children":44009},{"style":629},[44010],{"type":259,"value":1554},{"type":254,"tag":622,"props":44012,"children":44013},{"class":624,"line":679},[44014,44018,44023,44027,44031,44035,44040,44044,44048,44053,44057],{"type":254,"tag":622,"props":44015,"children":44016},{"style":714},[44017],{"type":259,"value":1710},{"type":254,"tag":622,"props":44019,"children":44020},{"style":714},[44021],{"type":259,"value":44022}," sqlAdAdminPassword",{"type":254,"tag":622,"props":44024,"children":44025},{"style":629},[44026],{"type":259,"value":1619},{"type":254,"tag":622,"props":44028,"children":44029},{"style":1202},[44030],{"type":259,"value":20010},{"type":254,"tag":622,"props":44032,"children":44033},{"style":629},[44034],{"type":259,"value":594},{"type":254,"tag":622,"props":44036,"children":44037},{"style":1735},[44038],{"type":259,"value":44039},"RequireSecret",{"type":254,"tag":622,"props":44041,"children":44042},{"style":629},[44043],{"type":259,"value":1668},{"type":254,"tag":622,"props":44045,"children":44046},{"style":629},[44047],{"type":259,"value":652},{"type":254,"tag":622,"props":44049,"children":44050},{"style":664},[44051],{"type":259,"value":44052},"sqlAdPassword",{"type":254,"tag":622,"props":44054,"children":44055},{"style":629},[44056],{"type":259,"value":652},{"type":254,"tag":622,"props":44058,"children":44059},{"style":629},[44060],{"type":259,"value":1554},{"type":254,"tag":622,"props":44062,"children":44063},{"class":624,"line":705},[44064],{"type":254,"tag":622,"props":44065,"children":44066},{"emptyLinePlaceholder":1699},[44067],{"type":259,"value":1702},{"type":254,"tag":622,"props":44069,"children":44070},{"class":624,"line":732},[44071,44075,44080,44084,44088,44093,44097,44101,44106,44110,44114,44118],{"type":254,"tag":622,"props":44072,"children":44073},{"style":714},[44074],{"type":259,"value":1710},{"type":254,"tag":622,"props":44076,"children":44077},{"style":714},[44078],{"type":259,"value":44079}," sqlAdAdmin",{"type":254,"tag":622,"props":44081,"children":44082},{"style":629},[44083],{"type":259,"value":1619},{"type":254,"tag":622,"props":44085,"children":44086},{"style":629},[44087],{"type":259,"value":1624},{"type":254,"tag":622,"props":44089,"children":44090},{"style":714},[44091],{"type":259,"value":44092}," User",{"type":254,"tag":622,"props":44094,"children":44095},{"style":629},[44096],{"type":259,"value":1668},{"type":254,"tag":622,"props":44098,"children":44099},{"style":629},[44100],{"type":259,"value":652},{"type":254,"tag":622,"props":44102,"children":44103},{"style":664},[44104],{"type":259,"value":44105},"sqlAdmin",{"type":254,"tag":622,"props":44107,"children":44108},{"style":629},[44109],{"type":259,"value":652},{"type":254,"tag":622,"props":44111,"children":44112},{"style":629},[44113],{"type":259,"value":3149},{"type":254,"tag":622,"props":44115,"children":44116},{"style":629},[44117],{"type":259,"value":1624},{"type":254,"tag":622,"props":44119,"children":44120},{"style":714},[44121],{"type":259,"value":44122}," UserArgs\n",{"type":254,"tag":622,"props":44124,"children":44125},{"class":624,"line":759},[44126],{"type":254,"tag":622,"props":44127,"children":44128},{"style":629},[44129],{"type":259,"value":632},{"type":254,"tag":622,"props":44131,"children":44132},{"class":624,"line":798},[44133,44138,44142,44146],{"type":254,"tag":622,"props":44134,"children":44135},{"style":1202},[44136],{"type":259,"value":44137},"    UserPrincipalName ",{"type":254,"tag":622,"props":44139,"children":44140},{"style":629},[44141],{"type":259,"value":1654},{"type":254,"tag":622,"props":44143,"children":44144},{"style":1202},[44145],{"type":259,"value":43972},{"type":254,"tag":622,"props":44147,"children":44148},{"style":629},[44149],{"type":259,"value":676},{"type":254,"tag":622,"props":44151,"children":44152},{"class":624,"line":836},[44153,44158,44162,44166],{"type":254,"tag":622,"props":44154,"children":44155},{"style":1202},[44156],{"type":259,"value":44157},"    Password ",{"type":254,"tag":622,"props":44159,"children":44160},{"style":629},[44161],{"type":259,"value":1654},{"type":254,"tag":622,"props":44163,"children":44164},{"style":1202},[44165],{"type":259,"value":44022},{"type":254,"tag":622,"props":44167,"children":44168},{"style":629},[44169],{"type":259,"value":676},{"type":254,"tag":622,"props":44171,"children":44172},{"class":624,"line":862},[44173,44177,44181,44185,44190],{"type":254,"tag":622,"props":44174,"children":44175},{"style":1202},[44176],{"type":259,"value":21751},{"type":254,"tag":622,"props":44178,"children":44179},{"style":629},[44180],{"type":259,"value":1654},{"type":254,"tag":622,"props":44182,"children":44183},{"style":629},[44184],{"type":259,"value":661},{"type":254,"tag":622,"props":44186,"children":44187},{"style":664},[44188],{"type":259,"value":44189},"Global SQL Admin",{"type":254,"tag":622,"props":44191,"children":44192},{"style":629},[44193],{"type":259,"value":9909},{"type":254,"tag":622,"props":44195,"children":44196},{"class":624,"line":900},[44197],{"type":254,"tag":622,"props":44198,"children":44199},{"style":629},[44200],{"type":259,"value":6661},{"type":254,"tag":255,"props":44202,"children":44203},{},[44204,44206,44212,44214,44220,44222,44229],{"type":259,"value":44205},"To create a new Azure AD user we need a login (it will be the email of the new user in our tenant) and a password. In this example, we retrieve these values from the ",{"type":254,"tag":262,"props":44207,"children":44210},{"href":44208,"rel":44209},"https://www.pulumi.com/docs/intro/concepts/config/",[266],[44211],{"type":259,"value":16047},{"type":259,"value":44213}," which is stored in the YAML settings file. You can notice there that we retrieve a secret (the password) from the configuration thanks to the ",{"type":254,"tag":389,"props":44215,"children":44217},{"className":44216},[],[44218],{"type":259,"value":44219},"config.RequireSecret",{"type":259,"value":44221}," method. Indeed to avoid exposing a secret in the configuration file or the state file, Pulumi has ",{"type":254,"tag":262,"props":44223,"children":44226},{"href":44224,"rel":44225},"https://www.pulumi.com/docs/intro/concepts/secrets/",[266],[44227],{"type":259,"value":44228},"built-in support for secret encryption and decryption",{"type":259,"value":44230}," (not sure Terraform folks can say the same thing 😉).",{"type":254,"tag":282,"props":44232,"children":44234},{"id":44233},"create-the-azure-sql-server-and-its-database",[44235],{"type":259,"value":44236},"Create the Azure SQL Server and its database.",{"type":254,"tag":255,"props":44238,"children":44239},{},[44240],{"type":259,"value":44241},"Now that we have our administrator account, we can create the Azure SQL Server:",{"type":254,"tag":612,"props":44243,"children":44245},{"className":1423,"code":44244,"language":1425,"meta":205,"style":205},"var sqlServer = new Server($\"sql-sqlDbWithAzureAd-{Deployment.Instance.StackName}\", new ServerArgs\n{\n    ResourceGroupName = resourceGroup.Name,\n    Administrators = new ServerExternalAdministratorArgs\n    {\n        Login = sqlAdAdmin.UserPrincipalName,\n        Sid = sqlAdAdmin.Id,\n        AzureADOnlyAuthentication = true,\n        AdministratorType = AdministratorType.ActiveDirectory,\n        PrincipalType = PrincipalType.User,\n    },\n});\n",[44246],{"type":254,"tag":389,"props":44247,"children":44248},{"__ignoreMap":205},[44249,44328,44335,44362,44383,44390,44419,44447,44467,44497,44526,44533],{"type":254,"tag":622,"props":44250,"children":44251},{"class":624,"line":625},[44252,44256,44261,44265,44269,44274,44278,44282,44287,44291,44295,44299,44303,44307,44311,44315,44319,44323],{"type":254,"tag":622,"props":44253,"children":44254},{"style":714},[44255],{"type":259,"value":1710},{"type":254,"tag":622,"props":44257,"children":44258},{"style":714},[44259],{"type":259,"value":44260}," sqlServer",{"type":254,"tag":622,"props":44262,"children":44263},{"style":629},[44264],{"type":259,"value":1619},{"type":254,"tag":622,"props":44266,"children":44267},{"style":629},[44268],{"type":259,"value":1624},{"type":254,"tag":622,"props":44270,"children":44271},{"style":714},[44272],{"type":259,"value":44273}," Server",{"type":254,"tag":622,"props":44275,"children":44276},{"style":629},[44277],{"type":259,"value":1668},{"type":254,"tag":622,"props":44279,"children":44280},{"style":629},[44281],{"type":259,"value":9607},{"type":254,"tag":622,"props":44283,"children":44284},{"style":664},[44285],{"type":259,"value":44286},"sql-sqlDbWithAzureAd-",{"type":254,"tag":622,"props":44288,"children":44289},{"style":629},[44290],{"type":259,"value":3234},{"type":254,"tag":622,"props":44292,"children":44293},{"style":1202},[44294],{"type":259,"value":34724},{"type":254,"tag":622,"props":44296,"children":44297},{"style":629},[44298],{"type":259,"value":594},{"type":254,"tag":622,"props":44300,"children":44301},{"style":1202},[44302],{"type":259,"value":34733},{"type":254,"tag":622,"props":44304,"children":44305},{"style":629},[44306],{"type":259,"value":594},{"type":254,"tag":622,"props":44308,"children":44309},{"style":1202},[44310],{"type":259,"value":34775},{"type":254,"tag":622,"props":44312,"children":44313},{"style":629},[44314],{"type":259,"value":2443},{"type":254,"tag":622,"props":44316,"children":44317},{"style":629},[44318],{"type":259,"value":3149},{"type":254,"tag":622,"props":44320,"children":44321},{"style":629},[44322],{"type":259,"value":1624},{"type":254,"tag":622,"props":44324,"children":44325},{"style":714},[44326],{"type":259,"value":44327}," ServerArgs\n",{"type":254,"tag":622,"props":44329,"children":44330},{"class":624,"line":635},[44331],{"type":254,"tag":622,"props":44332,"children":44333},{"style":629},[44334],{"type":259,"value":632},{"type":254,"tag":622,"props":44336,"children":44337},{"class":624,"line":679},[44338,44342,44346,44350,44354,44358],{"type":254,"tag":622,"props":44339,"children":44340},{"style":1202},[44341],{"type":259,"value":34928},{"type":254,"tag":622,"props":44343,"children":44344},{"style":629},[44345],{"type":259,"value":1654},{"type":254,"tag":622,"props":44347,"children":44348},{"style":1202},[44349],{"type":259,"value":34689},{"type":254,"tag":622,"props":44351,"children":44352},{"style":629},[44353],{"type":259,"value":594},{"type":254,"tag":622,"props":44355,"children":44356},{"style":1202},[44357],{"type":259,"value":2254},{"type":254,"tag":622,"props":44359,"children":44360},{"style":629},[44361],{"type":259,"value":676},{"type":254,"tag":622,"props":44363,"children":44364},{"class":624,"line":705},[44365,44370,44374,44378],{"type":254,"tag":622,"props":44366,"children":44367},{"style":1202},[44368],{"type":259,"value":44369},"    Administrators ",{"type":254,"tag":622,"props":44371,"children":44372},{"style":629},[44373],{"type":259,"value":1654},{"type":254,"tag":622,"props":44375,"children":44376},{"style":629},[44377],{"type":259,"value":1624},{"type":254,"tag":622,"props":44379,"children":44380},{"style":714},[44381],{"type":259,"value":44382}," ServerExternalAdministratorArgs\n",{"type":254,"tag":622,"props":44384,"children":44385},{"class":624,"line":732},[44386],{"type":254,"tag":622,"props":44387,"children":44388},{"style":629},[44389],{"type":259,"value":3574},{"type":254,"tag":622,"props":44391,"children":44392},{"class":624,"line":759},[44393,44398,44402,44406,44410,44415],{"type":254,"tag":622,"props":44394,"children":44395},{"style":1202},[44396],{"type":259,"value":44397},"        Login ",{"type":254,"tag":622,"props":44399,"children":44400},{"style":629},[44401],{"type":259,"value":1654},{"type":254,"tag":622,"props":44403,"children":44404},{"style":1202},[44405],{"type":259,"value":44079},{"type":254,"tag":622,"props":44407,"children":44408},{"style":629},[44409],{"type":259,"value":594},{"type":254,"tag":622,"props":44411,"children":44412},{"style":1202},[44413],{"type":259,"value":44414},"UserPrincipalName",{"type":254,"tag":622,"props":44416,"children":44417},{"style":629},[44418],{"type":259,"value":676},{"type":254,"tag":622,"props":44420,"children":44421},{"class":624,"line":798},[44422,44427,44431,44435,44439,44443],{"type":254,"tag":622,"props":44423,"children":44424},{"style":1202},[44425],{"type":259,"value":44426},"        Sid ",{"type":254,"tag":622,"props":44428,"children":44429},{"style":629},[44430],{"type":259,"value":1654},{"type":254,"tag":622,"props":44432,"children":44433},{"style":1202},[44434],{"type":259,"value":44079},{"type":254,"tag":622,"props":44436,"children":44437},{"style":629},[44438],{"type":259,"value":594},{"type":254,"tag":622,"props":44440,"children":44441},{"style":1202},[44442],{"type":259,"value":20553},{"type":254,"tag":622,"props":44444,"children":44445},{"style":629},[44446],{"type":259,"value":676},{"type":254,"tag":622,"props":44448,"children":44449},{"class":624,"line":836},[44450,44455,44459,44463],{"type":254,"tag":622,"props":44451,"children":44452},{"style":1202},[44453],{"type":259,"value":44454},"        AzureADOnlyAuthentication ",{"type":254,"tag":622,"props":44456,"children":44457},{"style":629},[44458],{"type":259,"value":1654},{"type":254,"tag":622,"props":44460,"children":44461},{"style":7715},[44462],{"type":259,"value":7718},{"type":254,"tag":622,"props":44464,"children":44465},{"style":629},[44466],{"type":259,"value":676},{"type":254,"tag":622,"props":44468,"children":44469},{"class":624,"line":862},[44470,44475,44479,44484,44488,44493],{"type":254,"tag":622,"props":44471,"children":44472},{"style":1202},[44473],{"type":259,"value":44474},"        AdministratorType ",{"type":254,"tag":622,"props":44476,"children":44477},{"style":629},[44478],{"type":259,"value":1654},{"type":254,"tag":622,"props":44480,"children":44481},{"style":1202},[44482],{"type":259,"value":44483}," AdministratorType",{"type":254,"tag":622,"props":44485,"children":44486},{"style":629},[44487],{"type":259,"value":594},{"type":254,"tag":622,"props":44489,"children":44490},{"style":1202},[44491],{"type":259,"value":44492},"ActiveDirectory",{"type":254,"tag":622,"props":44494,"children":44495},{"style":629},[44496],{"type":259,"value":676},{"type":254,"tag":622,"props":44498,"children":44499},{"class":624,"line":900},[44500,44505,44509,44513,44517,44522],{"type":254,"tag":622,"props":44501,"children":44502},{"style":1202},[44503],{"type":259,"value":44504},"        PrincipalType ",{"type":254,"tag":622,"props":44506,"children":44507},{"style":629},[44508],{"type":259,"value":1654},{"type":254,"tag":622,"props":44510,"children":44511},{"style":1202},[44512],{"type":259,"value":22019},{"type":254,"tag":622,"props":44514,"children":44515},{"style":629},[44516],{"type":259,"value":594},{"type":254,"tag":622,"props":44518,"children":44519},{"style":1202},[44520],{"type":259,"value":44521},"User",{"type":254,"tag":622,"props":44523,"children":44524},{"style":629},[44525],{"type":259,"value":676},{"type":254,"tag":622,"props":44527,"children":44528},{"class":624,"line":938},[44529],{"type":254,"tag":622,"props":44530,"children":44531},{"style":629},[44532],{"type":259,"value":20453},{"type":254,"tag":622,"props":44534,"children":44535},{"class":624,"line":976},[44536],{"type":254,"tag":622,"props":44537,"children":44538},{"style":629},[44539],{"type":259,"value":6661},{"type":254,"tag":255,"props":44541,"children":44542},{},[44543,44545,44550],{"type":259,"value":44544},"Nothing special here: we are using the variable ",{"type":254,"tag":389,"props":44546,"children":44548},{"className":44547},[],[44549],{"type":259,"value":44105},{"type":259,"value":44551}," that is our newly created user to set the administrator of the SQL Server and we set the authentication to Azure AD only. We can then create the database:",{"type":254,"tag":612,"props":44553,"children":44555},{"className":1423,"code":44554,"language":1425,"meta":205,"style":205},"var database = new Database(\"sqldb-sqlDbWithAzureAd-Main\", new DatabaseArgs\n{\n    ResourceGroupName = resourceGroup.Name,\n    ServerName = sqlServer.Name,\n    Sku = new SkuArgs\n    {\n        Name = \"Basic\"\n    }\n});\n",[44556],{"type":254,"tag":389,"props":44557,"children":44558},{"__ignoreMap":205},[44559,44614,44621,44648,44676,44696,44703,44726,44733],{"type":254,"tag":622,"props":44560,"children":44561},{"class":624,"line":625},[44562,44566,44571,44575,44579,44584,44588,44592,44597,44601,44605,44609],{"type":254,"tag":622,"props":44563,"children":44564},{"style":714},[44565],{"type":259,"value":1710},{"type":254,"tag":622,"props":44567,"children":44568},{"style":714},[44569],{"type":259,"value":44570}," database",{"type":254,"tag":622,"props":44572,"children":44573},{"style":629},[44574],{"type":259,"value":1619},{"type":254,"tag":622,"props":44576,"children":44577},{"style":629},[44578],{"type":259,"value":1624},{"type":254,"tag":622,"props":44580,"children":44581},{"style":714},[44582],{"type":259,"value":44583}," Database",{"type":254,"tag":622,"props":44585,"children":44586},{"style":629},[44587],{"type":259,"value":1668},{"type":254,"tag":622,"props":44589,"children":44590},{"style":629},[44591],{"type":259,"value":652},{"type":254,"tag":622,"props":44593,"children":44594},{"style":664},[44595],{"type":259,"value":44596},"sqldb-sqlDbWithAzureAd-Main",{"type":254,"tag":622,"props":44598,"children":44599},{"style":629},[44600],{"type":259,"value":652},{"type":254,"tag":622,"props":44602,"children":44603},{"style":629},[44604],{"type":259,"value":3149},{"type":254,"tag":622,"props":44606,"children":44607},{"style":629},[44608],{"type":259,"value":1624},{"type":254,"tag":622,"props":44610,"children":44611},{"style":714},[44612],{"type":259,"value":44613}," DatabaseArgs\n",{"type":254,"tag":622,"props":44615,"children":44616},{"class":624,"line":635},[44617],{"type":254,"tag":622,"props":44618,"children":44619},{"style":629},[44620],{"type":259,"value":632},{"type":254,"tag":622,"props":44622,"children":44623},{"class":624,"line":679},[44624,44628,44632,44636,44640,44644],{"type":254,"tag":622,"props":44625,"children":44626},{"style":1202},[44627],{"type":259,"value":34928},{"type":254,"tag":622,"props":44629,"children":44630},{"style":629},[44631],{"type":259,"value":1654},{"type":254,"tag":622,"props":44633,"children":44634},{"style":1202},[44635],{"type":259,"value":34689},{"type":254,"tag":622,"props":44637,"children":44638},{"style":629},[44639],{"type":259,"value":594},{"type":254,"tag":622,"props":44641,"children":44642},{"style":1202},[44643],{"type":259,"value":2254},{"type":254,"tag":622,"props":44645,"children":44646},{"style":629},[44647],{"type":259,"value":676},{"type":254,"tag":622,"props":44649,"children":44650},{"class":624,"line":705},[44651,44656,44660,44664,44668,44672],{"type":254,"tag":622,"props":44652,"children":44653},{"style":1202},[44654],{"type":259,"value":44655},"    ServerName ",{"type":254,"tag":622,"props":44657,"children":44658},{"style":629},[44659],{"type":259,"value":1654},{"type":254,"tag":622,"props":44661,"children":44662},{"style":1202},[44663],{"type":259,"value":44260},{"type":254,"tag":622,"props":44665,"children":44666},{"style":629},[44667],{"type":259,"value":594},{"type":254,"tag":622,"props":44669,"children":44670},{"style":1202},[44671],{"type":259,"value":2254},{"type":254,"tag":622,"props":44673,"children":44674},{"style":629},[44675],{"type":259,"value":676},{"type":254,"tag":622,"props":44677,"children":44678},{"class":624,"line":732},[44679,44683,44687,44691],{"type":254,"tag":622,"props":44680,"children":44681},{"style":1202},[44682],{"type":259,"value":34993},{"type":254,"tag":622,"props":44684,"children":44685},{"style":629},[44686],{"type":259,"value":1654},{"type":254,"tag":622,"props":44688,"children":44689},{"style":629},[44690],{"type":259,"value":1624},{"type":254,"tag":622,"props":44692,"children":44693},{"style":714},[44694],{"type":259,"value":44695}," SkuArgs\n",{"type":254,"tag":622,"props":44697,"children":44698},{"class":624,"line":759},[44699],{"type":254,"tag":622,"props":44700,"children":44701},{"style":629},[44702],{"type":259,"value":3574},{"type":254,"tag":622,"props":44704,"children":44705},{"class":624,"line":798},[44706,44710,44714,44718,44722],{"type":254,"tag":622,"props":44707,"children":44708},{"style":1202},[44709],{"type":259,"value":35062},{"type":254,"tag":622,"props":44711,"children":44712},{"style":629},[44713],{"type":259,"value":1654},{"type":254,"tag":622,"props":44715,"children":44716},{"style":629},[44717],{"type":259,"value":661},{"type":254,"tag":622,"props":44719,"children":44720},{"style":664},[44721],{"type":259,"value":35042},{"type":254,"tag":622,"props":44723,"children":44724},{"style":629},[44725],{"type":259,"value":9909},{"type":254,"tag":622,"props":44727,"children":44728},{"class":624,"line":836},[44729],{"type":254,"tag":622,"props":44730,"children":44731},{"style":629},[44732],{"type":259,"value":1093},{"type":254,"tag":622,"props":44734,"children":44735},{"class":624,"line":862},[44736],{"type":254,"tag":622,"props":44737,"children":44738},{"style":629},[44739],{"type":259,"value":6661},{"type":254,"tag":282,"props":44741,"children":44743},{"id":44742},"grant-sql-database-access-permissions-to-azure-ad-entities",[44744],{"type":259,"value":44745},"Grant SQL Database access permissions to Azure AD entities",{"type":254,"tag":255,"props":44747,"children":44748},{},[44749],{"type":259,"value":44750},"Once we have provisioned the Azure SQL Server and its database, here comes the tough part: we need to configure who can access the database. In a project, you will probably have to give access to some users and to the Azure resources that need to query the database (you will have to assign these resources a managed identity before that). But to keep things simple, we will just consider we need to grant SQL Database access to an Azure AD group. That could be a good way to do things by the way: create an Azure AD group, grant permissions to this group and add users and managed identities that need access to the database.",{"type":254,"tag":255,"props":44752,"children":44753},{},[44754,44756,44762],{"type":259,"value":44755},"Why did I say that this part was tough? It's because to grant SQL database permissions, we need to execute an SQL command on the Server as you can read ",{"type":254,"tag":262,"props":44757,"children":44760},{"href":44758,"rel":44759},"https://docs.microsoft.com/en-us/azure/app-service/tutorial-connect-msi-sql-database?tabs=windowsclient%2Cef%2Cdotnet#grant-permissions-to-managed-identity",[266],[44761],{"type":259,"value":17118},{"type":259,"value":594},{"type":254,"tag":612,"props":44764,"children":44768},{"className":44765,"code":44766,"language":44767,"meta":205,"style":205},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","CREATE USER [\u003Cidentity-name>] FROM EXTERNAL PROVIDER;\nALTER ROLE db_datareader ADD MEMBER [\u003Cidentity-name>];\nALTER ROLE db_datawriter ADD MEMBER [\u003Cidentity-name>];\nGO\n","sql",[44769],{"type":254,"tag":389,"props":44770,"children":44771},{"__ignoreMap":205},[44772,44804,44832,44856],{"type":254,"tag":622,"props":44773,"children":44774},{"class":624,"line":625},[44775,44780,44785,44790,44795,44800],{"type":254,"tag":622,"props":44776,"children":44777},{"style":741},[44778],{"type":259,"value":44779},"CREATE",{"type":254,"tag":622,"props":44781,"children":44782},{"style":1202},[44783],{"type":259,"value":44784}," USER [\u003Cidentity-name>] ",{"type":254,"tag":622,"props":44786,"children":44787},{"style":741},[44788],{"type":259,"value":44789},"FROM",{"type":254,"tag":622,"props":44791,"children":44792},{"style":741},[44793],{"type":259,"value":44794}," EXTERNAL",{"type":254,"tag":622,"props":44796,"children":44797},{"style":741},[44798],{"type":259,"value":44799}," PROVIDER",{"type":254,"tag":622,"props":44801,"children":44802},{"style":1202},[44803],{"type":259,"value":2350},{"type":254,"tag":622,"props":44805,"children":44806},{"class":624,"line":635},[44807,44812,44817,44822,44827],{"type":254,"tag":622,"props":44808,"children":44809},{"style":741},[44810],{"type":259,"value":44811},"ALTER",{"type":254,"tag":622,"props":44813,"children":44814},{"style":741},[44815],{"type":259,"value":44816}," ROLE",{"type":254,"tag":622,"props":44818,"children":44819},{"style":1202},[44820],{"type":259,"value":44821}," db_datareader ",{"type":254,"tag":622,"props":44823,"children":44824},{"style":741},[44825],{"type":259,"value":44826},"ADD",{"type":254,"tag":622,"props":44828,"children":44829},{"style":1202},[44830],{"type":259,"value":44831}," MEMBER [\u003Cidentity-name>];\n",{"type":254,"tag":622,"props":44833,"children":44834},{"class":624,"line":679},[44835,44839,44843,44848,44852],{"type":254,"tag":622,"props":44836,"children":44837},{"style":741},[44838],{"type":259,"value":44811},{"type":254,"tag":622,"props":44840,"children":44841},{"style":741},[44842],{"type":259,"value":44816},{"type":254,"tag":622,"props":44844,"children":44845},{"style":1202},[44846],{"type":259,"value":44847}," db_datawriter ",{"type":254,"tag":622,"props":44849,"children":44850},{"style":741},[44851],{"type":259,"value":44826},{"type":254,"tag":622,"props":44853,"children":44854},{"style":1202},[44855],{"type":259,"value":44831},{"type":254,"tag":622,"props":44857,"children":44858},{"class":624,"line":705},[44859],{"type":254,"tag":622,"props":44860,"children":44861},{"style":741},[44862],{"type":259,"value":44863},"GO\n",{"type":254,"tag":255,"props":44865,"children":44866},{},[44867,44869,44875,44876,44882],{"type":259,"value":44868},"With this command, we are creating a user and giving ",{"type":254,"tag":389,"props":44870,"children":44872},{"className":44871},[],[44873],{"type":259,"value":44874},"db_datareader",{"type":259,"value":10347},{"type":254,"tag":389,"props":44877,"children":44879},{"className":44878},[],[44880],{"type":259,"value":44881},"db_datawriter",{"type":259,"value":44883}," roles. However it is not a classical user, it's a user that is \"external\" to the database: in our case, it corresponds to an Azure AD entity (a user, group, or application).",{"type":254,"tag":255,"props":44885,"children":44886},{},[44887],{"type":259,"value":44888},"So it's not just about setting a property to properly configure an Azure resource, it's a bit more complicated.",{"type":254,"tag":255,"props":44890,"children":44891},{},[44892],{"type":259,"value":44893},"I see multiple ways of doing that:",{"type":254,"tag":332,"props":44895,"children":44896},{},[44897,44902,44907],{"type":254,"tag":336,"props":44898,"children":44899},{},[44900],{"type":259,"value":44901},"Create a new Pulumi provider \"SQL Server provider\" that is to able manage users in an SQL Server database",{"type":254,"tag":336,"props":44903,"children":44904},{},[44905],{"type":259,"value":44906},"Write custom C# code that executes the SQL command once the database is created",{"type":254,"tag":336,"props":44908,"children":44909},{},[44910,44912,44918],{"type":259,"value":44911},"Use the Pulumi Command provider to execute the SQL command using the ",{"type":254,"tag":389,"props":44913,"children":44915},{"className":44914},[],[44916],{"type":259,"value":44917},"sqlcmd",{"type":259,"value":44919}," utility",{"type":254,"tag":255,"props":44921,"children":44922},{},[44923],{"type":259,"value":44924},"Let's review these solutions.",{"type":254,"tag":455,"props":44926,"children":44928},{"id":44927},"new-sql-server-provider",[44929],{"type":259,"value":44930},"New \"SQL Server Provider\"",{"type":254,"tag":255,"props":44932,"children":44933},{},[44934,44936,44943],{"type":259,"value":44935},"To manage SQL Server resources like users and roles, we can create a complete provider. We could create it from scratch of course or use this ",{"type":254,"tag":262,"props":44937,"children":44940},{"href":44938,"rel":44939},"https://github.com/pulumi/pulumi-provider-boilerplate",[266],[44941],{"type":259,"value":44942},"Pulumi GitHub repository",{"type":259,"value":44944}," that provides some boilerplate code to create a Pulumi provider. Usually, Pulumi providers are written in Go (like the Terraform providers by the way) and generate SDKs for all programming languages supported by Pulumi.",{"type":254,"tag":255,"props":44946,"children":44947},{},[44948],{"type":254,"tag":1161,"props":44949,"children":44953},{"alt":44950,"className":44951,"src":44952},"xyz Pulumi Provider boilerplate code repository on GitHub.",[1165,1166],"/posts/images/sqldatabase_ad_provider_1.png",[],{"type":254,"tag":255,"props":44955,"children":44956},{},[44957,44959,44966,44968,44973,44975,44982],{"type":259,"value":44958},"Another way would be to adapt the existing ",{"type":254,"tag":262,"props":44960,"children":44963},{"href":44961,"rel":44962},"https://registry.terraform.io/providers/betr-io/mssql/latest/docs",[266],[44964],{"type":259,"value":44965},"Microsoft SQL Server Provider",{"type":259,"value":44967}," for Terraform. This Terraform provider made by the community enables you to create and manage logins and users on a SQL Server. I talked about \"adapting\" this provider because you can create a Pulumi provider out of a Terraform provider by using the ",{"type":254,"tag":262,"props":44969,"children":44971},{"href":43237,"rel":44970},[266],[44972],{"type":259,"value":43241},{"type":259,"value":44974},". That's great because instead of reinventing the wheel you can benefit from Terraform ecosystem by creating a Pulumi provider that wraps an existing Terraform provider. This ",{"type":254,"tag":262,"props":44976,"children":44979},{"href":44977,"rel":44978},"https://github.com/pulumi/pulumi-tf-provider-boilerplate",[266],[44980],{"type":259,"value":44981},"GitHub repository",{"type":259,"value":44983}," contains boilerplate code to do exactly that.",{"type":254,"tag":255,"props":44985,"children":44986},{},[44987],{"type":254,"tag":1161,"props":44988,"children":44991},{"alt":43205,"className":44989,"src":44990},[1165,1166],"/posts/images/sqldatabase_ad_provider_2.png",[],{"type":254,"tag":427,"props":44993,"children":44994},{"icon":429},[44995],{"type":254,"tag":255,"props":44996,"children":44997},{},[44998,45000,45006],{"type":259,"value":44999},"You might have noticed that I sometimes criticize Terraform in my articles. That's not because I think Terraform is a bad infrastructure as code solution, in fact, I think it is a great solution with a rich ecosystem. However, I am critical of Terraform because I believe Infrastructure as Code should be done with programming languages instead of Domain-Specific Languages. Moreover, there are some areas (API coverage of major cloud providers, security, IDE support, ...) where I found Terraform is not good enough, especially compared to other platforms like Pulumi. So I am always a bit disappointed when I see that many people choose by default Terraform as their infrastructure as code platform without considering alternatives (and I am not only talking about Pulumi, there are also Farmer and Bicep for instance), even when these alternatives would be better suited to their use cases. That being said, Terraform has also advantages like its great community that creates and contributes to many providers like the ",{"type":254,"tag":389,"props":45001,"children":45003},{"className":45002},[],[45004],{"type":259,"value":45005},"mssql",{"type":259,"value":45007}," one.",{"type":254,"tag":255,"props":45009,"children":45010},{},[45011,45013,45018],{"type":259,"value":45012},"This first solution of creating a new \"SQL Server Provider\" (whether it be from scratch, from boilerplate code, or from the ",{"type":254,"tag":389,"props":45014,"children":45016},{"className":45015},[],[45017],{"type":259,"value":45005},{"type":259,"value":45019}," Terraform provider) is interesting but could be time-consuming because there are some things to set up and some amount of code to write.",{"type":254,"tag":455,"props":45021,"children":45023},{"id":45022},"custom-c-code",[45024],{"type":259,"value":45025},"Custom C# code",{"type":254,"tag":255,"props":45027,"children":45028},{},[45029,45031,45043,45045,45052],{"type":259,"value":45030},"When you need to do something specific and there is no existing provider that can help you with it, you can just write the code to do it yourself without creating a complete provider. It's one of the reasons why I like Pulumi, even if you are doing Infrastructure as Code, at the end of the day you are just developing software so you can code what you need in the language you are familiar with. For instance, as I am developing in .NET, I can use the ",{"type":254,"tag":262,"props":45032,"children":45035},{"href":45033,"rel":45034},"https://docs.microsoft.com/en-us/sql/connect/ado-net/overview-sqlclient-driver",[266],[45036,45041],{"type":254,"tag":389,"props":45037,"children":45039},{"className":45038},[],[45040],{"type":259,"value":43886},{"type":259,"value":45042}," library",{"type":259,"value":45044}," (which is a data provider for Azure SQL Database) to connect and send commands to the database. And if I want to use ",{"type":254,"tag":262,"props":45046,"children":45049},{"href":45047,"rel":45048},"https://github.com/DapperLib/Dapper",[266],[45050],{"type":259,"value":45051},"Dapper",{"type":259,"value":45053}," on top of it because that's the library I am used to for querying a database I can. Hence writing the code that executes on the database the SQL command we have previously seen should not be very difficult.",{"type":254,"tag":255,"props":45055,"children":45056},{},[45057],{"type":259,"value":45058},"Now, even if we are using imperative language in Pulumi to write the infrastructure code it's still declarative infrastructure as code with a state. Therefore, we have to be careful about how and when this custom code should be executed.",{"type":254,"tag":255,"props":45060,"children":45061},{},[45062,45064,45069],{"type":259,"value":45063},"The easiest way is to use an ",{"type":254,"tag":389,"props":45065,"children":45067},{"className":45066},[],[45068],{"type":259,"value":21286},{"type":259,"value":45070}," method on an output of the database like this:",{"type":254,"tag":612,"props":45072,"children":45074},{"className":1423,"code":45073,"language":1425,"meta":205,"style":205},"database.Name.Apply(name =>\n{\n    /*** \n     * Indempotent code using Microsoft.Data.SqlClient library\n     * to execute the SQL command that assigns the correct roles\n     * to the Azure AD group we want to have access to the database.\n    ***/ \n    return true;\n});\n",[45075],{"type":254,"tag":389,"props":45076,"children":45077},{"__ignoreMap":205},[45078,45114,45121,45129,45137,45145,45153,45165,45180],{"type":254,"tag":622,"props":45079,"children":45080},{"class":624,"line":625},[45081,45086,45090,45094,45098,45102,45106,45110],{"type":254,"tag":622,"props":45082,"children":45083},{"style":1202},[45084],{"type":259,"value":45085},"database",{"type":254,"tag":622,"props":45087,"children":45088},{"style":629},[45089],{"type":259,"value":594},{"type":254,"tag":622,"props":45091,"children":45092},{"style":1202},[45093],{"type":259,"value":2254},{"type":254,"tag":622,"props":45095,"children":45096},{"style":629},[45097],{"type":259,"value":594},{"type":254,"tag":622,"props":45099,"children":45100},{"style":1735},[45101],{"type":259,"value":21286},{"type":254,"tag":622,"props":45103,"children":45104},{"style":629},[45105],{"type":259,"value":1668},{"type":254,"tag":622,"props":45107,"children":45108},{"style":714},[45109],{"type":259,"value":16394},{"type":254,"tag":622,"props":45111,"children":45112},{"style":629},[45113],{"type":259,"value":6569},{"type":254,"tag":622,"props":45115,"children":45116},{"class":624,"line":635},[45117],{"type":254,"tag":622,"props":45118,"children":45119},{"style":629},[45120],{"type":259,"value":632},{"type":254,"tag":622,"props":45122,"children":45123},{"class":624,"line":679},[45124],{"type":254,"tag":622,"props":45125,"children":45126},{"style":3195},[45127],{"type":259,"value":45128},"    /*** \n",{"type":254,"tag":622,"props":45130,"children":45131},{"class":624,"line":705},[45132],{"type":254,"tag":622,"props":45133,"children":45134},{"style":3195},[45135],{"type":259,"value":45136},"     * Indempotent code using Microsoft.Data.SqlClient library\n",{"type":254,"tag":622,"props":45138,"children":45139},{"class":624,"line":732},[45140],{"type":254,"tag":622,"props":45141,"children":45142},{"style":3195},[45143],{"type":259,"value":45144},"     * to execute the SQL command that assigns the correct roles\n",{"type":254,"tag":622,"props":45146,"children":45147},{"class":624,"line":759},[45148],{"type":254,"tag":622,"props":45149,"children":45150},{"style":3195},[45151],{"type":259,"value":45152},"     * to the Azure AD group we want to have access to the database.\n",{"type":254,"tag":622,"props":45154,"children":45155},{"class":624,"line":798},[45156,45161],{"type":254,"tag":622,"props":45157,"children":45158},{"style":3195},[45159],{"type":259,"value":45160},"    ***/",{"type":254,"tag":622,"props":45162,"children":45163},{"style":1202},[45164],{"type":259,"value":2314},{"type":254,"tag":622,"props":45166,"children":45167},{"class":624,"line":836},[45168,45172,45176],{"type":254,"tag":622,"props":45169,"children":45170},{"style":1196},[45171],{"type":259,"value":2358},{"type":254,"tag":622,"props":45173,"children":45174},{"style":7715},[45175],{"type":259,"value":7718},{"type":254,"tag":622,"props":45177,"children":45178},{"style":629},[45179],{"type":259,"value":2350},{"type":254,"tag":622,"props":45181,"children":45182},{"class":624,"line":862},[45183],{"type":254,"tag":622,"props":45184,"children":45185},{"style":629},[45186],{"type":259,"value":6661},{"type":254,"tag":255,"props":45188,"children":45189},{},[45190,45192,45197],{"type":259,"value":45191},"The code in the ",{"type":254,"tag":389,"props":45193,"children":45195},{"className":45194},[],[45196],{"type":259,"value":21286},{"type":259,"value":45198}," will execute on every run after the resource is created, that is why it needs to be idempotent. Having to make the code idempotent is a constraint that I would prefer to avoid but at least it gives us a simple way to execute the code that grants access to the database.",{"type":254,"tag":255,"props":45200,"children":45201},{},[45202,45204,45209,45211,45217,45219,45226],{"type":259,"value":45203},"Another way would be to use ",{"type":254,"tag":262,"props":45205,"children":45207},{"href":43446,"rel":45206},[266],[45208],{"type":259,"value":43450},{"type":259,"value":45210}," whose purpose is exactly that: do an infrastructure task that no existing provider can help you deliver. You can see some use cases of dynamic providers in ",{"type":254,"tag":262,"props":45212,"children":45214},{"href":43455,"rel":45213},[266],[45215],{"type":259,"value":45216},"this Pulumi article",{"type":259,"value":45218},". In our use case, we could imagine writing a dynamic resource provider for an Azure AD entity user in an Azure SQL Database.  We would have to implement the different CRUD operations to handle the different use cases properly (a user is added, a user is removed, user roles are updated, ...). Unfortunately, as you can see in ",{"type":254,"tag":262,"props":45220,"children":45223},{"href":45221,"rel":45222},"https://github.com/pulumi/pulumi/issues/3638",[266],[45224],{"type":259,"value":45225},"this GitHub issue",{"type":259,"value":45227},", .NET Dynamic Providers are not yet supported (only TypesScript, JavaScript and Python are for the moment). It's a shame because Dynamic Providers provide an easy and efficient way of supporting custom resource types.",{"type":254,"tag":455,"props":45229,"children":45231},{"id":45230},"command-provider-with-the-sqlcmd-utility",[45232,45234,45239],{"type":259,"value":45233},"Command provider with the ",{"type":254,"tag":389,"props":45235,"children":45237},{"className":45236},[],[45238],{"type":259,"value":44917},{"type":259,"value":44919},{"type":254,"tag":255,"props":45241,"children":45242},{},[45243,45249,45251,45262,45264,45269,45271,45278],{"type":254,"tag":262,"props":45244,"children":45246},{"href":44758,"rel":45245},[266],[45247],{"type":259,"value":45248},"The Microsoft tutorial",{"type":259,"value":45250},", that shows how to grant database permissions to an Azure AD entity, explains how the necessary SQL commands can be run using the ",{"type":254,"tag":262,"props":45252,"children":45255},{"href":45253,"rel":45254},"https://docs.microsoft.com/en-us/sql/tools/sqlcmd-utility",[266],[45256,45261],{"type":254,"tag":389,"props":45257,"children":45259},{"className":45258},[],[45260],{"type":259,"value":44917},{"type":259,"value":44919},{"type":259,"value":45263},". So instead of writing some C# code to do the same, an interesting idea would be to directly run the ",{"type":254,"tag":389,"props":45265,"children":45267},{"className":45266},[],[45268],{"type":259,"value":44917},{"type":259,"value":45270}," utility. And you know what? There is a Pulumi provider for executing commands and scripts: ",{"type":254,"tag":262,"props":45272,"children":45275},{"href":45273,"rel":45274},"https://www.pulumi.com/registry/packages/command/api-docs/",[266],[45276],{"type":259,"value":45277},"the Command Provider",{"type":259,"value":594},{"type":254,"tag":255,"props":45280,"children":45281},{},[45282],{"type":254,"tag":1161,"props":45283,"children":45287},{"alt":45284,"className":45285,"src":45286},"Pulumi Command Provider on GitHub.",[1165,1166],"/posts/images/sqldatabase_ad_pulumi_1.png",[],{"type":254,"tag":255,"props":45289,"children":45290},{},[45291,45293,45298,45300,45306,45308,45313],{"type":259,"value":45292},"Because it's a Pulumi provider, the ",{"type":254,"tag":389,"props":45294,"children":45296},{"className":45295},[],[45297],{"type":259,"value":44917},{"type":259,"value":45299}," command would be executed \"as part of the Pulumi resource model\" which means the scripts would be executed at the corresponding time of the resource life-cycle (the ",{"type":254,"tag":389,"props":45301,"children":45303},{"className":45302},[],[45304],{"type":259,"value":45305},"create",{"type":259,"value":45307}," script when the resource is created and so on). So it's very nice and not the same as executing the ",{"type":254,"tag":389,"props":45309,"children":45311},{"className":45310},[],[45312],{"type":259,"value":44917},{"type":259,"value":45314}," outside of a Pulumi program, without access to all the variables and where you would have to make your script idempotent. Moreover, the ability to execute commands remotely can bring interesting use cases, just not for our current concern here.",{"type":254,"tag":427,"props":45316,"children":45317},{"icon":567},[45318],{"type":254,"tag":255,"props":45319,"children":45320},{},[45321,45323,45328,45329,45335,45337,45343,45344,45350,45351,45357,45359,45366],{"type":259,"value":45322},"Pulumi Command Provider is currently in preview and only supports running scripts on ",{"type":254,"tag":389,"props":45324,"children":45326},{"className":45325},[],[45327],{"type":259,"value":45305},{"type":259,"value":10347},{"type":254,"tag":389,"props":45330,"children":45332},{"className":45331},[],[45333],{"type":259,"value":45334},"destroy",{"type":259,"value":45336}," operations (support for ",{"type":254,"tag":389,"props":45338,"children":45340},{"className":45339},[],[45341],{"type":259,"value":45342},"diff",{"type":259,"value":16280},{"type":254,"tag":389,"props":45345,"children":45347},{"className":45346},[],[45348],{"type":259,"value":45349},"update",{"type":259,"value":10347},{"type":254,"tag":389,"props":45352,"children":45354},{"className":45353},[],[45355],{"type":259,"value":45356},"read",{"type":259,"value":45358}," operations ",{"type":254,"tag":262,"props":45360,"children":45363},{"href":45361,"rel":45362},"https://github.com/pulumi/pulumi-command/issues/20",[266],[45364],{"type":259,"value":45365},"will probably be added later",{"type":259,"value":45367},"). It works fine but does not log details about the error when a script fails, which makes debugging difficult. That should not prevent you from using it but as with any components in preview, use it with caution knowing everything is not perfect yet.",{"type":254,"tag":282,"props":45369,"children":45371},{"id":45370},"implement-the-database-permissions-for-an-azure-ad-group",[45372],{"type":259,"value":45373},"Implement the database permissions for an Azure AD Group",{"type":254,"tag":255,"props":45375,"children":45376},{},[45377,45379,45384,45386,45391],{"type":259,"value":45378},"Of the 3 possible solutions let's take the 3rd one with the Command provider and the ",{"type":254,"tag":389,"props":45380,"children":45382},{"className":45381},[],[45383],{"type":259,"value":44917},{"type":259,"value":45385}," utility. It is probably not the \"best\" solution but I thought it would be simpler to use the ",{"type":254,"tag":389,"props":45387,"children":45389},{"className":45388},[],[45390],{"type":259,"value":44917},{"type":259,"value":45392}," utility than writing a complete provider or even custom C# code to do the same. Furthermore, it's the opportunity to test the Command provider which is fairly new.",{"type":254,"tag":455,"props":45394,"children":45396},{"id":45395},"allow-the-machine-running-the-pulumi-program-to-connect-to-the-sql-server",[45397],{"type":259,"value":45398},"Allow the machine running the Pulumi program to connect to the SQL Server",{"type":254,"tag":255,"props":45400,"children":45401},{},[45402,45404,45410],{"type":259,"value":45403},"To run a SQL command in the database, the machine that executes the Pulumi program needs to have its public IP authorized. To programmatically retrieve the public IP address from where the Pulumi program is running we can use ",{"type":254,"tag":389,"props":45405,"children":45407},{"className":45406},[],[45408],{"type":259,"value":45409},"ipify API",{"type":259,"value":45411},". It's a simple open source HTTP API that returns the public IP address of the caller.",{"type":254,"tag":612,"props":45413,"children":45415},{"className":1423,"code":45414,"language":1425,"meta":205,"style":205},"var publicIp = Output.Create(new HttpClient().GetStringAsync(\"https://api.ipify.org\"));\n",[45416],{"type":254,"tag":389,"props":45417,"children":45418},{"__ignoreMap":205},[45419],{"type":254,"tag":622,"props":45420,"children":45421},{"class":624,"line":625},[45422,45426,45431,45435,45439,45443,45448,45452,45456,45461,45466,45470,45474,45479,45483],{"type":254,"tag":622,"props":45423,"children":45424},{"style":714},[45425],{"type":259,"value":1710},{"type":254,"tag":622,"props":45427,"children":45428},{"style":714},[45429],{"type":259,"value":45430}," publicIp",{"type":254,"tag":622,"props":45432,"children":45433},{"style":629},[45434],{"type":259,"value":1619},{"type":254,"tag":622,"props":45436,"children":45437},{"style":1202},[45438],{"type":259,"value":22079},{"type":254,"tag":622,"props":45440,"children":45441},{"style":629},[45442],{"type":259,"value":594},{"type":254,"tag":622,"props":45444,"children":45445},{"style":1735},[45446],{"type":259,"value":45447},"Create",{"type":254,"tag":622,"props":45449,"children":45450},{"style":629},[45451],{"type":259,"value":6598},{"type":254,"tag":622,"props":45453,"children":45454},{"style":714},[45455],{"type":259,"value":1629},{"type":254,"tag":622,"props":45457,"children":45458},{"style":629},[45459],{"type":259,"value":45460},"().",{"type":254,"tag":622,"props":45462,"children":45463},{"style":1735},[45464],{"type":259,"value":45465},"GetStringAsync",{"type":254,"tag":622,"props":45467,"children":45468},{"style":629},[45469],{"type":259,"value":1668},{"type":254,"tag":622,"props":45471,"children":45472},{"style":629},[45473],{"type":259,"value":652},{"type":254,"tag":622,"props":45475,"children":45476},{"style":664},[45477],{"type":259,"value":45478},"https://api.ipify.org",{"type":254,"tag":622,"props":45480,"children":45481},{"style":629},[45482],{"type":259,"value":652},{"type":254,"tag":622,"props":45484,"children":45485},{"style":629},[45486],{"type":259,"value":6624},{"type":254,"tag":427,"props":45488,"children":45489},{"icon":429},[45490],{"type":254,"tag":255,"props":45491,"children":45492},{},[45493,45495,45500,45502,45507,45509,45516],{"type":259,"value":45494},"You can note here that we are just using standard C# code with an ",{"type":254,"tag":389,"props":45496,"children":45498},{"className":45497},[],[45499],{"type":259,"value":1565},{"type":259,"value":45501}," that makes a ",{"type":254,"tag":389,"props":45503,"children":45505},{"className":45504},[],[45506],{"type":259,"value":1199},{"type":259,"value":45508}," to the API and returns asynchronously a string. I like the fact that with Pulumi we can reuse our existing C# skills, and the libraries we are used to. If we were to do that in Terraform we would have to look in the documentation how to do HTTP calls, discover that there is an ",{"type":254,"tag":262,"props":45510,"children":45513},{"href":45511,"rel":45512},"https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http",[266],[45514],{"type":259,"value":45515},"http data source",{"type":259,"value":45517}," that can be used, understand how it works (to be honest it seems quite simple but still that is not natural) and use it.",{"type":254,"tag":255,"props":45519,"children":45520},{},[45521],{"type":259,"value":45522},"Now we can enable this public IP by creating a firewall rule in the SQL Server.",{"type":254,"tag":612,"props":45524,"children":45526},{"className":1423,"code":45525,"language":1425,"meta":205,"style":205},"var enableLocalMachine = new FirewallRule(\"AllowLocalMachine\", new FirewallRuleArgs\n{\n    ResourceGroupName = resourceGroup.Name,\n    ServerName = sqlServer.Name,\n    StartIpAddress = publicIp,\n    EndIpAddress = publicIp\n});\n",[45527],{"type":254,"tag":389,"props":45528,"children":45529},{"__ignoreMap":205},[45530,45585,45592,45619,45646,45666,45683],{"type":254,"tag":622,"props":45531,"children":45532},{"class":624,"line":625},[45533,45537,45542,45546,45550,45555,45559,45563,45568,45572,45576,45580],{"type":254,"tag":622,"props":45534,"children":45535},{"style":714},[45536],{"type":259,"value":1710},{"type":254,"tag":622,"props":45538,"children":45539},{"style":714},[45540],{"type":259,"value":45541}," enableLocalMachine",{"type":254,"tag":622,"props":45543,"children":45544},{"style":629},[45545],{"type":259,"value":1619},{"type":254,"tag":622,"props":45547,"children":45548},{"style":629},[45549],{"type":259,"value":1624},{"type":254,"tag":622,"props":45551,"children":45552},{"style":714},[45553],{"type":259,"value":45554}," FirewallRule",{"type":254,"tag":622,"props":45556,"children":45557},{"style":629},[45558],{"type":259,"value":1668},{"type":254,"tag":622,"props":45560,"children":45561},{"style":629},[45562],{"type":259,"value":652},{"type":254,"tag":622,"props":45564,"children":45565},{"style":664},[45566],{"type":259,"value":45567},"AllowLocalMachine",{"type":254,"tag":622,"props":45569,"children":45570},{"style":629},[45571],{"type":259,"value":652},{"type":254,"tag":622,"props":45573,"children":45574},{"style":629},[45575],{"type":259,"value":3149},{"type":254,"tag":622,"props":45577,"children":45578},{"style":629},[45579],{"type":259,"value":1624},{"type":254,"tag":622,"props":45581,"children":45582},{"style":714},[45583],{"type":259,"value":45584}," FirewallRuleArgs\n",{"type":254,"tag":622,"props":45586,"children":45587},{"class":624,"line":635},[45588],{"type":254,"tag":622,"props":45589,"children":45590},{"style":629},[45591],{"type":259,"value":632},{"type":254,"tag":622,"props":45593,"children":45594},{"class":624,"line":679},[45595,45599,45603,45607,45611,45615],{"type":254,"tag":622,"props":45596,"children":45597},{"style":1202},[45598],{"type":259,"value":34928},{"type":254,"tag":622,"props":45600,"children":45601},{"style":629},[45602],{"type":259,"value":1654},{"type":254,"tag":622,"props":45604,"children":45605},{"style":1202},[45606],{"type":259,"value":34689},{"type":254,"tag":622,"props":45608,"children":45609},{"style":629},[45610],{"type":259,"value":594},{"type":254,"tag":622,"props":45612,"children":45613},{"style":1202},[45614],{"type":259,"value":2254},{"type":254,"tag":622,"props":45616,"children":45617},{"style":629},[45618],{"type":259,"value":676},{"type":254,"tag":622,"props":45620,"children":45621},{"class":624,"line":705},[45622,45626,45630,45634,45638,45642],{"type":254,"tag":622,"props":45623,"children":45624},{"style":1202},[45625],{"type":259,"value":44655},{"type":254,"tag":622,"props":45627,"children":45628},{"style":629},[45629],{"type":259,"value":1654},{"type":254,"tag":622,"props":45631,"children":45632},{"style":1202},[45633],{"type":259,"value":44260},{"type":254,"tag":622,"props":45635,"children":45636},{"style":629},[45637],{"type":259,"value":594},{"type":254,"tag":622,"props":45639,"children":45640},{"style":1202},[45641],{"type":259,"value":2254},{"type":254,"tag":622,"props":45643,"children":45644},{"style":629},[45645],{"type":259,"value":676},{"type":254,"tag":622,"props":45647,"children":45648},{"class":624,"line":732},[45649,45654,45658,45662],{"type":254,"tag":622,"props":45650,"children":45651},{"style":1202},[45652],{"type":259,"value":45653},"    StartIpAddress ",{"type":254,"tag":622,"props":45655,"children":45656},{"style":629},[45657],{"type":259,"value":1654},{"type":254,"tag":622,"props":45659,"children":45660},{"style":1202},[45661],{"type":259,"value":45430},{"type":254,"tag":622,"props":45663,"children":45664},{"style":629},[45665],{"type":259,"value":676},{"type":254,"tag":622,"props":45667,"children":45668},{"class":624,"line":759},[45669,45674,45678],{"type":254,"tag":622,"props":45670,"children":45671},{"style":1202},[45672],{"type":259,"value":45673},"    EndIpAddress ",{"type":254,"tag":622,"props":45675,"children":45676},{"style":629},[45677],{"type":259,"value":1654},{"type":254,"tag":622,"props":45679,"children":45680},{"style":1202},[45681],{"type":259,"value":45682}," publicIp\n",{"type":254,"tag":622,"props":45684,"children":45685},{"class":624,"line":798},[45686],{"type":254,"tag":622,"props":45687,"children":45688},{"style":629},[45689],{"type":259,"value":6661},{"type":254,"tag":455,"props":45691,"children":45693},{"id":45692},"create-the-azure-ad-group-that-will-be-given-access-to-the-database",[45694],{"type":259,"value":45695},"Create the Azure AD group that will be given access to the database",{"type":254,"tag":255,"props":45697,"children":45698},{},[45699],{"type":259,"value":45700},"We said we wanted to grant SQL Database access to an Azure AD group that will contain in the future users and application managed identities that need access to the database. So let's create that:",{"type":254,"tag":612,"props":45702,"children":45704},{"className":1423,"code":45703,"language":1425,"meta":205,"style":205},"var sqlDatabaseAuthorizedGroup = new Group(\"SqlDbUsersGroup\", new GroupArgs\n{\n    DisplayName = \"SqlDbUsersGroup\",\n    SecurityEnabled = true,\n    Owners = new InputList\u003Cstring> { sqlAdAdmin.Id }\n});\n",[45705],{"type":254,"tag":389,"props":45706,"children":45707},{"__ignoreMap":205},[45708,45763,45770,45797,45817,45871],{"type":254,"tag":622,"props":45709,"children":45710},{"class":624,"line":625},[45711,45715,45720,45724,45728,45733,45737,45741,45746,45750,45754,45758],{"type":254,"tag":622,"props":45712,"children":45713},{"style":714},[45714],{"type":259,"value":1710},{"type":254,"tag":622,"props":45716,"children":45717},{"style":714},[45718],{"type":259,"value":45719}," sqlDatabaseAuthorizedGroup",{"type":254,"tag":622,"props":45721,"children":45722},{"style":629},[45723],{"type":259,"value":1619},{"type":254,"tag":622,"props":45725,"children":45726},{"style":629},[45727],{"type":259,"value":1624},{"type":254,"tag":622,"props":45729,"children":45730},{"style":714},[45731],{"type":259,"value":45732}," Group",{"type":254,"tag":622,"props":45734,"children":45735},{"style":629},[45736],{"type":259,"value":1668},{"type":254,"tag":622,"props":45738,"children":45739},{"style":629},[45740],{"type":259,"value":652},{"type":254,"tag":622,"props":45742,"children":45743},{"style":664},[45744],{"type":259,"value":45745},"SqlDbUsersGroup",{"type":254,"tag":622,"props":45747,"children":45748},{"style":629},[45749],{"type":259,"value":652},{"type":254,"tag":622,"props":45751,"children":45752},{"style":629},[45753],{"type":259,"value":3149},{"type":254,"tag":622,"props":45755,"children":45756},{"style":629},[45757],{"type":259,"value":1624},{"type":254,"tag":622,"props":45759,"children":45760},{"style":714},[45761],{"type":259,"value":45762}," GroupArgs\n",{"type":254,"tag":622,"props":45764,"children":45765},{"class":624,"line":635},[45766],{"type":254,"tag":622,"props":45767,"children":45768},{"style":629},[45769],{"type":259,"value":632},{"type":254,"tag":622,"props":45771,"children":45772},{"class":624,"line":679},[45773,45777,45781,45785,45789,45793],{"type":254,"tag":622,"props":45774,"children":45775},{"style":1202},[45776],{"type":259,"value":21751},{"type":254,"tag":622,"props":45778,"children":45779},{"style":629},[45780],{"type":259,"value":1654},{"type":254,"tag":622,"props":45782,"children":45783},{"style":629},[45784],{"type":259,"value":661},{"type":254,"tag":622,"props":45786,"children":45787},{"style":664},[45788],{"type":259,"value":45745},{"type":254,"tag":622,"props":45790,"children":45791},{"style":629},[45792],{"type":259,"value":652},{"type":254,"tag":622,"props":45794,"children":45795},{"style":629},[45796],{"type":259,"value":676},{"type":254,"tag":622,"props":45798,"children":45799},{"class":624,"line":705},[45800,45805,45809,45813],{"type":254,"tag":622,"props":45801,"children":45802},{"style":1202},[45803],{"type":259,"value":45804},"    SecurityEnabled ",{"type":254,"tag":622,"props":45806,"children":45807},{"style":629},[45808],{"type":259,"value":1654},{"type":254,"tag":622,"props":45810,"children":45811},{"style":7715},[45812],{"type":259,"value":7718},{"type":254,"tag":622,"props":45814,"children":45815},{"style":629},[45816],{"type":259,"value":676},{"type":254,"tag":622,"props":45818,"children":45819},{"class":624,"line":732},[45820,45825,45829,45833,45838,45842,45846,45850,45854,45858,45862,45867],{"type":254,"tag":622,"props":45821,"children":45822},{"style":1202},[45823],{"type":259,"value":45824},"    Owners ",{"type":254,"tag":622,"props":45826,"children":45827},{"style":629},[45828],{"type":259,"value":1654},{"type":254,"tag":622,"props":45830,"children":45831},{"style":629},[45832],{"type":259,"value":1624},{"type":254,"tag":622,"props":45834,"children":45835},{"style":714},[45836],{"type":259,"value":45837}," InputList",{"type":254,"tag":622,"props":45839,"children":45840},{"style":629},[45841],{"type":259,"value":1743},{"type":254,"tag":622,"props":45843,"children":45844},{"style":629},[45845],{"type":259,"value":2851},{"type":254,"tag":622,"props":45847,"children":45848},{"style":629},[45849],{"type":259,"value":6813},{"type":254,"tag":622,"props":45851,"children":45852},{"style":629},[45853],{"type":259,"value":1990},{"type":254,"tag":622,"props":45855,"children":45856},{"style":1202},[45857],{"type":259,"value":44079},{"type":254,"tag":622,"props":45859,"children":45860},{"style":629},[45861],{"type":259,"value":594},{"type":254,"tag":622,"props":45863,"children":45864},{"style":1202},[45865],{"type":259,"value":45866},"Id ",{"type":254,"tag":622,"props":45868,"children":45869},{"style":629},[45870],{"type":259,"value":1111},{"type":254,"tag":622,"props":45872,"children":45873},{"class":624,"line":759},[45874],{"type":254,"tag":622,"props":45875,"children":45876},{"style":629},[45877],{"type":259,"value":6661},{"type":254,"tag":255,"props":45879,"children":45880},{},[45881],{"type":259,"value":45882},"We set the Azure SQL Server admin as the owner of the group. This way, the admin of the database can add Azure AD users to the group and they directly have the permissions configured for this group. I like authorizing an Azure AD group instead of each Azure AD user because:",{"type":254,"tag":332,"props":45884,"children":45885},{},[45886,45891,45896],{"type":254,"tag":336,"props":45887,"children":45888},{},[45889],{"type":259,"value":45890},"it is easier to manage a group than individual users (adding a user to a group is less work than using SQL commands to assign the correct role for each user)",{"type":254,"tag":336,"props":45892,"children":45893},{},[45894],{"type":259,"value":45895},"you don't lose granularity of access control (you can always create several groups with different permissions if you need to)",{"type":254,"tag":336,"props":45897,"children":45898},{},[45899],{"type":259,"value":45900},"you can ensure that your application runs with the same permissions locally (the code you debug uses your user account identity) and on Azure (the code uses the managed identity of the App Service where it is hosted) by putting users and managed identities in the same group",{"type":254,"tag":455,"props":45902,"children":45904},{"id":45903},"assign-the-roles-to-the-azure-ad-group-using-the-command-provider",[45905],{"type":259,"value":45906},"Assign the roles to the Azure AD group using the Command provider",{"type":254,"tag":255,"props":45908,"children":45909},{},[45910,45912,45917,45919,45924],{"type":259,"value":45911},"As we already talked about, we can specify a script to run on the ",{"type":254,"tag":389,"props":45913,"children":45915},{"className":45914},[],[45916],{"type":259,"value":45305},{"type":259,"value":45918}," operation and another on the ",{"type":254,"tag":389,"props":45920,"children":45922},{"className":45921},[],[45923],{"type":259,"value":45334},{"type":259,"value":45925}," operations. To keep things simple for this sample, we will only handle the creation scenario where we will add our Azure AD group as a user of the database and give it the expected roles. We already showed the SQL Command to execute, with our new group name it becomes:",{"type":254,"tag":612,"props":45927,"children":45929},{"className":44765,"code":45928,"language":44767,"meta":205,"style":205},"CREATE USER {sqlDatabaseAuthorizedGroup.DisplayName} FROM EXTERNAL PROVIDER;\nALTER ROLE db_datareader ADD MEMBER {sqlDatabaseAuthorizedGroup.DisplayName};\nALTER ROLE db_datawriter ADD MEMBER {sqlDatabaseAuthorizedGroup.DisplayName};\nGO\n",[45930],{"type":254,"tag":389,"props":45931,"children":45932},{"__ignoreMap":205},[45933,45961,45985,46008],{"type":254,"tag":622,"props":45934,"children":45935},{"class":624,"line":625},[45936,45940,45945,45949,45953,45957],{"type":254,"tag":622,"props":45937,"children":45938},{"style":741},[45939],{"type":259,"value":44779},{"type":254,"tag":622,"props":45941,"children":45942},{"style":1202},[45943],{"type":259,"value":45944}," USER {sqlDatabaseAuthorizedGroup.DisplayName} ",{"type":254,"tag":622,"props":45946,"children":45947},{"style":741},[45948],{"type":259,"value":44789},{"type":254,"tag":622,"props":45950,"children":45951},{"style":741},[45952],{"type":259,"value":44794},{"type":254,"tag":622,"props":45954,"children":45955},{"style":741},[45956],{"type":259,"value":44799},{"type":254,"tag":622,"props":45958,"children":45959},{"style":1202},[45960],{"type":259,"value":2350},{"type":254,"tag":622,"props":45962,"children":45963},{"class":624,"line":635},[45964,45968,45972,45976,45980],{"type":254,"tag":622,"props":45965,"children":45966},{"style":741},[45967],{"type":259,"value":44811},{"type":254,"tag":622,"props":45969,"children":45970},{"style":741},[45971],{"type":259,"value":44816},{"type":254,"tag":622,"props":45973,"children":45974},{"style":1202},[45975],{"type":259,"value":44821},{"type":254,"tag":622,"props":45977,"children":45978},{"style":741},[45979],{"type":259,"value":44826},{"type":254,"tag":622,"props":45981,"children":45982},{"style":1202},[45983],{"type":259,"value":45984}," MEMBER {sqlDatabaseAuthorizedGroup.DisplayName};\n",{"type":254,"tag":622,"props":45986,"children":45987},{"class":624,"line":679},[45988,45992,45996,46000,46004],{"type":254,"tag":622,"props":45989,"children":45990},{"style":741},[45991],{"type":259,"value":44811},{"type":254,"tag":622,"props":45993,"children":45994},{"style":741},[45995],{"type":259,"value":44816},{"type":254,"tag":622,"props":45997,"children":45998},{"style":1202},[45999],{"type":259,"value":44847},{"type":254,"tag":622,"props":46001,"children":46002},{"style":741},[46003],{"type":259,"value":44826},{"type":254,"tag":622,"props":46005,"children":46006},{"style":1202},[46007],{"type":259,"value":45984},{"type":254,"tag":622,"props":46009,"children":46010},{"class":624,"line":705},[46011],{"type":254,"tag":622,"props":46012,"children":46013},{"style":741},[46014],{"type":259,"value":44863},{"type":254,"tag":255,"props":46016,"children":46017},{},[46018,46019,46024],{"type":259,"value":10970},{"type":254,"tag":389,"props":46020,"children":46022},{"className":46021},[],[46023],{"type":259,"value":44917},{"type":259,"value":46025}," utility can be used like this to send a command on the database:",{"type":254,"tag":612,"props":46027,"children":46029},{"className":10656,"code":46028,"language":10658,"meta":205,"style":205},"sqlcmd -S {sqlServer.Name}.database.windows.net -d {database.Name} -U {sqlAdAdmin.UserPrincipalName} -P {sqlAdAdmin.Password} -G -l 30 -Q '___SQL Command___'\n",[46030],{"type":254,"tag":389,"props":46031,"children":46032},{"__ignoreMap":205},[46033],{"type":254,"tag":622,"props":46034,"children":46035},{"class":624,"line":625},[46036,46041,46045,46050,46054,46059,46063,46068,46072,46076,46080,46085,46089,46093,46098,46102,46107,46111,46115,46120,46124,46129,46133,46137,46142,46146,46151,46156,46160,46165,46170,46175],{"type":254,"tag":622,"props":46037,"children":46038},{"style":1202},[46039],{"type":259,"value":46040},"sqlcmd ",{"type":254,"tag":622,"props":46042,"children":46043},{"style":629},[46044],{"type":259,"value":17065},{"type":254,"tag":622,"props":46046,"children":46047},{"style":1202},[46048],{"type":259,"value":46049},"S ",{"type":254,"tag":622,"props":46051,"children":46052},{"style":629},[46053],{"type":259,"value":3234},{"type":254,"tag":622,"props":46055,"children":46056},{"style":1202},[46057],{"type":259,"value":46058},"sqlServer.Name",{"type":254,"tag":622,"props":46060,"children":46061},{"style":629},[46062],{"type":259,"value":2421},{"type":254,"tag":622,"props":46064,"children":46065},{"style":1202},[46066],{"type":259,"value":46067},".database.windows.net ",{"type":254,"tag":622,"props":46069,"children":46070},{"style":629},[46071],{"type":259,"value":17065},{"type":254,"tag":622,"props":46073,"children":46074},{"style":1202},[46075],{"type":259,"value":24183},{"type":254,"tag":622,"props":46077,"children":46078},{"style":629},[46079],{"type":259,"value":3234},{"type":254,"tag":622,"props":46081,"children":46082},{"style":1202},[46083],{"type":259,"value":46084},"database.Name",{"type":254,"tag":622,"props":46086,"children":46087},{"style":629},[46088],{"type":259,"value":2421},{"type":254,"tag":622,"props":46090,"children":46091},{"style":629},[46092],{"type":259,"value":10733},{"type":254,"tag":622,"props":46094,"children":46095},{"style":1202},[46096],{"type":259,"value":46097},"U ",{"type":254,"tag":622,"props":46099,"children":46100},{"style":629},[46101],{"type":259,"value":3234},{"type":254,"tag":622,"props":46103,"children":46104},{"style":1202},[46105],{"type":259,"value":46106},"sqlAdAdmin.UserPrincipalName",{"type":254,"tag":622,"props":46108,"children":46109},{"style":629},[46110],{"type":259,"value":2421},{"type":254,"tag":622,"props":46112,"children":46113},{"style":629},[46114],{"type":259,"value":10733},{"type":254,"tag":622,"props":46116,"children":46117},{"style":1202},[46118],{"type":259,"value":46119},"P ",{"type":254,"tag":622,"props":46121,"children":46122},{"style":629},[46123],{"type":259,"value":3234},{"type":254,"tag":622,"props":46125,"children":46126},{"style":1202},[46127],{"type":259,"value":46128},"sqlAdAdmin.Password",{"type":254,"tag":622,"props":46130,"children":46131},{"style":629},[46132],{"type":259,"value":2421},{"type":254,"tag":622,"props":46134,"children":46135},{"style":629},[46136],{"type":259,"value":10733},{"type":254,"tag":622,"props":46138,"children":46139},{"style":1202},[46140],{"type":259,"value":46141},"G ",{"type":254,"tag":622,"props":46143,"children":46144},{"style":629},[46145],{"type":259,"value":17065},{"type":254,"tag":622,"props":46147,"children":46148},{"style":1202},[46149],{"type":259,"value":46150},"l ",{"type":254,"tag":622,"props":46152,"children":46153},{"style":741},[46154],{"type":259,"value":46155},"30",{"type":254,"tag":622,"props":46157,"children":46158},{"style":629},[46159],{"type":259,"value":10733},{"type":254,"tag":622,"props":46161,"children":46162},{"style":1202},[46163],{"type":259,"value":46164},"Q ",{"type":254,"tag":622,"props":46166,"children":46167},{"style":629},[46168],{"type":259,"value":46169},"'",{"type":254,"tag":622,"props":46171,"children":46172},{"style":664},[46173],{"type":259,"value":46174},"___SQL Command___",{"type":254,"tag":622,"props":46176,"children":46177},{"style":629},[46178],{"type":259,"value":16566},{"type":254,"tag":255,"props":46180,"children":46181},{},[46182,46183,46189,46191,46196],{"type":259,"value":1132},{"type":254,"tag":262,"props":46184,"children":46187},{"href":46185,"rel":46186},"https://docs.microsoft.com/en-us/sql/tools/sqlcmd-utility?view=sql-server-ver15#sqlcmd-commands",[266],[46188],{"type":259,"value":6340},{"type":259,"value":46190}," to learn more about how to use ",{"type":254,"tag":389,"props":46192,"children":46194},{"className":46193},[],[46195],{"type":259,"value":44917},{"type":259,"value":46197}," but that is quite simple: we are just specifying to send a command line query on our database using Azure Active Directory to authenticate.",{"type":254,"tag":255,"props":46199,"children":46200},{},[46201],{"type":259,"value":46202},"If we use all that with our Command provider, we get the following C# code.",{"type":254,"tag":612,"props":46204,"children":46206},{"className":1423,"code":46205,"language":1425,"meta":205,"style":205},"var authorizeAdGroup = new Command(\"AuthorizeAdGroup\", new CommandArgs\n{\n    Create = Output.Format($\"sqlcmd -S {sqlServer.Name}.database.windows.net -d {database.Name} -U {sqlAdAdmin.UserPrincipalName} -P {sqlAdAdmin.Password} -G -l 30 -Q 'CREATE USER {sqlDatabaseAuthorizedGroup.DisplayName} FROM EXTERNAL PROVIDER; ALTER ROLE db_datareader ADD MEMBER {sqlDatabaseAuthorizedGroup.DisplayName}; ALTER ROLE db_datawriter ADD MEMBER {sqlDatabaseAuthorizedGroup.DisplayName};'\"),\n    Interpreter = new InputList\u003Cstring>\n    {\n        \"pwsh\",\n        \"-c\"\n    }\n});\n",[46207],{"type":254,"tag":389,"props":46208,"children":46209},{"__ignoreMap":205},[46210,46265,46272,46494,46526,46533,46553,46569,46576],{"type":254,"tag":622,"props":46211,"children":46212},{"class":624,"line":625},[46213,46217,46222,46226,46230,46235,46239,46243,46248,46252,46256,46260],{"type":254,"tag":622,"props":46214,"children":46215},{"style":714},[46216],{"type":259,"value":1710},{"type":254,"tag":622,"props":46218,"children":46219},{"style":714},[46220],{"type":259,"value":46221}," authorizeAdGroup",{"type":254,"tag":622,"props":46223,"children":46224},{"style":629},[46225],{"type":259,"value":1619},{"type":254,"tag":622,"props":46227,"children":46228},{"style":629},[46229],{"type":259,"value":1624},{"type":254,"tag":622,"props":46231,"children":46232},{"style":714},[46233],{"type":259,"value":46234}," Command",{"type":254,"tag":622,"props":46236,"children":46237},{"style":629},[46238],{"type":259,"value":1668},{"type":254,"tag":622,"props":46240,"children":46241},{"style":629},[46242],{"type":259,"value":652},{"type":254,"tag":622,"props":46244,"children":46245},{"style":664},[46246],{"type":259,"value":46247},"AuthorizeAdGroup",{"type":254,"tag":622,"props":46249,"children":46250},{"style":629},[46251],{"type":259,"value":652},{"type":254,"tag":622,"props":46253,"children":46254},{"style":629},[46255],{"type":259,"value":3149},{"type":254,"tag":622,"props":46257,"children":46258},{"style":629},[46259],{"type":259,"value":1624},{"type":254,"tag":622,"props":46261,"children":46262},{"style":714},[46263],{"type":259,"value":46264}," CommandArgs\n",{"type":254,"tag":622,"props":46266,"children":46267},{"class":624,"line":635},[46268],{"type":254,"tag":622,"props":46269,"children":46270},{"style":629},[46271],{"type":259,"value":632},{"type":254,"tag":622,"props":46273,"children":46274},{"class":624,"line":679},[46275,46280,46284,46288,46292,46296,46300,46304,46309,46313,46318,46322,46326,46330,46335,46339,46343,46347,46351,46355,46360,46364,46368,46372,46376,46380,46385,46389,46393,46397,46401,46405,46410,46414,46419,46423,46427,46431,46436,46440,46444,46448,46452,46456,46461,46465,46469,46473,46477,46481,46486,46490],{"type":254,"tag":622,"props":46276,"children":46277},{"style":1202},[46278],{"type":259,"value":46279},"    Create ",{"type":254,"tag":622,"props":46281,"children":46282},{"style":629},[46283],{"type":259,"value":1654},{"type":254,"tag":622,"props":46285,"children":46286},{"style":1202},[46287],{"type":259,"value":22079},{"type":254,"tag":622,"props":46289,"children":46290},{"style":629},[46291],{"type":259,"value":594},{"type":254,"tag":622,"props":46293,"children":46294},{"style":1735},[46295],{"type":259,"value":22088},{"type":254,"tag":622,"props":46297,"children":46298},{"style":629},[46299],{"type":259,"value":1668},{"type":254,"tag":622,"props":46301,"children":46302},{"style":629},[46303],{"type":259,"value":9607},{"type":254,"tag":622,"props":46305,"children":46306},{"style":664},[46307],{"type":259,"value":46308},"sqlcmd -S ",{"type":254,"tag":622,"props":46310,"children":46311},{"style":629},[46312],{"type":259,"value":3234},{"type":254,"tag":622,"props":46314,"children":46315},{"style":1202},[46316],{"type":259,"value":46317},"sqlServer",{"type":254,"tag":622,"props":46319,"children":46320},{"style":629},[46321],{"type":259,"value":594},{"type":254,"tag":622,"props":46323,"children":46324},{"style":1202},[46325],{"type":259,"value":2254},{"type":254,"tag":622,"props":46327,"children":46328},{"style":629},[46329],{"type":259,"value":2421},{"type":254,"tag":622,"props":46331,"children":46332},{"style":664},[46333],{"type":259,"value":46334},".database.windows.net -d ",{"type":254,"tag":622,"props":46336,"children":46337},{"style":629},[46338],{"type":259,"value":3234},{"type":254,"tag":622,"props":46340,"children":46341},{"style":1202},[46342],{"type":259,"value":45085},{"type":254,"tag":622,"props":46344,"children":46345},{"style":629},[46346],{"type":259,"value":594},{"type":254,"tag":622,"props":46348,"children":46349},{"style":1202},[46350],{"type":259,"value":2254},{"type":254,"tag":622,"props":46352,"children":46353},{"style":629},[46354],{"type":259,"value":2421},{"type":254,"tag":622,"props":46356,"children":46357},{"style":664},[46358],{"type":259,"value":46359}," -U ",{"type":254,"tag":622,"props":46361,"children":46362},{"style":629},[46363],{"type":259,"value":3234},{"type":254,"tag":622,"props":46365,"children":46366},{"style":1202},[46367],{"type":259,"value":44002},{"type":254,"tag":622,"props":46369,"children":46370},{"style":629},[46371],{"type":259,"value":594},{"type":254,"tag":622,"props":46373,"children":46374},{"style":1202},[46375],{"type":259,"value":44414},{"type":254,"tag":622,"props":46377,"children":46378},{"style":629},[46379],{"type":259,"value":2421},{"type":254,"tag":622,"props":46381,"children":46382},{"style":664},[46383],{"type":259,"value":46384}," -P ",{"type":254,"tag":622,"props":46386,"children":46387},{"style":629},[46388],{"type":259,"value":3234},{"type":254,"tag":622,"props":46390,"children":46391},{"style":1202},[46392],{"type":259,"value":44002},{"type":254,"tag":622,"props":46394,"children":46395},{"style":629},[46396],{"type":259,"value":594},{"type":254,"tag":622,"props":46398,"children":46399},{"style":1202},[46400],{"type":259,"value":40628},{"type":254,"tag":622,"props":46402,"children":46403},{"style":629},[46404],{"type":259,"value":2421},{"type":254,"tag":622,"props":46406,"children":46407},{"style":664},[46408],{"type":259,"value":46409}," -G -l 30 -Q 'CREATE USER ",{"type":254,"tag":622,"props":46411,"children":46412},{"style":629},[46413],{"type":259,"value":3234},{"type":254,"tag":622,"props":46415,"children":46416},{"style":1202},[46417],{"type":259,"value":46418},"sqlDatabaseAuthorizedGroup",{"type":254,"tag":622,"props":46420,"children":46421},{"style":629},[46422],{"type":259,"value":594},{"type":254,"tag":622,"props":46424,"children":46425},{"style":1202},[46426],{"type":259,"value":21603},{"type":254,"tag":622,"props":46428,"children":46429},{"style":629},[46430],{"type":259,"value":2421},{"type":254,"tag":622,"props":46432,"children":46433},{"style":664},[46434],{"type":259,"value":46435}," FROM EXTERNAL PROVIDER; ALTER ROLE db_datareader ADD MEMBER ",{"type":254,"tag":622,"props":46437,"children":46438},{"style":629},[46439],{"type":259,"value":3234},{"type":254,"tag":622,"props":46441,"children":46442},{"style":1202},[46443],{"type":259,"value":46418},{"type":254,"tag":622,"props":46445,"children":46446},{"style":629},[46447],{"type":259,"value":594},{"type":254,"tag":622,"props":46449,"children":46450},{"style":1202},[46451],{"type":259,"value":21603},{"type":254,"tag":622,"props":46453,"children":46454},{"style":629},[46455],{"type":259,"value":2421},{"type":254,"tag":622,"props":46457,"children":46458},{"style":664},[46459],{"type":259,"value":46460},"; ALTER ROLE db_datawriter ADD MEMBER ",{"type":254,"tag":622,"props":46462,"children":46463},{"style":629},[46464],{"type":259,"value":3234},{"type":254,"tag":622,"props":46466,"children":46467},{"style":1202},[46468],{"type":259,"value":46418},{"type":254,"tag":622,"props":46470,"children":46471},{"style":629},[46472],{"type":259,"value":594},{"type":254,"tag":622,"props":46474,"children":46475},{"style":1202},[46476],{"type":259,"value":21603},{"type":254,"tag":622,"props":46478,"children":46479},{"style":629},[46480],{"type":259,"value":2421},{"type":254,"tag":622,"props":46482,"children":46483},{"style":664},[46484],{"type":259,"value":46485},";'",{"type":254,"tag":622,"props":46487,"children":46488},{"style":629},[46489],{"type":259,"value":652},{"type":254,"tag":622,"props":46491,"children":46492},{"style":629},[46493],{"type":259,"value":12556},{"type":254,"tag":622,"props":46495,"children":46496},{"class":624,"line":705},[46497,46502,46506,46510,46514,46518,46522],{"type":254,"tag":622,"props":46498,"children":46499},{"style":1202},[46500],{"type":259,"value":46501},"    Interpreter ",{"type":254,"tag":622,"props":46503,"children":46504},{"style":629},[46505],{"type":259,"value":1654},{"type":254,"tag":622,"props":46507,"children":46508},{"style":629},[46509],{"type":259,"value":1624},{"type":254,"tag":622,"props":46511,"children":46512},{"style":714},[46513],{"type":259,"value":45837},{"type":254,"tag":622,"props":46515,"children":46516},{"style":629},[46517],{"type":259,"value":1743},{"type":254,"tag":622,"props":46519,"children":46520},{"style":629},[46521],{"type":259,"value":2851},{"type":254,"tag":622,"props":46523,"children":46524},{"style":629},[46525],{"type":259,"value":15654},{"type":254,"tag":622,"props":46527,"children":46528},{"class":624,"line":732},[46529],{"type":254,"tag":622,"props":46530,"children":46531},{"style":629},[46532],{"type":259,"value":3574},{"type":254,"tag":622,"props":46534,"children":46535},{"class":624,"line":759},[46536,46540,46545,46549],{"type":254,"tag":622,"props":46537,"children":46538},{"style":629},[46539],{"type":259,"value":765},{"type":254,"tag":622,"props":46541,"children":46542},{"style":664},[46543],{"type":259,"value":46544},"pwsh",{"type":254,"tag":622,"props":46546,"children":46547},{"style":629},[46548],{"type":259,"value":652},{"type":254,"tag":622,"props":46550,"children":46551},{"style":629},[46552],{"type":259,"value":676},{"type":254,"tag":622,"props":46554,"children":46555},{"class":624,"line":798},[46556,46560,46565],{"type":254,"tag":622,"props":46557,"children":46558},{"style":629},[46559],{"type":259,"value":765},{"type":254,"tag":622,"props":46561,"children":46562},{"style":664},[46563],{"type":259,"value":46564},"-c",{"type":254,"tag":622,"props":46566,"children":46567},{"style":629},[46568],{"type":259,"value":9909},{"type":254,"tag":622,"props":46570,"children":46571},{"class":624,"line":836},[46572],{"type":254,"tag":622,"props":46573,"children":46574},{"style":629},[46575],{"type":259,"value":1093},{"type":254,"tag":622,"props":46577,"children":46578},{"class":624,"line":862},[46579],{"type":254,"tag":622,"props":46580,"children":46581},{"style":629},[46582],{"type":259,"value":6661},{"type":254,"tag":255,"props":46584,"children":46585},{},[46586],{"type":259,"value":46587},"As you can see, we can specify a specific interpreter to use (PowerShell here).",{"type":254,"tag":427,"props":46589,"children":46590},{"icon":429},[46591],{"type":254,"tag":255,"props":46592,"children":46593},{},[46594,46596,46603,46605,46611,46613,46618],{"type":259,"value":46595},"Don't do like me and forget that our variables are ",{"type":254,"tag":262,"props":46597,"children":46600},{"href":46598,"rel":46599},"https://www.pulumi.com/docs/intro/concepts/inputs-outputs/#inputs-and-outputs",[266],[46601],{"type":259,"value":46602},"outputs",{"type":259,"value":46604}," (only fully known when the infrastructure resource is completely provisioned). Because of that it is necessary to use the ",{"type":254,"tag":389,"props":46606,"children":46608},{"className":46607},[],[46609],{"type":259,"value":46610},"Output.Format",{"type":259,"value":46612}," method for string interpolation instead of using the C# operator ",{"type":254,"tag":389,"props":46614,"children":46616},{"className":46615},[],[46617],{"type":259,"value":16922},{"type":259,"value":46619},". Thanks to the community on Slack for helping me on that one because with the Command provider not logging the errors details I had a hard time on this.",{"type":254,"tag":455,"props":46621,"children":46623},{"id":46622},"results",[46624],{"type":259,"value":46625},"Results",{"type":254,"tag":255,"props":46627,"children":46628},{},[46629,46631,46636,46637,46642],{"type":259,"value":46630},"And that's it! We now have created the Azure AD group as an external user in the database and assigned it the ",{"type":254,"tag":389,"props":46632,"children":46634},{"className":46633},[],[46635],{"type":259,"value":44874},{"type":259,"value":10347},{"type":254,"tag":389,"props":46638,"children":46640},{"className":46639},[],[46641],{"type":259,"value":44881},{"type":259,"value":46643}," roles.\nHere is what it looks like in Azure Data Studio:",{"type":254,"tag":255,"props":46645,"children":46646},{},[46647],{"type":254,"tag":1161,"props":46648,"children":46652},{"alt":46649,"className":46650,"src":46651},"SQL query listing database members and roles in Azure Data Studio.",[1165,1166],"/posts/images/sqldatabase_ad_azuredatastudio.png",[],{"type":254,"tag":282,"props":46654,"children":46655},{"id":37059},[46656],{"type":259,"value":37062},{"type":254,"tag":255,"props":46658,"children":46659},{},[46660,46662,46668],{"type":259,"value":46661},"This article is a bit long because I explain all the steps and possibilities but the complete code is not very big or complex. You can find it in this ",{"type":254,"tag":262,"props":46663,"children":46666},{"href":46664,"rel":46665},"https://github.com/TechWatching/SqlDatabaseWithAzureAd",[266],[46667],{"type":259,"value":44981},{"type":259,"value":594},{"type":254,"tag":255,"props":46670,"children":46671},{},[46672],{"type":259,"value":46673},"I did not see that many articles on the web that talk about using Azure Active Directory authentication for an Azure SQL Database, and even less that showed how to properly configure it using Infrastructure as Code. Yet, I think it's an important thing to do to properly secure your Azure SQL database. So I hope you enjoyed it and learn something. Whether you use Azure CLI, Bicep, ARM Templates, Terraform, or Pulumi, don't hesitate to use Azure AD authentication on your Azure SQL Database, for me that is the right and secure way to go.",{"type":254,"tag":255,"props":46675,"children":46676},{},[46677],{"type":259,"value":46678},"As you have seen in this article, even when there is no provider for your custom resource or task, there are always several solutions to do what you want with Pulumi. Some are more elegant, some are more complex than others but you will always find a way and you will not be limited by the platform.",{"type":254,"tag":255,"props":46680,"children":46681},{},[46682],{"type":259,"value":46683},"A big thank you to the Pulumi community that gave me some insights on how to configure Azure AD authentication on a database properly using Pulumi. Without the help of some people in the Pulumi Slack or the GitHub Issues/Discussions I would not have been able to write this article. Indeed some ideas and solutions are directly inspired by people's answers to my questions. This article is my way of contributing back and helping others that would have similar questions.",{"type":254,"tag":1260,"props":46685,"children":46686},{},[46687],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":46689},[46690,46691,46692,46693,46699,46705],{"id":43863,"depth":635,"text":43866},{"id":43917,"depth":635,"text":43920},{"id":44233,"depth":635,"text":44236},{"id":44742,"depth":635,"text":44745,"children":46694},[46695,46696,46697],{"id":44927,"depth":679,"text":44930},{"id":45022,"depth":679,"text":45025},{"id":45230,"depth":679,"text":46698},"Command provider with the sqlcmd utility",{"id":45370,"depth":635,"text":45373,"children":46700},[46701,46702,46703,46704],{"id":45395,"depth":679,"text":45398},{"id":45692,"depth":679,"text":45695},{"id":45903,"depth":679,"text":45906},{"id":46622,"depth":679,"text":46625},{"id":37059,"depth":635,"text":37062},"content:1.posts:35.sqldatabase-active-directory-authent.md","1.posts/35.sqldatabase-active-directory-authent.md",{"_path":109,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":108,"description":46709,"lead":46710,"date":46711,"image":46712,"badge":46713,"tags":46714,"body":46717,"_type":1275,"_id":47010,"_source":231,"_file":47011,"_extension":1278},"This week we talk about code analysis in .NET, cron expressions with crontab guru, diagrams in Azure DevOps wikis, and sending HTTP requests in VS Code.","Tooling around .NET, Azure DevOps and VS Code.","2022-02-04T00:00:00.000Z",{"src":9258},{"label":9260},[9262,1288,46715,9264,46716,37215,245],"Code analysis","Diagram",{"type":251,"children":46718,"toc":47004},[46719,46723,46729,46775,46787,46796,46809,46818,46830,46839,46852,46858,46871,46880,46893,46899,46904,46947,46952,46961,46967,46978,46987,47000],{"type":254,"tag":255,"props":46720,"children":46721},{},[46722],{"type":259,"value":46709},{"type":254,"tag":282,"props":46724,"children":46726},{"id":46725},"net-tip-of-the-week-configuring-code-analysis-on-your-project",[46727],{"type":259,"value":46728},".NET tip of the week: configuring code analysis on your project",{"type":254,"tag":255,"props":46730,"children":46731},{},[46732,46734,46740,46742,46748,46750,46757,46759,46765,46767,46773],{"type":259,"value":46733},"Static code analysis is great because it helps you to have a better code quality, and it allows you to detect potential issues or bad practices in your code directly from your IDE. I knew about Roslyn Analyzers and the possibility to configure which rules are enabled or not (with their level of severity) through an ",{"type":254,"tag":389,"props":46735,"children":46737},{"className":46736},[],[46738],{"type":259,"value":46739},".editorconfig",{"type":259,"value":46741}," file. However, until I read this ",{"type":254,"tag":262,"props":46743,"children":46746},{"href":46744,"rel":46745},"https://endjin.com/blog/2022/01/raising-coding-standard-dotnet-analyzers.html",[266],[46747],{"type":259,"value":36966},{"type":259,"value":46749}," a few days ago, I did not know that you could set a ",{"type":254,"tag":262,"props":46751,"children":46754},{"href":46752,"rel":46753},"https://docs.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#analysismode",[266],[46755],{"type":259,"value":46756},"predefined code analysis configuration",{"type":259,"value":46758}," just by setting an ",{"type":254,"tag":389,"props":46760,"children":46762},{"className":46761},[],[46763],{"type":259,"value":46764},"AnalysisMode",{"type":259,"value":46766}," property in your project file. This is great, especially if you don't want to lose time configuring all the rules individually in an ",{"type":254,"tag":389,"props":46768,"children":46770},{"className":46769},[],[46771],{"type":259,"value":46772},"editorconfig",{"type":259,"value":46774}," file. You can just choose the analysis mode you feel more appropriate and eventually disable a few rules that you don't want.",{"type":254,"tag":255,"props":46776,"children":46777},{},[46778,46780,46785],{"type":259,"value":46779},"For instance, in this ASP.NET Core 6 project I created from the built-in template, I added an empty interface in the ",{"type":254,"tag":389,"props":46781,"children":46783},{"className":46782},[],[46784],{"type":259,"value":3667},{"type":259,"value":46786}," file.\nBy default I see a message indicating that I should declare my interface in a namespace.",{"type":254,"tag":255,"props":46788,"children":46789},{},[46790],{"type":254,"tag":1161,"props":46791,"children":46795},{"alt":46792,"className":46793,"src":46794},"Program in vscode with default analysis mode.",[1165,1166],"/posts/images/w052022tips_analysismode_1.png",[],{"type":254,"tag":255,"props":46797,"children":46798},{},[46799,46801,46807],{"type":259,"value":46800},"If I set the analysis mode to ",{"type":254,"tag":389,"props":46802,"children":46804},{"className":46803},[],[46805],{"type":259,"value":46806},"Recommended",{"type":259,"value":46808},", the code analysis indicates to me the same thing but this time as a warning.",{"type":254,"tag":255,"props":46810,"children":46811},{},[46812],{"type":254,"tag":1161,"props":46813,"children":46817},{"alt":46814,"className":46815,"src":46816},"Program in vscode with 'Recommended' analysis mode.",[1165,1166],"/posts/images/w052022tips_analysismode_2.png",[],{"type":254,"tag":255,"props":46819,"children":46820},{},[46821,46822,46828],{"type":259,"value":46800},{"type":254,"tag":389,"props":46823,"children":46825},{"className":46824},[],[46826],{"type":259,"value":46827},"All",{"type":259,"value":46829},", then not only do I have the previous warning but I also have a warning to tell me I should not have an empty interface.",{"type":254,"tag":255,"props":46831,"children":46832},{},[46833],{"type":254,"tag":1161,"props":46834,"children":46838},{"alt":46835,"className":46836,"src":46837},"Program in vscode with 'All' analysis mode.",[1165,1166],"/posts/images/w052022tips_analysismode_3.png",[],{"type":254,"tag":255,"props":46840,"children":46841},{},[46842,46844,46850],{"type":259,"value":46843},"There are also properties to only apply rules specific to a category (security for instance). You can check the ",{"type":254,"tag":262,"props":46845,"children":46848},{"href":46846,"rel":46847},"https://docs.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#analysismodecategory",[266],[46849],{"type":259,"value":6340},{"type":259,"value":46851}," to learn more about these.",{"type":254,"tag":282,"props":46853,"children":46855},{"id":46854},"tool-of-the-week-crontab-guru",[46856],{"type":259,"value":46857},"Tool of the week: crontab guru",{"type":254,"tag":255,"props":46859,"children":46860},{},[46861,46863,46869],{"type":259,"value":46862},"You are probably using cron expressions in a lot of different contexts whether it be to schedule an Azure Function or to trigger periodically a CI/CD pipeline (GitHub Actions or Azure DevOps for instance). Cron expressions are useful but you might not use them often, which makes it hard to remember the syntax. ",{"type":254,"tag":389,"props":46864,"children":46866},{"className":46865},[],[46867],{"type":259,"value":46868},"crontab guru",{"type":259,"value":46870}," is a tool that helps you create cron schedule expressions or understand what existing cron expressions mean.",{"type":254,"tag":255,"props":46872,"children":46873},{},[46874],{"type":254,"tag":1161,"props":46875,"children":46879},{"alt":46876,"className":46877,"src":46878},"Crontab Guru website.",[1165,1166],"/posts/images/w052022tips_crontab_guru.png",[],{"type":254,"tag":255,"props":46881,"children":46882},{},[46883,46885,46892],{"type":259,"value":46884},"It's really helpful, so add it to your ",{"type":254,"tag":262,"props":46886,"children":46889},{"href":46887,"rel":46888},"https://crontab.guru/",[266],[46890],{"type":259,"value":46891},"bookmarks",{"type":259,"value":594},{"type":254,"tag":282,"props":46894,"children":46896},{"id":46895},"the-azure-devops-tip-you-did-not-know-about-creating-diagrams-in-markdown-with-mermaidjs",[46897],{"type":259,"value":46898},"The Azure DevOps tip you did not know about: creating diagrams in markdown with Mermaid.js",{"type":254,"tag":255,"props":46900,"children":46901},{},[46902],{"type":259,"value":46903},"If you are using Azure DevOps, you are probably writing your technical documentation in markdown in a wiki. I like the idea of having \"documentation as code\" with markdown stored in a git repository that keeps the history of changes. Yet, sometimes documentation is not just about text, you want to have diagrams to properly illustrate what your text is explaining. And you don't want to have these diagrams just stored as images in your repository but you want them within the markdown to be modified as easily as the text.",{"type":254,"tag":255,"props":46905,"children":46906},{},[46907,46909,46915,46917,46923,46925,46930,46932,46937,46939,46946],{"type":259,"value":46908},"And guess what, that's possible thanks to ",{"type":254,"tag":389,"props":46910,"children":46912},{"className":46911},[],[46913],{"type":259,"value":46914},"Mermaid.js",{"type":259,"value":46916},". As you can read on its website, ",{"type":254,"tag":262,"props":46918,"children":46921},{"href":46919,"rel":46920},"https://mermaid-js.github.io/",[266],[46922],{"type":259,"value":46914},{"type":259,"value":46924}," is a \"JavaScript based diagramming and charting tool that renders Markdown-inspired text definitions to create and modify diagrams dynamically\". It means that you can write in your markdown file some text that describes a diagram and it will be rendered by ",{"type":254,"tag":389,"props":46926,"children":46928},{"className":46927},[],[46929],{"type":259,"value":46914},{"type":259,"value":46931},". There are a lot of types of diagrams that you can create using ",{"type":254,"tag":389,"props":46933,"children":46935},{"className":46934},[],[46936],{"type":259,"value":46914},{"type":259,"value":46938}," but Azure DevOps only ",{"type":254,"tag":262,"props":46940,"children":46943},{"href":46941,"rel":46942},"https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page",[266],[46944],{"type":259,"value":46945},"supports Sequence diagrams, Gantt Charts, and Flowcharts",{"type":259,"value":594},{"type":254,"tag":255,"props":46948,"children":46949},{},[46950],{"type":259,"value":46951},"Here is an example of a diagram I created in an Azure DevOps wiki:",{"type":254,"tag":255,"props":46953,"children":46954},{},[46955],{"type":254,"tag":1161,"props":46956,"children":46960},{"alt":46957,"className":46958,"src":46959},"Mermaid.js diagram in Azure DevOps wiki.",[1165,1166],"/posts/images/w052022tips_mermaid_1.png",[],{"type":254,"tag":282,"props":46962,"children":46964},{"id":46963},"the-vs-code-extension-you-should-try-rest-client",[46965],{"type":259,"value":46966},"The VS Code extension you should try: Rest Client",{"type":254,"tag":255,"props":46968,"children":46969},{},[46970,46976],{"type":254,"tag":262,"props":46971,"children":46974},{"href":46972,"rel":46973},"https://github.com/Huachao/vscode-restclient",[266],[46975],{"type":259,"value":37506},{"type":259,"value":46977}," is an open source VS Code extension that allows you to send HTTP requests and view the responses as you would do with Postman. But I prefer using REST Client over Postman because with REST Client you write the HTTP requests in text files (using the RFC 2616 standard) that you can version in your git repository along with your code. REST Client is not something I discovered this week, I have been using it for quite a long time but it's still very useful to me so I thought this post was a good opportunity to tell you to try it if you have not yet.",{"type":254,"tag":255,"props":46979,"children":46980},{},[46981],{"type":254,"tag":1161,"props":46982,"children":46986},{"alt":46983,"className":46984,"src":46985},"REST Client vscode extension.",[1165,1166],"/posts/images/w052022tips_restclient.png",[],{"type":254,"tag":255,"props":46988,"children":46989},{},[46990,46992,46998],{"type":259,"value":46991},"In fact, it's quite funny because the ",{"type":254,"tag":262,"props":46993,"children":46995},{"href":37481,"rel":46994},[266],[46996],{"type":259,"value":46997},"first article",{"type":259,"value":46999}," I wrote here on my blog was an article about REST Client, how to use it and why you should use it instead of Postman.",{"type":254,"tag":255,"props":47001,"children":47002},{},[47003],{"type":259,"value":10040},{"title":205,"searchDepth":635,"depth":635,"links":47005},[47006,47007,47008,47009],{"id":46725,"depth":635,"text":46728},{"id":46854,"depth":635,"text":46857},{"id":46895,"depth":635,"text":46898},{"id":46963,"depth":635,"text":46966},"content:1.posts:34.w05-2022-tips-learned-this-week.md","1.posts/34.w05-2022-tips-learned-this-week.md",{"_path":106,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":105,"description":47013,"lead":47014,"date":47015,"image":47016,"badge":47017,"tags":47018,"body":47020,"_type":1275,"_id":47438,"_source":231,"_file":47439,"_extension":1278},".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":9258},{"label":9260},[9262,1288,2924,9264,37215,47019,16850,245],"Vite",{"type":251,"children":47021,"toc":47432},[47022,47028,47078,47087,47100,47189,47211,47217,47240,47245,47254,47260,47265,47279,47292,47305,47310,47319,47324,47329,47334,47343,47358,47364,47369,47403,47412,47424,47428],{"type":254,"tag":282,"props":47023,"children":47025},{"id":47024},"net-tip-of-the-week-the-new-way-to-add-a-configuration-source",[47026],{"type":259,"value":47027},".NET tip of the week: the new way to add a configuration source",{"type":254,"tag":255,"props":47029,"children":47030},{},[47031,47033,47039,47040,47046,47048,47054,47056,47061,47063,47068,47070,47077],{"type":259,"value":47032},".NET 6 introduced a new way to build a .NET application using the new ",{"type":254,"tag":389,"props":47034,"children":47036},{"className":47035},[],[47037],{"type":259,"value":47038},"WebApplication",{"type":259,"value":10347},{"type":254,"tag":389,"props":47041,"children":47043},{"className":47042},[],[47044],{"type":259,"value":47045},"WebApplicationBuilder",{"type":259,"value":47047}," classes. One thing I like about it is how configuration is handled. Instead of using the ",{"type":254,"tag":389,"props":47049,"children":47051},{"className":47050},[],[47052],{"type":259,"value":47053},"ConfigureAppConfiguration",{"type":259,"value":47055}," method to add a new configuration source, you can directly use the ",{"type":254,"tag":389,"props":47057,"children":47059},{"className":47058},[],[47060],{"type":259,"value":2924},{"type":259,"value":47062}," property on the ",{"type":254,"tag":389,"props":47064,"children":47066},{"className":47065},[],[47067],{"type":259,"value":47045},{"type":259,"value":47069}," instance. You can see an example of this change on this screenshot of the ",{"type":254,"tag":262,"props":47071,"children":47074},{"href":47072,"rel":47073},"https://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60-samples?view=aspnetcore-6.0#add-configuration-providers",[266],[47075],{"type":259,"value":47076},"ASP.NET Core documentation",{"type":259,"value":474},{"type":254,"tag":255,"props":47079,"children":47080},{},[47081],{"type":254,"tag":1161,"props":47082,"children":47086},{"alt":47083,"className":47084,"src":47085},"Using configuration provider in ASP.NET 5 versus ASP.NET 6.",[1165,1166],"/posts/images/w032022tips_net_configuration_1.png",[],{"type":254,"tag":255,"props":47088,"children":47089},{},[47090,47092,47098],{"type":259,"value":47091},"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":254,"tag":389,"props":47093,"children":47095},{"className":47094},[],[47096],{"type":259,"value":47097},"appsettings.Development.json",{"type":259,"value":47099}," file). Before you would have to partially build your configuration to get the value of a setting, now you can just access it.",{"type":254,"tag":612,"props":47101,"children":47103},{"className":1423,"code":47102,"language":1425,"meta":205,"style":205},"builder.Configuration.AddAzureKeyVault(new Uri(builder.Configuration[\"KeyVault:VaultUri\"]), new DefaultAzureCredential());\n",[47104],{"type":254,"tag":389,"props":47105,"children":47106},{"__ignoreMap":205},[47107],{"type":254,"tag":622,"props":47108,"children":47109},{"class":624,"line":625},[47110,47114,47118,47122,47126,47131,47135,47139,47143,47147,47151,47155,47159,47163,47168,47172,47177,47181,47185],{"type":254,"tag":622,"props":47111,"children":47112},{"style":1202},[47113],{"type":259,"value":6499},{"type":254,"tag":622,"props":47115,"children":47116},{"style":629},[47117],{"type":259,"value":594},{"type":254,"tag":622,"props":47119,"children":47120},{"style":1202},[47121],{"type":259,"value":2924},{"type":254,"tag":622,"props":47123,"children":47124},{"style":629},[47125],{"type":259,"value":594},{"type":254,"tag":622,"props":47127,"children":47128},{"style":1735},[47129],{"type":259,"value":47130},"AddAzureKeyVault",{"type":254,"tag":622,"props":47132,"children":47133},{"style":629},[47134],{"type":259,"value":6598},{"type":254,"tag":622,"props":47136,"children":47137},{"style":714},[47138],{"type":259,"value":1663},{"type":254,"tag":622,"props":47140,"children":47141},{"style":629},[47142],{"type":259,"value":1668},{"type":254,"tag":622,"props":47144,"children":47145},{"style":1202},[47146],{"type":259,"value":6499},{"type":254,"tag":622,"props":47148,"children":47149},{"style":629},[47150],{"type":259,"value":594},{"type":254,"tag":622,"props":47152,"children":47153},{"style":1202},[47154],{"type":259,"value":2924},{"type":254,"tag":622,"props":47156,"children":47157},{"style":629},[47158],{"type":259,"value":10993},{"type":254,"tag":622,"props":47160,"children":47161},{"style":629},[47162],{"type":259,"value":652},{"type":254,"tag":622,"props":47164,"children":47165},{"style":664},[47166],{"type":259,"value":47167},"KeyVault:VaultUri",{"type":254,"tag":622,"props":47169,"children":47170},{"style":629},[47171],{"type":259,"value":652},{"type":254,"tag":622,"props":47173,"children":47174},{"style":629},[47175],{"type":259,"value":47176},"]),",{"type":254,"tag":622,"props":47178,"children":47179},{"style":629},[47180],{"type":259,"value":1624},{"type":254,"tag":622,"props":47182,"children":47183},{"style":714},[47184],{"type":259,"value":6649},{"type":254,"tag":622,"props":47186,"children":47187},{"style":629},[47188],{"type":259,"value":2154},{"type":254,"tag":255,"props":47190,"children":47191},{},[47192,47194,47201,47203,47209],{"type":259,"value":47193},"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":254,"tag":262,"props":47195,"children":47198},{"href":47196,"rel":47197},"https://andrewlock.net/exploring-dotnet-6-part-1-looking-inside-configurationmanager-in-dotnet-6/",[266],[47199],{"type":259,"value":47200},"very interesting article",{"type":259,"value":47202}," about ",{"type":254,"tag":389,"props":47204,"children":47206},{"className":47205},[],[47207],{"type":259,"value":47208},"ConfigurationManager",{"type":259,"value":47210}," that I suggest you read.",{"type":254,"tag":282,"props":47212,"children":47214},{"id":47213},"the-vs-code-extension-you-should-try-vite",[47215],{"type":259,"value":47216},"The VS Code extension you should try: Vite",{"type":254,"tag":255,"props":47218,"children":47219},{},[47220,47222,47229,47231,47238],{"type":259,"value":47221},"If you are developing a front-end using Vite (and there are ",{"type":254,"tag":262,"props":47223,"children":47226},{"href":47224,"rel":47225},"https://vitejs.dev/guide/why.html",[266],[47227],{"type":259,"value":47228},"good reasons",{"type":259,"value":47230}," why you should), there is a ",{"type":254,"tag":262,"props":47232,"children":47235},{"href":47233,"rel":47234},"https://marketplace.visualstudio.com/items?itemName=antfu.vite",[266],[47236],{"type":259,"value":47237},"Vite extension for VS Code",{"type":259,"value":47239}," currently in preview.",{"type":254,"tag":255,"props":47241,"children":47242},{},[47243],{"type":259,"value":47244},"You can see the main features of this extension below: little things that make you always more productive.",{"type":254,"tag":255,"props":47246,"children":47247},{},[47248],{"type":254,"tag":1161,"props":47249,"children":47253},{"alt":47250,"className":47251,"src":47252},"Vs code vite exension.",[1165,1166],"/posts/images/w032022tips_vscode_vite_1.png",[],{"type":254,"tag":282,"props":47255,"children":47257},{"id":47256},"the-azure-devops-tip-you-did-not-know-about-creating-pull-requests-templates",[47258],{"type":259,"value":47259},"The Azure DevOps tip you did not know about: creating pull requests templates",{"type":254,"tag":255,"props":47261,"children":47262},{},[47263],{"type":259,"value":47264},"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":254,"tag":255,"props":47266,"children":47267},{},[47268,47270,47277],{"type":259,"value":47269},"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":254,"tag":262,"props":47271,"children":47274},{"href":47272,"rel":47273},"https://docs.microsoft.com/en-us/azure/devops/repos/git/pull-request-templates?view=azure-devops",[266],[47275],{"type":259,"value":47276},"official documentation",{"type":259,"value":47278}," but let me show you quickly how it works:",{"type":254,"tag":255,"props":47280,"children":47281},{},[47282,47284,47290],{"type":259,"value":47283},"1- You create a ",{"type":254,"tag":389,"props":47285,"children":47287},{"className":47286},[],[47288],{"type":259,"value":47289},".azuredevops",{"type":259,"value":47291}," folder in the root of your git repository",{"type":254,"tag":255,"props":47293,"children":47294},{},[47295,47297,47303],{"type":259,"value":47296},"2- You create a markdown file ",{"type":254,"tag":389,"props":47298,"children":47300},{"className":47299},[],[47301],{"type":259,"value":47302},"pull_request_template.md",{"type":259,"value":47304}," containing the description you want",{"type":254,"tag":255,"props":47306,"children":47307},{},[47308],{"type":259,"value":47309},"You can see below an example of a template I created:",{"type":254,"tag":255,"props":47311,"children":47312},{},[47313],{"type":254,"tag":1161,"props":47314,"children":47318},{"alt":47315,"className":47316,"src":47317},"Pull request markdown template file.",[1165,1166],"/posts/images/w032022tips_pr_template_1.png",[],{"type":254,"tag":255,"props":47320,"children":47321},{},[47322],{"type":259,"value":47323},"3- You commit this file and push it in your main branch",{"type":254,"tag":255,"props":47325,"children":47326},{},[47327],{"type":259,"value":47328},"4- Now when someone creates a pull request, he will have a pre-filled description to complete before submitting his pull request",{"type":254,"tag":255,"props":47330,"children":47331},{},[47332],{"type":259,"value":47333},"This is what it looks like for my template:",{"type":254,"tag":255,"props":47335,"children":47336},{},[47337],{"type":254,"tag":1161,"props":47338,"children":47342},{"alt":47339,"className":47340,"src":47341},"Pull request template in Azure DevOps.",[1165,1166],"/posts/images/w032022tips_pr_template_2.png",[],{"type":254,"tag":427,"props":47344,"children":47345},{"icon":429},[47346],{"type":254,"tag":255,"props":47347,"children":47348},{},[47349,47351,47357],{"type":259,"value":47350},"If you are using GitHub and not Azure DevOps, just know that there are also pull requests templates in ",{"type":254,"tag":262,"props":47352,"children":47355},{"href":47353,"rel":47354},"https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository",[266],[47356],{"type":259,"value":16352},{"type":259,"value":594},{"type":254,"tag":282,"props":47359,"children":47361},{"id":47360},"tool-of-the-week-degit",[47362],{"type":259,"value":47363},"Tool of the week: degit",{"type":254,"tag":255,"props":47365,"children":47366},{},[47367],{"type":259,"value":47368},"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":254,"tag":255,"props":47370,"children":47371},{},[47372,47379,47381,47386,47388,47394,47396,47402],{"type":254,"tag":262,"props":47373,"children":47376},{"href":47374,"rel":47375},"https://github.com/Rich-Harris/degit",[266],[47377],{"type":259,"value":47378},"degit",{"type":259,"value":47380}," 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":254,"tag":389,"props":47382,"children":47384},{"className":47383},[],[47385],{"type":259,"value":24289},{"type":259,"value":47387}," tool so you can install it globally with ",{"type":254,"tag":389,"props":47389,"children":47391},{"className":47390},[],[47392],{"type":259,"value":47393},"npm install -g degit",{"type":259,"value":47395}," or directly run it using ",{"type":254,"tag":389,"props":47397,"children":47399},{"className":47398},[],[47400],{"type":259,"value":47401},"npx",{"type":259,"value":594},{"type":254,"tag":255,"props":47404,"children":47405},{},[47406],{"type":254,"tag":1161,"props":47407,"children":47411},{"alt":47408,"className":47409,"src":47410},"Ouput when using degit package on a repository.",[1165,1166],"/posts/images/w032022tips_degit_1.png",[],{"type":254,"tag":255,"props":47413,"children":47414},{},[47415,47417,47422],{"type":259,"value":47416},"Don't hesitate to give the project a star on ",{"type":254,"tag":262,"props":47418,"children":47420},{"href":47374,"rel":47419},[266],[47421],{"type":259,"value":16352},{"type":259,"value":47423}," if you find it useful.",{"type":254,"tag":255,"props":47425,"children":47426},{},[47427],{"type":259,"value":10040},{"type":254,"tag":1260,"props":47429,"children":47430},{},[47431],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":47433},[47434,47435,47436,47437],{"id":47024,"depth":635,"text":47027},{"id":47213,"depth":635,"text":47216},{"id":47256,"depth":635,"text":47259},{"id":47360,"depth":635,"text":47363},"content:1.posts:33.w03-2022-tips-learned-this-week.md","1.posts/33.w03-2022-tips-learned-this-week.md",{"_path":103,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":102,"description":47441,"lead":47442,"date":47443,"image":47444,"badge":47445,"tags":47446,"body":47447,"_type":1275,"_id":47679,"_source":231,"_file":47680,"_extension":1278},"This is my first article of the series Tips I learned this week for 2022 🚀! And today we are going to see some tips about .NET, Azure, GitHub, and VS Code.","Tooling around .NET, Azure, Github and VS Code.","2022-01-14T00:00:00.000Z",{"src":9258},{"label":9260},[9262,1288,16351,16352,37215,245],{"type":251,"children":47448,"toc":47672},[47449,47463,47469,47482,47491,47496,47508,47519,47549,47558,47564,47578,47591,47604,47613,47626,47632,47646,47655,47668],{"type":254,"tag":255,"props":47450,"children":47451},{},[47452,47454,47461],{"type":259,"value":47453},"This is my first article of the series ",{"type":254,"tag":262,"props":47455,"children":47458},{"href":47456,"rel":47457},"https://www.techwatching.dev/tags/tips-learned-this-week/",[266],[47459],{"type":259,"value":47460},"Tips I learned this week",{"type":259,"value":47462}," for 2022 🚀! And today we are going to see some tips about .NET, Azure, GitHub, and VS Code.",{"type":254,"tag":282,"props":47464,"children":47466},{"id":47465},"net-tip-of-the-week-changing-the-net-cli-language",[47467],{"type":259,"value":47468},".NET tip of the week: changing the .NET CLI language",{"type":254,"tag":255,"props":47470,"children":47471},{},[47472,47474,47480],{"type":259,"value":47473},"Did you know that you could change the language of the .NET CLI to the one you find most appropriate? By default, the dotnet CLI messages you see when running a dotnet program are your language OS (French in my case). However, by setting the \"DOTNET_CLI_UI_LANGUAGE\" variable environment to ",{"type":254,"tag":389,"props":47475,"children":47477},{"className":47476},[],[47478],{"type":259,"value":47479},"en",{"type":259,"value":47481}," for instance I can change it to English.",{"type":254,"tag":255,"props":47483,"children":47484},{},[47485],{"type":254,"tag":1161,"props":47486,"children":47490},{"alt":47487,"className":47488,"src":47489},".NET CLI language change in terminal.",[1165,1166],"/posts/images/w022022tips_dotnet_cli.png",[],{"type":254,"tag":255,"props":47492,"children":47493},{},[47494],{"type":259,"value":47495},"Why is it useful? Let's imagine I have an issue with a dotnet CLI command and that I want to get some help from the community by posting a question to a Q&A website with a screenshot of my error. If all the messages are in French I will probably not get many answers whereas if it's in English everyone will be able to understand what my commands are doing. There is also the fact that sometimes French accents and special characters are not well displayed by some terminals.",{"type":254,"tag":282,"props":47497,"children":47499},{"id":47498},"the-azure-tip-you-did-not-know-about-simplify-your-azure-cli-configuration-with-azure-init",[47500,47502],{"type":259,"value":47501},"The Azure tip you did not know about: simplify your Azure CLI configuration with ",{"type":254,"tag":389,"props":47503,"children":47505},{"className":47504},[],[47506],{"type":259,"value":47507},"azure init",{"type":254,"tag":255,"props":47509,"children":47510},{},[47511,47513,47518],{"type":259,"value":47512},"I don't know if you use a lot Azure CLI but it's a very nice tool! Each time I use Azure CLI, I think \"it's awesome 🤩 I should use it more often instead of using Azure Portal\". I even wrote a post about that 2 years ago: \"",{"type":254,"tag":262,"props":47514,"children":47516},{"href":17271,"rel":47515},[266],[47517],{"type":259,"value":15},{"type":259,"value":23774},{"type":254,"tag":255,"props":47520,"children":47521},{},[47522,47524,47530,47532,47539,47541,47547],{"type":259,"value":47523},"However, if you want to configure your Azure CLI, it can be sometimes a bit boring and slow to configure it using the ",{"type":254,"tag":389,"props":47525,"children":47527},{"className":47526},[],[47528],{"type":259,"value":47529},"az config",{"type":259,"value":47531}," command. That is why Microsoft has ",{"type":254,"tag":262,"props":47533,"children":47536},{"href":47534,"rel":47535},"https://techcommunity.microsoft.com/t5/azure-tools-blog/streamline-configuring-azure-cli-with-az-init/ba-p/3051810",[266],[47537],{"type":259,"value":47538},"released in preview",{"type":259,"value":47540}," an Azure CLI extension called ",{"type":254,"tag":389,"props":47542,"children":47544},{"className":47543},[],[47545],{"type":259,"value":47546},"az init",{"type":259,"value":47548}," to simplify this configuration. For example you can quickly configure the output of the commands or the syntax highlighting, things like that.",{"type":254,"tag":255,"props":47550,"children":47551},{},[47552],{"type":254,"tag":1161,"props":47553,"children":47557},{"alt":47554,"className":47555,"src":47556},"azure init command output.",[1165,1166],"/posts/images/w022022tips_az_init.png",[],{"type":254,"tag":282,"props":47559,"children":47561},{"id":47560},"tool-of-the-week-github-code-search",[47562],{"type":259,"value":47563},"Tool of the week: GitHub Code Search",{"type":254,"tag":255,"props":47565,"children":47566},{},[47567,47569,47576],{"type":259,"value":47568},"Have you ever wished you could easily search code on GitHub in multiple repositories without cloning anything? That is now possible with ",{"type":254,"tag":262,"props":47570,"children":47573},{"href":47571,"rel":47572},"https://cs.github.com/",[266],[47574],{"type":259,"value":47575},"GitHub Code Search",{"type":259,"value":47577},". It is still in preview but looks promising! You have access to nice filters to find exactly the code you are looking for, and once you get it you can navigate in and across files.",{"type":254,"tag":255,"props":47579,"children":47580},{},[47581,47583,47589],{"type":259,"value":47582},"For a long time, I have kept a bookmark to the \"",{"type":254,"tag":262,"props":47584,"children":47587},{"href":47585,"rel":47586},"https://source.dot.net/",[266],[47588],{"type":259,"value":47585},{"type":259,"value":47590},"\" website for the times when I needed to understand how something was implemented in the .NET Core framework. But with GitHub Code Search I think I don't need it anymore. .NET Core is open source and all the source code is on GitHub so I can quickly find everything I need just by searching it on GitHub.",{"type":254,"tag":255,"props":47592,"children":47593},{},[47594,47596,47602],{"type":259,"value":47595},"For instance, let's say I don't remember exactly what configuration is injected by default in a dotnet project when you use the ",{"type":254,"tag":389,"props":47597,"children":47599},{"className":47598},[],[47600],{"type":259,"value":47601},"Host.CreateDefaultBuilder",{"type":259,"value":47603}," method. I will scope my search to dotnet organization repositories and a few keystrokes later, I can see all the configuration providers used to load some default configuration in a project.",{"type":254,"tag":255,"props":47605,"children":47606},{},[47607],{"type":254,"tag":1161,"props":47608,"children":47612},{"alt":47609,"className":47610,"src":47611},"GitHub search usage.",[1165,1166],"/posts/images/w022022tips_githu_cs.gif",[],{"type":254,"tag":255,"props":47614,"children":47615},{},[47616,47618,47625],{"type":259,"value":47617},"You can read more about GitHub Code Search on ",{"type":254,"tag":262,"props":47619,"children":47622},{"href":47620,"rel":47621},"https://github.blog/2021-12-08-improving-github-code-search/",[266],[47623],{"type":259,"value":47624},"GitHub's blog",{"type":259,"value":594},{"type":254,"tag":282,"props":47627,"children":47629},{"id":47628},"the-vs-code-extension-you-should-try-i18n-ally",[47630],{"type":259,"value":47631},"The VS Code extension you should try: i18n ally",{"type":254,"tag":255,"props":47633,"children":47634},{},[47635,47637,47644],{"type":259,"value":47636},"When you are developing an application that supports several languages, it can quickly become annoying to go in every translation file when you need to check or modify a translation. If you are developing your application using VS Code, you probably should check out the extension ",{"type":254,"tag":262,"props":47638,"children":47641},{"href":47639,"rel":47640},"https://github.com/lokalise/i18n-ally",[266],[47642],{"type":259,"value":47643},"i18n ally",{"type":259,"value":47645}," because it will save you a lot of time.",{"type":254,"tag":255,"props":47647,"children":47648},{},[47649],{"type":254,"tag":1161,"props":47650,"children":47654},{"alt":47651,"className":47652,"src":47653},"i18n ally extension in vscode.",[1165,1166],"/posts/images/w222021tips_i18n_ally.png",[],{"type":254,"tag":255,"props":47656,"children":47657},{},[47658,47660,47666],{"type":259,"value":47659},"It is very handy and I like the fact the extension support lots of frameworks like Vue.js, Angular, React, Svelte, and Flutter (you can find the complete list of supported frameworks ",{"type":254,"tag":262,"props":47661,"children":47664},{"href":47662,"rel":47663},"https://github.com/lokalise/i18n-ally/wiki/Supported-Frameworks",[266],[47665],{"type":259,"value":1414},{"type":259,"value":47667},"). My only regret with this extension is not to have heard about it sooner.",{"type":254,"tag":255,"props":47669,"children":47670},{},[47671],{"type":259,"value":10040},{"title":205,"searchDepth":635,"depth":635,"links":47673},[47674,47675,47677,47678],{"id":47465,"depth":635,"text":47468},{"id":47498,"depth":635,"text":47676},"The Azure tip you did not know about: simplify your Azure CLI configuration with azure init",{"id":47560,"depth":635,"text":47563},{"id":47628,"depth":635,"text":47631},"content:1.posts:32.w02-2022-tips-learned-this-week.md","1.posts/32.w02-2022-tips-learned-this-week.md",{"_path":100,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":99,"description":47682,"lead":47683,"date":47684,"badge":47685,"image":47686,"tags":47688,"body":47689,"_type":1275,"_id":47963,"_source":231,"_file":47964,"_extension":1278},"Do you like using hot reload when developing applications? How about using hot reload when developing the cloud infrastructure of an application? Keep reading because that's what we are going to talk about.","Hot-reloading your cloud infrastructure.","2022-01-02T00:00:00.000Z",{"label":16349},{"src":47687},"/images/cloud-lighthouse_1.jpg",[10225,19569],{"type":251,"children":47690,"toc":47958},[47691,47695,47701,47706,47724,47754,47788,47826,47839,47845,47857,47878,47891,47897,47912,47924,47933,47945],{"type":254,"tag":255,"props":47692,"children":47693},{},[47694],{"type":259,"value":47682},{"type":254,"tag":282,"props":47696,"children":47698},{"id":47697},"developing-and-deploying-cloud-infrastructure",[47699],{"type":259,"value":47700},"Developing and deploying Cloud Infrastructure",{"type":254,"tag":255,"props":47702,"children":47703},{},[47704],{"type":259,"value":47705},"When doing Infrastructure as Code for a cloud application we usually do the following steps:",{"type":254,"tag":476,"props":47707,"children":47708},{},[47709,47714,47719],{"type":254,"tag":336,"props":47710,"children":47711},{},[47712],{"type":259,"value":47713},"we write the code describing the desired state of the infrastructure",{"type":254,"tag":336,"props":47715,"children":47716},{},[47717],{"type":259,"value":47718},"we build this infrastructure code and compare the resulting desired state against the current state of the infrastructure",{"type":254,"tag":336,"props":47720,"children":47721},{},[47722],{"type":259,"value":47723},"we deploy to the provisioned infrastructure the changes needed to achieve the desired state",{"type":254,"tag":255,"props":47725,"children":47726},{},[47727,47729,47740,47742,47752],{"type":259,"value":47728},"When using Pulumi, you can run the ",{"type":254,"tag":262,"props":47730,"children":47733},{"href":47731,"rel":47732},"https://www.pulumi.com/docs/reference/cli/pulumi_preview/",[266],[47734],{"type":254,"tag":389,"props":47735,"children":47737},{"className":47736},[],[47738],{"type":259,"value":47739},"pulumi preview",{"type":259,"value":47741}," command for step 2 and the ",{"type":254,"tag":262,"props":47743,"children":47746},{"href":47744,"rel":47745},"https://www.pulumi.com/docs/reference/cli/pulumi_up/",[266],[47747],{"type":254,"tag":389,"props":47748,"children":47750},{"className":47749},[],[47751],{"type":259,"value":23629},{"type":259,"value":47753}," command for step 3.",{"type":254,"tag":255,"props":47755,"children":47756},{},[47757,47759,47764,47766,47772,47774,47779,47781,47786],{"type":259,"value":47758},"As its name suggests, the ",{"type":254,"tag":389,"props":47760,"children":47762},{"className":47761},[],[47763],{"type":259,"value":47739},{"type":259,"value":47765}," command only \"preview\" the updates that could be made to the infrastructure but does not apply them. To perform an update of the cloud infrastructure you have to use the ",{"type":254,"tag":389,"props":47767,"children":47769},{"className":47768},[],[47770],{"type":259,"value":47771},"pulumi update",{"type":259,"value":47773}," command which also does a preview of the changes, prompts the user to approve the changes to be made, and performs these changes. That is why, to be honest I don't bother with ",{"type":254,"tag":389,"props":47775,"children":47777},{"className":47776},[],[47778],{"type":259,"value":47739},{"type":259,"value":47780},": most of the time I only use the ",{"type":254,"tag":389,"props":47782,"children":47784},{"className":47783},[],[47785],{"type":259,"value":23629},{"type":259,"value":47787}," command (which means I do steps 2 and 3 in one row).",{"type":254,"tag":427,"props":47789,"children":47790},{"icon":429},[47791],{"type":254,"tag":255,"props":47792,"children":47793},{},[47794,47796,47801,47803,47808,47810,47816,47818,47824],{"type":259,"value":47795},"In case you wonder, if you already have run ",{"type":254,"tag":389,"props":47797,"children":47799},{"className":47798},[],[47800],{"type":259,"value":47739},{"type":259,"value":47802}," before running ",{"type":254,"tag":389,"props":47804,"children":47806},{"className":47805},[],[47807],{"type":259,"value":23629},{"type":259,"value":47809}," you can skip the preview in the ",{"type":254,"tag":389,"props":47811,"children":47813},{"className":47812},[],[47814],{"type":259,"value":47815},"up",{"type":259,"value":47817}," command by using the ",{"type":254,"tag":389,"props":47819,"children":47821},{"className":47820},[],[47822],{"type":259,"value":47823},"--skip-preview",{"type":259,"value":47825}," option.",{"type":254,"tag":255,"props":47827,"children":47828},{},[47829,47831,47838],{"type":259,"value":47830},"Of course, you can use these commands to automate the deployment of your cloud infrastructure using your favorite ",{"type":254,"tag":262,"props":47832,"children":47835},{"href":47833,"rel":47834},"https://www.pulumi.com/docs/guides/continuous-delivery/",[266],[47836],{"type":259,"value":47837},"CI/CD system",{"type":259,"value":594},{"type":254,"tag":282,"props":47840,"children":47842},{"id":47841},"the-need-for-a-hot-reload-like-experience-when-doing-iac",[47843],{"type":259,"value":47844},"The need for a hot reload-like experience when doing IaC",{"type":254,"tag":255,"props":47846,"children":47847},{},[47848,47850,47855],{"type":259,"value":47849},"All this is great but there are times when all you want is to quickly write your infrastructure code and check that you can successfully provision and configure the cloud resources you need. This can happen when you want to prototype something, test a new cloud resource, or simply when you are developing your infrastructure and want to verify your infrastructure code works. Usually, you are using a \"sandbox\" cloud environment for this, the same way you would use your local environment for debugging application code. At these moments, you don't care about CI/CD. You care about quickly experimenting with changes to your infrastructure, being productive, making changes in your code, and checking what it does, so you want to be able to quickly iterate to make your code work. Yet, this is not possible if each time you make a change to the code you have to manually run the ",{"type":254,"tag":389,"props":47851,"children":47853},{"className":47852},[],[47854],{"type":259,"value":23629},{"type":259,"value":47856}," command, and approve the deployment.",{"type":254,"tag":427,"props":47858,"children":47859},{"icon":429},[47860],{"type":254,"tag":255,"props":47861,"children":47862},{},[47863,47864,47870,47872,47877],{"type":259,"value":17001},{"type":254,"tag":389,"props":47865,"children":47867},{"className":47866},[],[47868],{"type":259,"value":47869},"--yes",{"type":259,"value":47871}," option to automatically approve the changes and directly perform the update when running the ",{"type":254,"tag":389,"props":47873,"children":47875},{"className":47874},[],[47876],{"type":259,"value":23629},{"type":259,"value":32093},{"type":254,"tag":255,"props":47879,"children":47880},{},[47881,47883,47889],{"type":259,"value":47882},"But guess what? Fast feedback when doing a code change is exactly what you want when developing an application. Indeed whether you are building an application or building infrastructure you are doing software development so you have the same needs and practices. And what do application developers have in their toolbox to be more productive when developing? They have \"hot reload\": while debugging locally an application, they can modify the source code, and changes made will be almost instantaneously reflected on the application. Wouldn't it be great if similarly you could make a change in your infrastructure code and have the provisioned infrastructure automatically updated? That is what the ",{"type":254,"tag":389,"props":47884,"children":47886},{"className":47885},[],[47887],{"type":259,"value":47888},"pulumi watch",{"type":259,"value":47890}," command is here for.",{"type":254,"tag":282,"props":47892,"children":47894},{"id":47893},"using-pulumi-watch",[47895],{"type":259,"value":47896},"Using Pulumi Watch",{"type":254,"tag":255,"props":47898,"children":47899},{},[47900,47910],{"type":254,"tag":262,"props":47901,"children":47904},{"href":47902,"rel":47903},"https://www.pulumi.com/docs/reference/cli/pulumi_watch/",[266],[47905],{"type":254,"tag":389,"props":47906,"children":47908},{"className":47907},[],[47909],{"type":259,"value":47888},{"type":259,"value":47911}," is a command currently in preview that watches for changes in the infrastructure code directory and continuously updates the cloud resources.",{"type":254,"tag":255,"props":47913,"children":47914},{},[47915,47917,47922],{"type":259,"value":47916},"But the best is to see by yourself. In the following example, you can see on the left of the screen a terminal opened with the ",{"type":254,"tag":389,"props":47918,"children":47920},{"className":47919},[],[47921],{"type":259,"value":47888},{"type":259,"value":47923}," command running, and on the right of the screen vscode opened with the code describing the currently provisioned Azure infrastructure for my project. Some lines to create a \"Tweets\" table in the storage account are commented. When I uncomment them and save the code file, you can see that pulumi detects it, builds the code, and deploys the changes so creates the table in that case.",{"type":254,"tag":255,"props":47925,"children":47926},{},[47927],{"type":254,"tag":1161,"props":47928,"children":47932},{"alt":47929,"className":47930,"src":47931},"Pulumi watch in terminal.",[1165,1166],"/posts/images/pulumiwatch_terminal_1.gif",[],{"type":254,"tag":255,"props":47934,"children":47935},{},[47936,47938,47943],{"type":259,"value":47937},"I don't know what you think but I find this pretty cool: it's like hot reload for your infrastructure as code. Of course, you will probably not use ",{"type":254,"tag":389,"props":47939,"children":47941},{"className":47940},[],[47942],{"type":259,"value":47888},{"type":259,"value":47944}," all the time, but for quickly writing and testing your infrastructure code it can be very helpful.",{"type":254,"tag":255,"props":47946,"children":47947},{},[47948,47950,47956],{"type":259,"value":47949},"As far as I know (don't hesitate to correct me in the comments if I am wrong), there is no such feature in Terraform and it's too bad because when you start using the ",{"type":254,"tag":389,"props":47951,"children":47953},{"className":47952},[],[47954],{"type":259,"value":47955},"watch",{"type":259,"value":47957}," command you don't want to do without it.",{"title":205,"searchDepth":635,"depth":635,"links":47959},[47960,47961,47962],{"id":47697,"depth":635,"text":47700},{"id":47841,"depth":635,"text":47844},{"id":47893,"depth":635,"text":47896},"content:1.posts:31.pulumi-watch.md","1.posts/31.pulumi-watch.md",{"_path":97,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":96,"description":47966,"lead":47967,"date":47968,"badge":47969,"image":47970,"tags":47972,"body":47974,"_type":1275,"_id":50509,"_source":231,"_file":50510,"_extension":1278},"By default when you use Pulumi, the state is managed by Pulumi Service which is very convenient as you can concentrate on building your project infrastructure instead of spending time on where to store the state and how to handle concurrency. However, sometimes for governance or pricing concerns, or any other reasons, you don't want to use Pulumi Service and you prefer to manage the state yourself with your own backend. In this article, we will see how we can do that using Azure.","Pulumi without Pulumi Service.","2021-12-14T00:00:00.000Z",{"label":16349},{"src":47971},"/images/cloud-crave_1.jpg",[10225,19569,16351,47973,40204,6260],"Azure Storage",{"type":251,"children":47975,"toc":50491},[47976,47980,47986,47992,47997,48015,48023,48028,48033,48056,48064,48070,48075,48080,48103,48108,48114,48120,48131,48165,48170,48175,48181,48203,48212,48226,48231,48375,48380,48480,48485,48561,48566,48685,48690,48726,48732,48737,48761,48781,48868,48873,48893,48902,48908,48914,48919,48924,48930,48935,48940,48967,48972,48978,48983,49119,49132,49137,49212,49225,49295,49300,49350,49355,49390,49395,49489,49495,49500,49505,49519,49541,49550,49555,49559,49564,50469,50474,50487],{"type":254,"tag":255,"props":47977,"children":47978},{},[47979],{"type":259,"value":47966},{"type":254,"tag":282,"props":47981,"children":47983},{"id":47982},"a-quick-reminder-about-states-and-backends",[47984],{"type":259,"value":47985},"A quick reminder about states and backends",{"type":254,"tag":455,"props":47987,"children":47989},{"id":47988},"what-is-this-state-we-need-to-store",[47990],{"type":259,"value":47991},"What is this state we need to store?",{"type":254,"tag":255,"props":47993,"children":47994},{},[47995],{"type":259,"value":47996},"Like other Infrastructure as Code platforms, Pulumi uses a declarative approach:",{"type":254,"tag":332,"props":47998,"children":47999},{},[48000,48005,48010],{"type":254,"tag":336,"props":48001,"children":48002},{},[48003],{"type":259,"value":48004},"we write code to describe the desired state of our infrastructure",{"type":254,"tag":336,"props":48006,"children":48007},{},[48008],{"type":259,"value":48009},"Pulumi engine compares this desired state with the current state of the infrastructure and determines what changes need to be made",{"type":254,"tag":336,"props":48011,"children":48012},{},[48013],{"type":259,"value":48014},"Pulumi deploys these changes and updates the current state of the provisioned infrastructure",{"type":254,"tag":427,"props":48016,"children":48017},{"icon":429},[48018],{"type":254,"tag":255,"props":48019,"children":48020},{},[48021],{"type":259,"value":48022},"Some people think using Pulumi means adopting an imperative approach because we are using programming languages (so imperative languages) instead of using declarative languages (like YAML, JSON, and HCL). However, being declarative is not about the language used but about defining the \"what\" (the infrastructure we want to provision) instead of the \"how\" (the steps to provision this infrastructure). So Pulumi has the best of both worlds by being declarative while using programming languages.",{"type":254,"tag":255,"props":48024,"children":48025},{},[48026],{"type":259,"value":48027},"As you understood, being able to provision and modify an infrastructure with this declarative approach requires 2 states: the desired state and the current state of the infrastructure. The desired state is the infrastructure code that we usually store in a Git repository alongside the application code. The current state however is computed by the Pulumi engine each time we modify the infrastructure and needs to be stored somewhere.",{"type":254,"tag":255,"props":48029,"children":48030},{},[48031],{"type":259,"value":48032},"That is why we need a \"backend\" to use Pulumi, it's just a place to store the current state of the provisioned infrastructure.",{"type":254,"tag":255,"props":48034,"children":48035},{},[48036,48038,48045,48047,48054],{"type":259,"value":48037},"If you want more information about states and backend, Pulumi has a ",{"type":254,"tag":262,"props":48039,"children":48042},{"href":48040,"rel":48041},"https://www.pulumi.com/docs/intro/concepts/state/",[266],[48043],{"type":259,"value":48044},"documentation page",{"type":259,"value":48046}," about that and there is also ",{"type":254,"tag":262,"props":48048,"children":48051},{"href":48049,"rel":48050},"https://www.pulumi.com/docs/intro/concepts/how-pulumi-works/",[266],[48052],{"type":259,"value":48053},"a page",{"type":259,"value":48055}," about how Pulumi works.",{"type":254,"tag":255,"props":48057,"children":48058},{},[48059],{"type":254,"tag":1161,"props":48060,"children":48063},{"alt":43180,"className":48061,"src":48062},[1165,1166],"/posts/images/pulumiazurebackend_schema_1.png",[],{"type":254,"tag":455,"props":48065,"children":48067},{"id":48066},"what-backends-can-we-use-to-manage-the-infrastructure",[48068],{"type":259,"value":48069},"What \"backends\" can we use to manage the infrastructure?",{"type":254,"tag":255,"props":48071,"children":48072},{},[48073],{"type":259,"value":48074},"The default backend is Pulumi Service which is a web application that stores the infrastructure state and has additional features like concurrent state locking, team policies, or deployment history. This service is managed by Pulumi, is free for individuals but charged for teams, and enterprises. It can be self-hosted in the enterprise plan. Just as a side note, Pulumi Service (along with support and training) is how the company Pulumi makes money because everything else is free and open source.",{"type":254,"tag":255,"props":48076,"children":48077},{},[48078],{"type":259,"value":48079},"Yet, we don't have to pay anything to use Pulumi because Pulumi Service, no matter how good it may be, is not the only solution to store the infrastructure state. Indeed, Pulumi supports other backends that we can manage ourselves:",{"type":254,"tag":332,"props":48081,"children":48082},{},[48083,48088,48093,48098],{"type":254,"tag":336,"props":48084,"children":48085},{},[48086],{"type":259,"value":48087},"Local Filesystem",{"type":254,"tag":336,"props":48089,"children":48090},{},[48091],{"type":259,"value":48092},"AWS S3 (or compatible server)",{"type":254,"tag":336,"props":48094,"children":48095},{},[48096],{"type":259,"value":48097},"Google Cloud Storage",{"type":254,"tag":336,"props":48099,"children":48100},{},[48101],{"type":259,"value":48102},"Azure Blob Storage",{"type":254,"tag":255,"props":48104,"children":48105},{},[48106],{"type":259,"value":48107},"In the rest of this article, we will see how to use Pulumi with Azure Blob Storage as the backend for our infrastructure state.",{"type":254,"tag":282,"props":48109,"children":48111},{"id":48110},"using-pulumi-with-the-azure-blob-storage-backend",[48112],{"type":259,"value":48113},"Using Pulumi with the Azure Blob Storage backend",{"type":254,"tag":455,"props":48115,"children":48117},{"id":48116},"what-do-we-need",[48118],{"type":259,"value":48119},"What do we need?",{"type":254,"tag":255,"props":48121,"children":48122},{},[48123,48129],{"type":254,"tag":262,"props":48124,"children":48127},{"href":48125,"rel":48126},"https://www.pulumi.com/docs/intro/concepts/state/#logging-into-the-azure-blob-storage-backend",[266],[48128],{"type":259,"value":43254},{"type":259,"value":48130}," on using Azure Blob Storage backend is short. It only says that we need to:",{"type":254,"tag":332,"props":48132,"children":48133},{},[48134,48139,48144],{"type":254,"tag":336,"props":48135,"children":48136},{},[48137],{"type":259,"value":48138},"set the AZURE_STORAGE_ACCOUNT environment variable to specify the Azure storage account to use",{"type":254,"tag":336,"props":48140,"children":48141},{},[48142],{"type":259,"value":48143},"set the AZURE_STORAGE_KEY or the AZURE_STORAGE_SAS_TOKEN environment variables to let Pulumi access the storage",{"type":254,"tag":336,"props":48145,"children":48146},{},[48147,48149,48155,48157,48163],{"type":259,"value":48148},"execute the following command ",{"type":254,"tag":389,"props":48150,"children":48152},{"className":48151},[],[48153],{"type":259,"value":48154},"pulumi login azblob://\u003Ccontainer-path>",{"type":259,"value":48156}," where ",{"type":254,"tag":389,"props":48158,"children":48160},{"className":48159},[],[48161],{"type":259,"value":48162},"container-path",{"type":259,"value":48164}," is the path to a blob container in the storage account",{"type":254,"tag":255,"props":48166,"children":48167},{},[48168],{"type":259,"value":48169},"Once this command is executed, we can start using Pulumi as we would with any other backend. The infrastructure's current state will automatically be stored in the blob container you specified. It will be compared to the desired state when a change is made in the code to know what resources need to be created/updated/deleted.",{"type":254,"tag":255,"props":48171,"children":48172},{},[48173],{"type":259,"value":48174},"In fact, that is not very complex. Nevertheless, the documentation assumes we already have created an Azure storage account with a blob container in it and retrieved the key to access it. That is not the case, so now that we know what we need let's script it!",{"type":254,"tag":455,"props":48176,"children":48178},{"id":48177},"how-to-create-and-configure-the-azure-blob-storage-backend",[48179],{"type":259,"value":48180},"How to create and configure the Azure Blob Storage backend?",{"type":254,"tag":255,"props":48182,"children":48183},{},[48184,48186,48193,48195,48201],{"type":259,"value":48185},"For me, the easiest way to write a script to create and configure the storage account we need is to use Azure CLI. One nice way of writing Azure CLI scripts is to do it in vscode with the ",{"type":254,"tag":262,"props":48187,"children":48190},{"href":48188,"rel":48189},"https://marketplace.visualstudio.com/items?itemName=ms-vscode.azurecli",[266],[48191],{"type":259,"value":48192},"Azure CLI Tools extension",{"type":259,"value":48194},": you can create ",{"type":254,"tag":389,"props":48196,"children":48198},{"className":48197},[],[48199],{"type":259,"value":48200},".azcli",{"type":259,"value":48202}," files with IntelliSense on them and run the commands you are writing in the integrated terminal (see screenshot below).",{"type":254,"tag":255,"props":48204,"children":48205},{},[48206],{"type":254,"tag":1161,"props":48207,"children":48211},{"alt":48208,"className":48209,"src":48210},"Azure CLI script in vscode.",[1165,1166],"/posts/images/pulumiazurebackend_vscode_1.png",[],{"type":254,"tag":427,"props":48213,"children":48214},{"icon":429},[48215],{"type":254,"tag":255,"props":48216,"children":48217},{},[48218,48220,48225],{"type":259,"value":48219},"If you are not familiar with Azure CLI, you can check my article \"",{"type":254,"tag":262,"props":48221,"children":48223},{"href":17271,"rel":48222},[266],[48224],{"type":259,"value":15},{"type":259,"value":23774},{"type":254,"tag":255,"props":48227,"children":48228},{},[48229],{"type":259,"value":48230},"Let's first define a few environment variables: the name of the resource group that will contain our storage account, its location, and the name of the storage account (I am using PowerShell but don't forget to change the syntax if you are using another shell like bash).",{"type":254,"tag":612,"props":48232,"children":48234},{"className":10656,"code":48233,"language":10658,"meta":205,"style":205},"$random=Get-Random -Maximum 1000\n$location=\"West Europe\"\n$rgName=\"rg-iacstate-westeu-$random\"\n$saName=\"stiacstate$random\"\n",[48235],{"type":254,"tag":389,"props":48236,"children":48237},{"__ignoreMap":205},[48238,48272,48301,48338],{"type":254,"tag":622,"props":48239,"children":48240},{"class":624,"line":625},[48241,48245,48249,48253,48258,48262,48267],{"type":254,"tag":622,"props":48242,"children":48243},{"style":629},[48244],{"type":259,"value":16922},{"type":254,"tag":622,"props":48246,"children":48247},{"style":1202},[48248],{"type":259,"value":14785},{"type":254,"tag":622,"props":48250,"children":48251},{"style":629},[48252],{"type":259,"value":1654},{"type":254,"tag":622,"props":48254,"children":48255},{"style":1735},[48256],{"type":259,"value":48257},"Get-Random",{"type":254,"tag":622,"props":48259,"children":48260},{"style":629},[48261],{"type":259,"value":10733},{"type":254,"tag":622,"props":48263,"children":48264},{"style":1202},[48265],{"type":259,"value":48266},"Maximum ",{"type":254,"tag":622,"props":48268,"children":48269},{"style":741},[48270],{"type":259,"value":48271},"1000\n",{"type":254,"tag":622,"props":48273,"children":48274},{"class":624,"line":635},[48275,48279,48284,48288,48292,48297],{"type":254,"tag":622,"props":48276,"children":48277},{"style":629},[48278],{"type":259,"value":16922},{"type":254,"tag":622,"props":48280,"children":48281},{"style":1202},[48282],{"type":259,"value":48283},"location",{"type":254,"tag":622,"props":48285,"children":48286},{"style":629},[48287],{"type":259,"value":1654},{"type":254,"tag":622,"props":48289,"children":48290},{"style":629},[48291],{"type":259,"value":652},{"type":254,"tag":622,"props":48293,"children":48294},{"style":664},[48295],{"type":259,"value":48296},"West Europe",{"type":254,"tag":622,"props":48298,"children":48299},{"style":629},[48300],{"type":259,"value":9909},{"type":254,"tag":622,"props":48302,"children":48303},{"class":624,"line":679},[48304,48308,48313,48317,48321,48326,48330,48334],{"type":254,"tag":622,"props":48305,"children":48306},{"style":629},[48307],{"type":259,"value":16922},{"type":254,"tag":622,"props":48309,"children":48310},{"style":1202},[48311],{"type":259,"value":48312},"rgName",{"type":254,"tag":622,"props":48314,"children":48315},{"style":629},[48316],{"type":259,"value":1654},{"type":254,"tag":622,"props":48318,"children":48319},{"style":629},[48320],{"type":259,"value":652},{"type":254,"tag":622,"props":48322,"children":48323},{"style":664},[48324],{"type":259,"value":48325},"rg-iacstate-westeu-",{"type":254,"tag":622,"props":48327,"children":48328},{"style":629},[48329],{"type":259,"value":16922},{"type":254,"tag":622,"props":48331,"children":48332},{"style":1202},[48333],{"type":259,"value":14785},{"type":254,"tag":622,"props":48335,"children":48336},{"style":629},[48337],{"type":259,"value":9909},{"type":254,"tag":622,"props":48339,"children":48340},{"class":624,"line":705},[48341,48345,48350,48354,48358,48363,48367,48371],{"type":254,"tag":622,"props":48342,"children":48343},{"style":629},[48344],{"type":259,"value":16922},{"type":254,"tag":622,"props":48346,"children":48347},{"style":1202},[48348],{"type":259,"value":48349},"saName",{"type":254,"tag":622,"props":48351,"children":48352},{"style":629},[48353],{"type":259,"value":1654},{"type":254,"tag":622,"props":48355,"children":48356},{"style":629},[48357],{"type":259,"value":652},{"type":254,"tag":622,"props":48359,"children":48360},{"style":664},[48361],{"type":259,"value":48362},"stiacstate",{"type":254,"tag":622,"props":48364,"children":48365},{"style":629},[48366],{"type":259,"value":16922},{"type":254,"tag":622,"props":48368,"children":48369},{"style":1202},[48370],{"type":259,"value":14785},{"type":254,"tag":622,"props":48372,"children":48373},{"style":629},[48374],{"type":259,"value":9909},{"type":254,"tag":255,"props":48376,"children":48377},{},[48378],{"type":259,"value":48379},"Then let's create our resource group and our storage account:",{"type":254,"tag":612,"props":48381,"children":48383},{"className":16836,"code":48382,"language":16838,"meta":205,"style":205},"az group create -n $rgName -l $location\naz storage account create -g $rgName -n $saName -l $location --sku Standard_LRS\n",[48384],{"type":254,"tag":389,"props":48385,"children":48386},{"__ignoreMap":205},[48387,48423],{"type":254,"tag":622,"props":48388,"children":48389},{"class":624,"line":625},[48390,48394,48399,48404,48408,48413,48418],{"type":254,"tag":622,"props":48391,"children":48392},{"style":714},[48393],{"type":259,"value":41000},{"type":254,"tag":622,"props":48395,"children":48396},{"style":664},[48397],{"type":259,"value":48398}," group",{"type":254,"tag":622,"props":48400,"children":48401},{"style":664},[48402],{"type":259,"value":48403}," create",{"type":254,"tag":622,"props":48405,"children":48406},{"style":664},[48407],{"type":259,"value":19782},{"type":254,"tag":622,"props":48409,"children":48410},{"style":1202},[48411],{"type":259,"value":48412}," $rgName ",{"type":254,"tag":622,"props":48414,"children":48415},{"style":664},[48416],{"type":259,"value":48417},"-l",{"type":254,"tag":622,"props":48419,"children":48420},{"style":1202},[48421],{"type":259,"value":48422}," $location\n",{"type":254,"tag":622,"props":48424,"children":48425},{"class":624,"line":635},[48426,48430,48435,48440,48444,48448,48452,48456,48461,48465,48470,48475],{"type":254,"tag":622,"props":48427,"children":48428},{"style":714},[48429],{"type":259,"value":41000},{"type":254,"tag":622,"props":48431,"children":48432},{"style":664},[48433],{"type":259,"value":48434}," storage",{"type":254,"tag":622,"props":48436,"children":48437},{"style":664},[48438],{"type":259,"value":48439}," account",{"type":254,"tag":622,"props":48441,"children":48442},{"style":664},[48443],{"type":259,"value":48403},{"type":254,"tag":622,"props":48445,"children":48446},{"style":664},[48447],{"type":259,"value":32126},{"type":254,"tag":622,"props":48449,"children":48450},{"style":1202},[48451],{"type":259,"value":48412},{"type":254,"tag":622,"props":48453,"children":48454},{"style":664},[48455],{"type":259,"value":19849},{"type":254,"tag":622,"props":48457,"children":48458},{"style":1202},[48459],{"type":259,"value":48460}," $saName ",{"type":254,"tag":622,"props":48462,"children":48463},{"style":664},[48464],{"type":259,"value":48417},{"type":254,"tag":622,"props":48466,"children":48467},{"style":1202},[48468],{"type":259,"value":48469}," $location ",{"type":254,"tag":622,"props":48471,"children":48472},{"style":664},[48473],{"type":259,"value":48474},"--sku",{"type":254,"tag":622,"props":48476,"children":48477},{"style":664},[48478],{"type":259,"value":48479}," Standard_LRS\n",{"type":254,"tag":255,"props":48481,"children":48482},{},[48483],{"type":259,"value":48484},"The key to access the storage account can be retrieved with the following command:",{"type":254,"tag":612,"props":48486,"children":48488},{"className":16836,"code":48487,"language":16838,"meta":205,"style":205},"az storage account keys list --account-name $saName -g $rgName -o tsv --query '[0].value'\n",[48489],{"type":254,"tag":389,"props":48490,"children":48491},{"__ignoreMap":205},[48492],{"type":254,"tag":622,"props":48493,"children":48494},{"class":624,"line":625},[48495,48499,48503,48507,48512,48516,48521,48525,48529,48533,48538,48543,48548,48552,48557],{"type":254,"tag":622,"props":48496,"children":48497},{"style":714},[48498],{"type":259,"value":41000},{"type":254,"tag":622,"props":48500,"children":48501},{"style":664},[48502],{"type":259,"value":48434},{"type":254,"tag":622,"props":48504,"children":48505},{"style":664},[48506],{"type":259,"value":48439},{"type":254,"tag":622,"props":48508,"children":48509},{"style":664},[48510],{"type":259,"value":48511}," keys",{"type":254,"tag":622,"props":48513,"children":48514},{"style":664},[48515],{"type":259,"value":40934},{"type":254,"tag":622,"props":48517,"children":48518},{"style":664},[48519],{"type":259,"value":48520}," --account-name",{"type":254,"tag":622,"props":48522,"children":48523},{"style":1202},[48524],{"type":259,"value":48460},{"type":254,"tag":622,"props":48526,"children":48527},{"style":664},[48528],{"type":259,"value":24273},{"type":254,"tag":622,"props":48530,"children":48531},{"style":1202},[48532],{"type":259,"value":48412},{"type":254,"tag":622,"props":48534,"children":48535},{"style":664},[48536],{"type":259,"value":48537},"-o",{"type":254,"tag":622,"props":48539,"children":48540},{"style":664},[48541],{"type":259,"value":48542}," tsv",{"type":254,"tag":622,"props":48544,"children":48545},{"style":664},[48546],{"type":259,"value":48547}," --query",{"type":254,"tag":622,"props":48549,"children":48550},{"style":629},[48551],{"type":259,"value":16556},{"type":254,"tag":622,"props":48553,"children":48554},{"style":664},[48555],{"type":259,"value":48556},"[0].value",{"type":254,"tag":622,"props":48558,"children":48559},{"style":629},[48560],{"type":259,"value":16566},{"type":254,"tag":255,"props":48562,"children":48563},{},[48564],{"type":259,"value":48565},"Using this command, we can now set the environment variables that will be used by the Pulumi CLI to access our Azure Blob Storage account backend:",{"type":254,"tag":612,"props":48567,"children":48569},{"className":10656,"code":48568,"language":10658,"meta":205,"style":205},"$env:AZURE_STORAGE_KEY=$(az storage account keys list -n $saName -g $rgName -o tsv --query '[0].value')\n$env:AZURE_STORAGE_ACCOUNT=$saName\n",[48570],{"type":254,"tag":389,"props":48571,"children":48572},{"__ignoreMap":205},[48573,48663],{"type":254,"tag":622,"props":48574,"children":48575},{"class":624,"line":625},[48576,48580,48585,48589,48594,48598,48603,48607,48612,48616,48621,48625,48630,48634,48639,48643,48647,48651,48655,48659],{"type":254,"tag":622,"props":48577,"children":48578},{"style":629},[48579],{"type":259,"value":16922},{"type":254,"tag":622,"props":48581,"children":48582},{"style":1202},[48583],{"type":259,"value":48584},"env:AZURE_STORAGE_KEY",{"type":254,"tag":622,"props":48586,"children":48587},{"style":629},[48588],{"type":259,"value":17046},{"type":254,"tag":622,"props":48590,"children":48591},{"style":1202},[48592],{"type":259,"value":48593},"az storage account keys list ",{"type":254,"tag":622,"props":48595,"children":48596},{"style":629},[48597],{"type":259,"value":17065},{"type":254,"tag":622,"props":48599,"children":48600},{"style":1202},[48601],{"type":259,"value":48602},"n ",{"type":254,"tag":622,"props":48604,"children":48605},{"style":629},[48606],{"type":259,"value":16922},{"type":254,"tag":622,"props":48608,"children":48609},{"style":1202},[48610],{"type":259,"value":48611},"saName ",{"type":254,"tag":622,"props":48613,"children":48614},{"style":629},[48615],{"type":259,"value":17065},{"type":254,"tag":622,"props":48617,"children":48618},{"style":1202},[48619],{"type":259,"value":48620},"g ",{"type":254,"tag":622,"props":48622,"children":48623},{"style":629},[48624],{"type":259,"value":16922},{"type":254,"tag":622,"props":48626,"children":48627},{"style":1202},[48628],{"type":259,"value":48629},"rgName ",{"type":254,"tag":622,"props":48631,"children":48632},{"style":629},[48633],{"type":259,"value":17065},{"type":254,"tag":622,"props":48635,"children":48636},{"style":1202},[48637],{"type":259,"value":48638},"o tsv ",{"type":254,"tag":622,"props":48640,"children":48641},{"style":629},[48642],{"type":259,"value":10675},{"type":254,"tag":622,"props":48644,"children":48645},{"style":1202},[48646],{"type":259,"value":17166},{"type":254,"tag":622,"props":48648,"children":48649},{"style":629},[48650],{"type":259,"value":46169},{"type":254,"tag":622,"props":48652,"children":48653},{"style":664},[48654],{"type":259,"value":48556},{"type":254,"tag":622,"props":48656,"children":48657},{"style":629},[48658],{"type":259,"value":46169},{"type":254,"tag":622,"props":48660,"children":48661},{"style":629},[48662],{"type":259,"value":1685},{"type":254,"tag":622,"props":48664,"children":48665},{"class":624,"line":635},[48666,48670,48675,48680],{"type":254,"tag":622,"props":48667,"children":48668},{"style":629},[48669],{"type":259,"value":16922},{"type":254,"tag":622,"props":48671,"children":48672},{"style":1202},[48673],{"type":259,"value":48674},"env:AZURE_STORAGE_ACCOUNT",{"type":254,"tag":622,"props":48676,"children":48677},{"style":629},[48678],{"type":259,"value":48679},"=$",{"type":254,"tag":622,"props":48681,"children":48682},{"style":1202},[48683],{"type":259,"value":48684},"saName\n",{"type":254,"tag":255,"props":48686,"children":48687},{},[48688],{"type":259,"value":48689},"And finally, we can create the blob container that will contain the infrastructure state:",{"type":254,"tag":612,"props":48691,"children":48693},{"className":16836,"code":48692,"language":16838,"meta":205,"style":205},"az storage container create -n iacstate\n",[48694],{"type":254,"tag":389,"props":48695,"children":48696},{"__ignoreMap":205},[48697],{"type":254,"tag":622,"props":48698,"children":48699},{"class":624,"line":625},[48700,48704,48708,48713,48717,48721],{"type":254,"tag":622,"props":48701,"children":48702},{"style":714},[48703],{"type":259,"value":41000},{"type":254,"tag":622,"props":48705,"children":48706},{"style":664},[48707],{"type":259,"value":48434},{"type":254,"tag":622,"props":48709,"children":48710},{"style":664},[48711],{"type":259,"value":48712}," container",{"type":254,"tag":622,"props":48714,"children":48715},{"style":664},[48716],{"type":259,"value":48403},{"type":254,"tag":622,"props":48718,"children":48719},{"style":664},[48720],{"type":259,"value":19782},{"type":254,"tag":622,"props":48722,"children":48723},{"style":664},[48724],{"type":259,"value":48725}," iacstate\n",{"type":254,"tag":455,"props":48727,"children":48729},{"id":48728},"how-to-provision-your-project-infrastructure-using-the-azure-blob-storage-backend",[48730],{"type":259,"value":48731},"How to provision your project infrastructure using the Azure Blob Storage backend?",{"type":254,"tag":255,"props":48733,"children":48734},{},[48735],{"type":259,"value":48736},"Now that our blob container exists, we can use the pulumi login command we already talked about to indicate pulumi to use the newly created azure blob storage as the backend.",{"type":254,"tag":612,"props":48738,"children":48740},{"className":10656,"code":48739,"language":10658,"meta":205,"style":205},"pulumi login azblob://iacstate\n",[48741],{"type":254,"tag":389,"props":48742,"children":48743},{"__ignoreMap":205},[48744],{"type":254,"tag":622,"props":48745,"children":48746},{"class":624,"line":625},[48747,48752,48756],{"type":254,"tag":622,"props":48748,"children":48749},{"style":1202},[48750],{"type":259,"value":48751},"pulumi login azblob:",{"type":254,"tag":622,"props":48753,"children":48754},{"style":629},[48755],{"type":259,"value":38393},{"type":254,"tag":622,"props":48757,"children":48758},{"style":1202},[48759],{"type":259,"value":48760},"iacstate\n",{"type":254,"tag":255,"props":48762,"children":48763},{},[48764,48766,48772,48774,48779],{"type":259,"value":48765},"To verify Pulumi can correctly provision cloud resources using our Azure Blob Storage backend, we can create a new Pulumi project using the ",{"type":254,"tag":389,"props":48767,"children":48769},{"className":48768},[],[48770],{"type":259,"value":48771},"azure-csharp",{"type":259,"value":48773}," template and deploy the infrastructure with the ",{"type":254,"tag":389,"props":48775,"children":48777},{"className":48776},[],[48778],{"type":259,"value":23629},{"type":259,"value":48780}," command:",{"type":254,"tag":612,"props":48782,"children":48784},{"className":10656,"code":48783,"language":10658,"meta":205,"style":205},"mkdir infra;cd infra;\npulumi new azure-csharp -n AzureStorageBackend -s dev -y\npulumi up -y\n",[48785],{"type":254,"tag":389,"props":48786,"children":48787},{"__ignoreMap":205},[48788,48809,48852],{"type":254,"tag":622,"props":48789,"children":48790},{"class":624,"line":625},[48791,48796,48800,48805],{"type":254,"tag":622,"props":48792,"children":48793},{"style":1202},[48794],{"type":259,"value":48795},"mkdir infra",{"type":254,"tag":622,"props":48797,"children":48798},{"style":629},[48799],{"type":259,"value":2000},{"type":254,"tag":622,"props":48801,"children":48802},{"style":1202},[48803],{"type":259,"value":48804},"cd infra",{"type":254,"tag":622,"props":48806,"children":48807},{"style":629},[48808],{"type":259,"value":2350},{"type":254,"tag":622,"props":48810,"children":48811},{"class":624,"line":635},[48812,48817,48821,48826,48830,48835,48839,48843,48847],{"type":254,"tag":622,"props":48813,"children":48814},{"style":1202},[48815],{"type":259,"value":48816},"pulumi new azure",{"type":254,"tag":622,"props":48818,"children":48819},{"style":629},[48820],{"type":259,"value":17065},{"type":254,"tag":622,"props":48822,"children":48823},{"style":1202},[48824],{"type":259,"value":48825},"csharp ",{"type":254,"tag":622,"props":48827,"children":48828},{"style":629},[48829],{"type":259,"value":17065},{"type":254,"tag":622,"props":48831,"children":48832},{"style":1202},[48833],{"type":259,"value":48834},"n AzureStorageBackend ",{"type":254,"tag":622,"props":48836,"children":48837},{"style":629},[48838],{"type":259,"value":17065},{"type":254,"tag":622,"props":48840,"children":48841},{"style":1202},[48842],{"type":259,"value":24174},{"type":254,"tag":622,"props":48844,"children":48845},{"style":629},[48846],{"type":259,"value":17065},{"type":254,"tag":622,"props":48848,"children":48849},{"style":1202},[48850],{"type":259,"value":48851},"y\n",{"type":254,"tag":622,"props":48853,"children":48854},{"class":624,"line":679},[48855,48860,48864],{"type":254,"tag":622,"props":48856,"children":48857},{"style":1202},[48858],{"type":259,"value":48859},"pulumi up ",{"type":254,"tag":622,"props":48861,"children":48862},{"style":629},[48863],{"type":259,"value":17065},{"type":254,"tag":622,"props":48865,"children":48866},{"style":1202},[48867],{"type":259,"value":48851},{"type":254,"tag":255,"props":48869,"children":48870},{},[48871],{"type":259,"value":48872},"When executing these commands, Pulumi will ask us to provide a passphrase. Why is that? It is to encrypt secrets contained in the infrastructure state. This way no secret is stored in plain text in the state.",{"type":254,"tag":255,"props":48874,"children":48875},{},[48876,48878,48883,48885,48891],{"type":259,"value":48877},"Once the ",{"type":254,"tag":389,"props":48879,"children":48881},{"className":48880},[],[48882],{"type":259,"value":23629},{"type":259,"value":48884}," command is finished, the infrastructure requested is provisioned, and we can see a new state file has been created in the ",{"type":254,"tag":389,"props":48886,"children":48888},{"className":48887},[],[48889],{"type":259,"value":48890},"iacstate",{"type":259,"value":48892}," blob container.",{"type":254,"tag":255,"props":48894,"children":48895},{},[48896],{"type":254,"tag":1161,"props":48897,"children":48901},{"alt":48898,"className":48899,"src":48900},"Azure Blob container explorer showing the Pulumi state file.",[1165,1166],"/posts/images/pulumiazurebackend_azure_1.png",[],{"type":254,"tag":282,"props":48903,"children":48905},{"id":48904},"managing-state-sensitive-data",[48906],{"type":259,"value":48907},"Managing state sensitive data",{"type":254,"tag":455,"props":48909,"children":48911},{"id":48910},"why-is-it-needed-to-protect-sensitive-data-in-the-state",[48912],{"type":259,"value":48913},"Why is it needed to protect sensitive data in the state?",{"type":254,"tag":255,"props":48915,"children":48916},{},[48917],{"type":259,"value":48918},"The state is transmitted and stored securely by Pulumi and whatever the backend you use you should restrict its access. For instance, in our example, you should have assigned the permissions on the storage account so that only the right people have access to it. Nevertheless, securing the state file is not enough because it contains sensitive data (keys, connection strings, ...) that you probably don't want anyone that has access to the file to be able to get.",{"type":254,"tag":255,"props":48920,"children":48921},{},[48922],{"type":259,"value":48923},"Indeed, it's not because a developer needs to read the state file to debug an issue that you want him to be able to see some production sensitive data in plain text in the state.  Having secrets in plain text in a state file would be like putting secrets in your source control and telling it is safe because only developers of the project team have access to it. Moreover, even if an unauthorized person succeeds to get access to the state file, it won't be an issue if all secrets in it are encrypted. Hence that is very nice to see Pulumi take security seriously and always encrypt sensitive information.",{"type":254,"tag":455,"props":48925,"children":48927},{"id":48926},"what-are-the-available-encryption-providers",[48928],{"type":259,"value":48929},"What are the available encryption providers?",{"type":254,"tag":255,"props":48931,"children":48932},{},[48933],{"type":259,"value":48934},"As we have seen previously, when using a self-managed backend like Azure Blob Storage, by default Pulumi uses a passphrase to encrypt sensitive data.",{"type":254,"tag":255,"props":48936,"children":48937},{},[48938],{"type":259,"value":48939},"The passphrase is just one of the supported encryption/secrets providers but there are others:",{"type":254,"tag":332,"props":48941,"children":48942},{},[48943,48948,48952,48957,48962],{"type":254,"tag":336,"props":48944,"children":48945},{},[48946],{"type":259,"value":48947},"AWS Key Management Service",{"type":254,"tag":336,"props":48949,"children":48950},{},[48951],{"type":259,"value":40204},{"type":254,"tag":336,"props":48953,"children":48954},{},[48955],{"type":259,"value":48956},"Google Cloud Key Management Service",{"type":254,"tag":336,"props":48958,"children":48959},{},[48960],{"type":259,"value":48961},"HashiCorp Vault Transit Secrets Engine",{"type":254,"tag":336,"props":48963,"children":48964},{},[48965],{"type":259,"value":48966},"Pulumi Service (used by default when using Pulumi Service as the backend)",{"type":254,"tag":255,"props":48968,"children":48969},{},[48970],{"type":259,"value":48971},"As for the backend, you don't have to use the default encryption provider and can come with your own resource. These providers can be used whatever the backend you chose, which lets you many possibilities. Now let's see how to use Azure Key Vault as our encryption provider.",{"type":254,"tag":455,"props":48973,"children":48975},{"id":48974},"how-to-use-azure-key-vault-as-the-encryption-provider",[48976],{"type":259,"value":48977},"How to use Azure Key Vault as the encryption provider?",{"type":254,"tag":255,"props":48979,"children":48980},{},[48981],{"type":259,"value":48982},"Let's first create a Key Vault:",{"type":254,"tag":612,"props":48984,"children":48986},{"className":10656,"code":48985,"language":10658,"meta":205,"style":205},"$kvName=\"kv-iacstate-westeu-$random\"\n$vaultId=az keyvault create -g $rgName -n $kvName --enable-rbac-authorization true --query \"id\"\n",[48987],{"type":254,"tag":389,"props":48988,"children":48989},{"__ignoreMap":205},[48990,49027],{"type":254,"tag":622,"props":48991,"children":48992},{"class":624,"line":625},[48993,48997,49002,49006,49010,49015,49019,49023],{"type":254,"tag":622,"props":48994,"children":48995},{"style":629},[48996],{"type":259,"value":16922},{"type":254,"tag":622,"props":48998,"children":48999},{"style":1202},[49000],{"type":259,"value":49001},"kvName",{"type":254,"tag":622,"props":49003,"children":49004},{"style":629},[49005],{"type":259,"value":1654},{"type":254,"tag":622,"props":49007,"children":49008},{"style":629},[49009],{"type":259,"value":652},{"type":254,"tag":622,"props":49011,"children":49012},{"style":664},[49013],{"type":259,"value":49014},"kv-iacstate-westeu-",{"type":254,"tag":622,"props":49016,"children":49017},{"style":629},[49018],{"type":259,"value":16922},{"type":254,"tag":622,"props":49020,"children":49021},{"style":1202},[49022],{"type":259,"value":14785},{"type":254,"tag":622,"props":49024,"children":49025},{"style":629},[49026],{"type":259,"value":9909},{"type":254,"tag":622,"props":49028,"children":49029},{"class":624,"line":635},[49030,49034,49039,49043,49048,49052,49056,49060,49064,49068,49072,49076,49081,49085,49090,49094,49099,49103,49107,49111,49115],{"type":254,"tag":622,"props":49031,"children":49032},{"style":629},[49033],{"type":259,"value":16922},{"type":254,"tag":622,"props":49035,"children":49036},{"style":1202},[49037],{"type":259,"value":49038},"vaultId",{"type":254,"tag":622,"props":49040,"children":49041},{"style":629},[49042],{"type":259,"value":1654},{"type":254,"tag":622,"props":49044,"children":49045},{"style":1202},[49046],{"type":259,"value":49047},"az keyvault create ",{"type":254,"tag":622,"props":49049,"children":49050},{"style":629},[49051],{"type":259,"value":17065},{"type":254,"tag":622,"props":49053,"children":49054},{"style":1202},[49055],{"type":259,"value":48620},{"type":254,"tag":622,"props":49057,"children":49058},{"style":629},[49059],{"type":259,"value":16922},{"type":254,"tag":622,"props":49061,"children":49062},{"style":1202},[49063],{"type":259,"value":48629},{"type":254,"tag":622,"props":49065,"children":49066},{"style":629},[49067],{"type":259,"value":17065},{"type":254,"tag":622,"props":49069,"children":49070},{"style":1202},[49071],{"type":259,"value":48602},{"type":254,"tag":622,"props":49073,"children":49074},{"style":629},[49075],{"type":259,"value":16922},{"type":254,"tag":622,"props":49077,"children":49078},{"style":1202},[49079],{"type":259,"value":49080},"kvName ",{"type":254,"tag":622,"props":49082,"children":49083},{"style":629},[49084],{"type":259,"value":10675},{"type":254,"tag":622,"props":49086,"children":49087},{"style":1735},[49088],{"type":259,"value":49089},"enable-rbac",{"type":254,"tag":622,"props":49091,"children":49092},{"style":629},[49093],{"type":259,"value":17065},{"type":254,"tag":622,"props":49095,"children":49096},{"style":1202},[49097],{"type":259,"value":49098},"authorization true ",{"type":254,"tag":622,"props":49100,"children":49101},{"style":629},[49102],{"type":259,"value":10675},{"type":254,"tag":622,"props":49104,"children":49105},{"style":1202},[49106],{"type":259,"value":17166},{"type":254,"tag":622,"props":49108,"children":49109},{"style":629},[49110],{"type":259,"value":652},{"type":254,"tag":622,"props":49112,"children":49113},{"style":664},[49114],{"type":259,"value":17175},{"type":254,"tag":622,"props":49116,"children":49117},{"style":629},[49118],{"type":259,"value":9909},{"type":254,"tag":255,"props":49120,"children":49121},{},[49122,49124,49130],{"type":259,"value":49123},"We retrieve its id so that we can use it to assign the correct role to my user to be able to perform cryptographic operations. With the ",{"type":254,"tag":389,"props":49125,"children":49127},{"className":49126},[],[49128],{"type":259,"value":49129},"--enable-rbac-authorization",{"type":259,"value":49131}," parameter we set the permissions model on the key vault to Role-Based Access Control but you can use the classic Vault access policies as well. I prefer using RBAC because I think it's more modern and more consistent with how we manage permissions on other Azure resources.",{"type":254,"tag":255,"props":49133,"children":49134},{},[49135],{"type":259,"value":49136},"To assign the appropriate permission to the current logged-in user, we will need its current identifier in Azure that we can retrieve with the following command:",{"type":254,"tag":612,"props":49138,"children":49140},{"className":10656,"code":49139,"language":10658,"meta":205,"style":205},"$myUserId=az ad signed-in-user show --query \"objectId\" -o tsv \n",[49141],{"type":254,"tag":389,"props":49142,"children":49143},{"__ignoreMap":205},[49144],{"type":254,"tag":622,"props":49145,"children":49146},{"class":624,"line":625},[49147,49151,49156,49160,49165,49169,49174,49178,49183,49187,49191,49195,49199,49203,49207],{"type":254,"tag":622,"props":49148,"children":49149},{"style":629},[49150],{"type":259,"value":16922},{"type":254,"tag":622,"props":49152,"children":49153},{"style":1202},[49154],{"type":259,"value":49155},"myUserId",{"type":254,"tag":622,"props":49157,"children":49158},{"style":629},[49159],{"type":259,"value":1654},{"type":254,"tag":622,"props":49161,"children":49162},{"style":1202},[49163],{"type":259,"value":49164},"az ad signed",{"type":254,"tag":622,"props":49166,"children":49167},{"style":629},[49168],{"type":259,"value":17065},{"type":254,"tag":622,"props":49170,"children":49171},{"style":1202},[49172],{"type":259,"value":49173},"in",{"type":254,"tag":622,"props":49175,"children":49176},{"style":629},[49177],{"type":259,"value":17065},{"type":254,"tag":622,"props":49179,"children":49180},{"style":1202},[49181],{"type":259,"value":49182},"user show ",{"type":254,"tag":622,"props":49184,"children":49185},{"style":629},[49186],{"type":259,"value":10675},{"type":254,"tag":622,"props":49188,"children":49189},{"style":1202},[49190],{"type":259,"value":17166},{"type":254,"tag":622,"props":49192,"children":49193},{"style":629},[49194],{"type":259,"value":652},{"type":254,"tag":622,"props":49196,"children":49197},{"style":664},[49198],{"type":259,"value":25120},{"type":254,"tag":622,"props":49200,"children":49201},{"style":629},[49202],{"type":259,"value":652},{"type":254,"tag":622,"props":49204,"children":49205},{"style":629},[49206],{"type":259,"value":10733},{"type":254,"tag":622,"props":49208,"children":49209},{"style":1202},[49210],{"type":259,"value":49211},"o tsv\n",{"type":254,"tag":255,"props":49213,"children":49214},{},[49215,49217,49223],{"type":259,"value":49216},"We can then assign to this user the ",{"type":254,"tag":389,"props":49218,"children":49220},{"className":49219},[],[49221],{"type":259,"value":49222},"Key Vault Crypto Officer",{"type":259,"value":49224}," role that will allow us to create a key and encrypt/decrypt data.",{"type":254,"tag":612,"props":49226,"children":49228},{"className":10656,"code":49227,"language":10658,"meta":205,"style":205},"az role assignment create --scope $vaultId --role \"Key Vault Crypto Officer\" --assignee $myUserId \n",[49229],{"type":254,"tag":389,"props":49230,"children":49231},{"__ignoreMap":205},[49232],{"type":254,"tag":622,"props":49233,"children":49234},{"class":624,"line":625},[49235,49239,49243,49247,49251,49256,49260,49265,49269,49273,49277,49281,49286,49290],{"type":254,"tag":622,"props":49236,"children":49237},{"style":1202},[49238],{"type":259,"value":17471},{"type":254,"tag":622,"props":49240,"children":49241},{"style":629},[49242],{"type":259,"value":10675},{"type":254,"tag":622,"props":49244,"children":49245},{"style":1202},[49246],{"type":259,"value":17569},{"type":254,"tag":622,"props":49248,"children":49249},{"style":629},[49250],{"type":259,"value":16922},{"type":254,"tag":622,"props":49252,"children":49253},{"style":1202},[49254],{"type":259,"value":49255},"vaultId ",{"type":254,"tag":622,"props":49257,"children":49258},{"style":629},[49259],{"type":259,"value":10675},{"type":254,"tag":622,"props":49261,"children":49262},{"style":1202},[49263],{"type":259,"value":49264},"role ",{"type":254,"tag":622,"props":49266,"children":49267},{"style":629},[49268],{"type":259,"value":652},{"type":254,"tag":622,"props":49270,"children":49271},{"style":664},[49272],{"type":259,"value":49222},{"type":254,"tag":622,"props":49274,"children":49275},{"style":629},[49276],{"type":259,"value":652},{"type":254,"tag":622,"props":49278,"children":49279},{"style":629},[49280],{"type":259,"value":16988},{"type":254,"tag":622,"props":49282,"children":49283},{"style":1202},[49284],{"type":259,"value":49285},"assignee ",{"type":254,"tag":622,"props":49287,"children":49288},{"style":629},[49289],{"type":259,"value":16922},{"type":254,"tag":622,"props":49291,"children":49292},{"style":1202},[49293],{"type":259,"value":49294},"myUserId\n",{"type":254,"tag":255,"props":49296,"children":49297},{},[49298],{"type":259,"value":49299},"The key to encrypt/decrypt data can be created with the following command:",{"type":254,"tag":612,"props":49301,"children":49303},{"className":10656,"code":49302,"language":10658,"meta":205,"style":205},"az keyvault key create -n encryptionState --vault-name $kvName\n",[49304],{"type":254,"tag":389,"props":49305,"children":49306},{"__ignoreMap":205},[49307],{"type":254,"tag":622,"props":49308,"children":49309},{"class":624,"line":625},[49310,49315,49319,49324,49328,49333,49337,49341,49345],{"type":254,"tag":622,"props":49311,"children":49312},{"style":1202},[49313],{"type":259,"value":49314},"az keyvault key create ",{"type":254,"tag":622,"props":49316,"children":49317},{"style":629},[49318],{"type":259,"value":17065},{"type":254,"tag":622,"props":49320,"children":49321},{"style":1202},[49322],{"type":259,"value":49323},"n encryptionState ",{"type":254,"tag":622,"props":49325,"children":49326},{"style":629},[49327],{"type":259,"value":10675},{"type":254,"tag":622,"props":49329,"children":49330},{"style":1202},[49331],{"type":259,"value":49332},"vault",{"type":254,"tag":622,"props":49334,"children":49335},{"style":629},[49336],{"type":259,"value":17065},{"type":254,"tag":622,"props":49338,"children":49339},{"style":1202},[49340],{"type":259,"value":17326},{"type":254,"tag":622,"props":49342,"children":49343},{"style":629},[49344],{"type":259,"value":16922},{"type":254,"tag":622,"props":49346,"children":49347},{"style":1202},[49348],{"type":259,"value":49349},"kvName\n",{"type":254,"tag":255,"props":49351,"children":49352},{},[49353],{"type":259,"value":49354},"By default, Pulumi CLI will try to use environment variables to authenticate to the key vault, so we need to tell it to use the Azure CLI instead as we gave the permission on the key vault to the user currently logged in:",{"type":254,"tag":612,"props":49356,"children":49358},{"className":10656,"code":49357,"language":10658,"meta":205,"style":205},"$env:AZURE_KEYVAULT_AUTH_VIA_CLI=\"true\"\n",[49359],{"type":254,"tag":389,"props":49360,"children":49361},{"__ignoreMap":205},[49362],{"type":254,"tag":622,"props":49363,"children":49364},{"class":624,"line":625},[49365,49369,49374,49378,49382,49386],{"type":254,"tag":622,"props":49366,"children":49367},{"style":629},[49368],{"type":259,"value":16922},{"type":254,"tag":622,"props":49370,"children":49371},{"style":1202},[49372],{"type":259,"value":49373},"env:AZURE_KEYVAULT_AUTH_VIA_CLI",{"type":254,"tag":622,"props":49375,"children":49376},{"style":629},[49377],{"type":259,"value":1654},{"type":254,"tag":622,"props":49379,"children":49380},{"style":629},[49381],{"type":259,"value":652},{"type":254,"tag":622,"props":49383,"children":49384},{"style":664},[49385],{"type":259,"value":24747},{"type":254,"tag":622,"props":49387,"children":49388},{"style":629},[49389],{"type":259,"value":9909},{"type":254,"tag":255,"props":49391,"children":49392},{},[49393],{"type":259,"value":49394},"Now that everything is configured, we can modify our previous command to create a new Pulumi project by specifying the encryption provider to use:",{"type":254,"tag":612,"props":49396,"children":49398},{"className":10656,"code":49397,"language":10658,"meta":205,"style":205},"pulumi new azure-csharp -n AzureStorageBackend -s dev -y --secrets-provider=\"azurekeyvault://$kvName.vault.azure.net/keys/encryptionState\"\n",[49399],{"type":254,"tag":389,"props":49400,"children":49401},{"__ignoreMap":205},[49402],{"type":254,"tag":622,"props":49403,"children":49404},{"class":624,"line":625},[49405,49409,49413,49417,49421,49425,49429,49433,49437,49442,49446,49451,49455,49459,49463,49467,49472,49476,49480,49485],{"type":254,"tag":622,"props":49406,"children":49407},{"style":1202},[49408],{"type":259,"value":48816},{"type":254,"tag":622,"props":49410,"children":49411},{"style":629},[49412],{"type":259,"value":17065},{"type":254,"tag":622,"props":49414,"children":49415},{"style":1202},[49416],{"type":259,"value":48825},{"type":254,"tag":622,"props":49418,"children":49419},{"style":629},[49420],{"type":259,"value":17065},{"type":254,"tag":622,"props":49422,"children":49423},{"style":1202},[49424],{"type":259,"value":48834},{"type":254,"tag":622,"props":49426,"children":49427},{"style":629},[49428],{"type":259,"value":17065},{"type":254,"tag":622,"props":49430,"children":49431},{"style":1202},[49432],{"type":259,"value":24174},{"type":254,"tag":622,"props":49434,"children":49435},{"style":629},[49436],{"type":259,"value":17065},{"type":254,"tag":622,"props":49438,"children":49439},{"style":1202},[49440],{"type":259,"value":49441},"y ",{"type":254,"tag":622,"props":49443,"children":49444},{"style":629},[49445],{"type":259,"value":10675},{"type":254,"tag":622,"props":49447,"children":49448},{"style":1202},[49449],{"type":259,"value":49450},"secrets",{"type":254,"tag":622,"props":49452,"children":49453},{"style":629},[49454],{"type":259,"value":17065},{"type":254,"tag":622,"props":49456,"children":49457},{"style":1202},[49458],{"type":259,"value":15928},{"type":254,"tag":622,"props":49460,"children":49461},{"style":629},[49462],{"type":259,"value":1654},{"type":254,"tag":622,"props":49464,"children":49465},{"style":629},[49466],{"type":259,"value":652},{"type":254,"tag":622,"props":49468,"children":49469},{"style":664},[49470],{"type":259,"value":49471},"azurekeyvault://",{"type":254,"tag":622,"props":49473,"children":49474},{"style":629},[49475],{"type":259,"value":16922},{"type":254,"tag":622,"props":49477,"children":49478},{"style":1202},[49479],{"type":259,"value":49001},{"type":254,"tag":622,"props":49481,"children":49482},{"style":664},[49483],{"type":259,"value":49484},".vault.azure.net/keys/encryptionState",{"type":254,"tag":622,"props":49486,"children":49487},{"style":629},[49488],{"type":259,"value":9909},{"type":254,"tag":282,"props":49490,"children":49492},{"id":49491},"comparing-with-how-terraform-handle-state",[49493],{"type":259,"value":49494},"Comparing with how Terraform handle state",{"type":254,"tag":255,"props":49496,"children":49497},{},[49498],{"type":259,"value":49499},"Terraform is another very popular Infrastructure as Code platform with lots of similarities so I thought it might be interesting to look at how Terraform handles state compared to Pulumi.",{"type":254,"tag":255,"props":49501,"children":49502},{},[49503],{"type":259,"value":49504},"Terraform has a SaaS platform called Terraform Cloud that can be used to manage the infrastructure state. It is similar to what Pulumi Service offers. However, when using Terraform the default backend is not Terraform Cloud but local filesystem. That is not better or worse, just a different choice HashiCorp (the company behind Terraform) did. Although I must say that when I started working on Pulumi, I found it easier not having to take care of where the state is stored and how it is managed, so maybe a SaaS backend by default is simpler.",{"type":254,"tag":255,"props":49506,"children":49507},{},[49508,49510,49517],{"type":259,"value":49509},"On Microsoft documentation, there is a tutorial ",{"type":254,"tag":262,"props":49511,"children":49514},{"href":49512,"rel":49513},"https://docs.microsoft.com/en-us/azure/developer/terraform/store-state-in-azure-storage",[266],[49515],{"type":259,"value":49516},"\"Store Terraform state in Azure Storage\"",{"type":259,"value":49518}," that shows how to use Terraform with an Azure Storage backend. I have done it and it is very similar to what we have done in this article with Pulumi. Instead of using a CLI command to configure the infrastructure to use Azure Blob Storage as the backend for the state, in Terraform, you configure it directly in one of the code files but the idea is the same. Both IaC tools store the infrastructure state in a JSON file in a blob container.",{"type":254,"tag":255,"props":49520,"children":49521},{},[49522,49524,49531,49533,49539],{"type":259,"value":49523},"One big difference however is that by default Terraform does not encrypt sensitive information in the state file. As far as I know, there is no concept of secret providers in Terraform so no built-in solution. ",{"type":254,"tag":262,"props":49525,"children":49528},{"href":49526,"rel":49527},"https://www.terraform.io/docs/language/state/sensitive-data.html",[266],[49529],{"type":259,"value":49530},"Terraform documentation",{"type":259,"value":49532}," just says to ",{"type":254,"tag":389,"props":49534,"children":49536},{"className":49535},[],[49537],{"type":259,"value":49538},"treat the state itself as sensitive data",{"type":259,"value":49540},". That means when I created a storage account using Terraform with the Azure Blob Storage backend, the keys of my storage were available in plain text in my state file (as you can see in the image below).",{"type":254,"tag":255,"props":49542,"children":49543},{},[49544],{"type":254,"tag":1161,"props":49545,"children":49549},{"alt":49546,"className":49547,"src":49548},"Terraform state file containing clear text sensitive information in vscode.",[1165,1166],"/posts/images/pulumiazurebackend_tf_1.png",[],{"type":254,"tag":255,"props":49551,"children":49552},{},[49553],{"type":259,"value":49554},"You should not have this kind of security issue using Terraform Cloud and there are probably external tools to avoid this, but I think an IaC platform should be secure by default and that encryption of sensitive data should be built-in.",{"type":254,"tag":282,"props":49556,"children":49557},{"id":10572},[49558],{"type":259,"value":10575},{"type":254,"tag":255,"props":49560,"children":49561},{},[49562],{"type":259,"value":49563},"You can find below the complete Azure CLI script used in this article:",{"type":254,"tag":612,"props":49565,"children":49567},{"className":10656,"code":49566,"language":10658,"meta":205,"style":205},"# PowerShell variables used in the script \n$random=Get-Random -Maximum 1000\n$location=\"West Europe\"\n$rgName=\"rg-iacstate-westeu-$random\"\n$saName=\"stiacstate$random\"\n$kvName=\"kv-iacstate-westeu-$random\"\n\naz group create -n $rgName -l $location\n\n# Configure the Azure Blob Storage that will contain the state \naz storage account create -g $rgName -n $saName -l $location --sku Standard_LRS\n# Set environment variables needed to write on the storage account\n$env:AZURE_STORAGE_KEY=$(az storage account keys list -n $saName -g $rgName -o tsv --query '[0].value')\n$env:AZURE_STORAGE_ACCOUNT=$saName\naz storage container create -n iacstate\n\n# Configure the Key Vault that will be used to encrypt the sensitive data\n$vaultId=az keyvault create -g $rgName -n $kvName --enable-rbac-authorization true --query \"id\"\n$myUserId=az ad signed-in-user show --query \"objectId\" -o tsv \naz role assignment create --scope $vaultId --role \"Key Vault Crypto Officer\" --assignee $myUserId \naz keyvault key create -n encryptionState --vault-name $kvName\n# Use az cli to authenticate to key vault instead of using environment variables \n$env:AZURE_KEYVAULT_AUTH_VIA_CLI=\"true\"\n\n# Indicate pulumi to use the newly created azure blob storage as a backend\npulumi login azblob://iacstate\n# Create and use a folder to store the infrastructure code\nmkdir infra;cd infra;\n# Create a new Pulumi project using the azure blob storage as the backend and the keyvault as the encryption provider \npulumi new azure-csharp -n AzureStorageBackend -s dev -y --secrets-provider=\"azurekeyvault://$kvName.vault.azure.net/keys/encryptionState\"\n# Deploy the infrastructure\npulumi up -y\n",[49568],{"type":254,"tag":389,"props":49569,"children":49570},{"__ignoreMap":205},[49571,49579,49610,49637,49672,49707,49742,49749,49790,49797,49805,49871,49879,49962,49981,49998,50005,50013,50100,50164,50224,50263,50271,50298,50305,50313,50328,50336,50355,50363,50446,50454],{"type":254,"tag":622,"props":49572,"children":49573},{"class":624,"line":625},[49574],{"type":254,"tag":622,"props":49575,"children":49576},{"style":3195},[49577],{"type":259,"value":49578},"# PowerShell variables used in the script \n",{"type":254,"tag":622,"props":49580,"children":49581},{"class":624,"line":635},[49582,49586,49590,49594,49598,49602,49606],{"type":254,"tag":622,"props":49583,"children":49584},{"style":629},[49585],{"type":259,"value":16922},{"type":254,"tag":622,"props":49587,"children":49588},{"style":1202},[49589],{"type":259,"value":14785},{"type":254,"tag":622,"props":49591,"children":49592},{"style":629},[49593],{"type":259,"value":1654},{"type":254,"tag":622,"props":49595,"children":49596},{"style":1735},[49597],{"type":259,"value":48257},{"type":254,"tag":622,"props":49599,"children":49600},{"style":629},[49601],{"type":259,"value":10733},{"type":254,"tag":622,"props":49603,"children":49604},{"style":1202},[49605],{"type":259,"value":48266},{"type":254,"tag":622,"props":49607,"children":49608},{"style":741},[49609],{"type":259,"value":48271},{"type":254,"tag":622,"props":49611,"children":49612},{"class":624,"line":679},[49613,49617,49621,49625,49629,49633],{"type":254,"tag":622,"props":49614,"children":49615},{"style":629},[49616],{"type":259,"value":16922},{"type":254,"tag":622,"props":49618,"children":49619},{"style":1202},[49620],{"type":259,"value":48283},{"type":254,"tag":622,"props":49622,"children":49623},{"style":629},[49624],{"type":259,"value":1654},{"type":254,"tag":622,"props":49626,"children":49627},{"style":629},[49628],{"type":259,"value":652},{"type":254,"tag":622,"props":49630,"children":49631},{"style":664},[49632],{"type":259,"value":48296},{"type":254,"tag":622,"props":49634,"children":49635},{"style":629},[49636],{"type":259,"value":9909},{"type":254,"tag":622,"props":49638,"children":49639},{"class":624,"line":705},[49640,49644,49648,49652,49656,49660,49664,49668],{"type":254,"tag":622,"props":49641,"children":49642},{"style":629},[49643],{"type":259,"value":16922},{"type":254,"tag":622,"props":49645,"children":49646},{"style":1202},[49647],{"type":259,"value":48312},{"type":254,"tag":622,"props":49649,"children":49650},{"style":629},[49651],{"type":259,"value":1654},{"type":254,"tag":622,"props":49653,"children":49654},{"style":629},[49655],{"type":259,"value":652},{"type":254,"tag":622,"props":49657,"children":49658},{"style":664},[49659],{"type":259,"value":48325},{"type":254,"tag":622,"props":49661,"children":49662},{"style":629},[49663],{"type":259,"value":16922},{"type":254,"tag":622,"props":49665,"children":49666},{"style":1202},[49667],{"type":259,"value":14785},{"type":254,"tag":622,"props":49669,"children":49670},{"style":629},[49671],{"type":259,"value":9909},{"type":254,"tag":622,"props":49673,"children":49674},{"class":624,"line":732},[49675,49679,49683,49687,49691,49695,49699,49703],{"type":254,"tag":622,"props":49676,"children":49677},{"style":629},[49678],{"type":259,"value":16922},{"type":254,"tag":622,"props":49680,"children":49681},{"style":1202},[49682],{"type":259,"value":48349},{"type":254,"tag":622,"props":49684,"children":49685},{"style":629},[49686],{"type":259,"value":1654},{"type":254,"tag":622,"props":49688,"children":49689},{"style":629},[49690],{"type":259,"value":652},{"type":254,"tag":622,"props":49692,"children":49693},{"style":664},[49694],{"type":259,"value":48362},{"type":254,"tag":622,"props":49696,"children":49697},{"style":629},[49698],{"type":259,"value":16922},{"type":254,"tag":622,"props":49700,"children":49701},{"style":1202},[49702],{"type":259,"value":14785},{"type":254,"tag":622,"props":49704,"children":49705},{"style":629},[49706],{"type":259,"value":9909},{"type":254,"tag":622,"props":49708,"children":49709},{"class":624,"line":759},[49710,49714,49718,49722,49726,49730,49734,49738],{"type":254,"tag":622,"props":49711,"children":49712},{"style":629},[49713],{"type":259,"value":16922},{"type":254,"tag":622,"props":49715,"children":49716},{"style":1202},[49717],{"type":259,"value":49001},{"type":254,"tag":622,"props":49719,"children":49720},{"style":629},[49721],{"type":259,"value":1654},{"type":254,"tag":622,"props":49723,"children":49724},{"style":629},[49725],{"type":259,"value":652},{"type":254,"tag":622,"props":49727,"children":49728},{"style":664},[49729],{"type":259,"value":49014},{"type":254,"tag":622,"props":49731,"children":49732},{"style":629},[49733],{"type":259,"value":16922},{"type":254,"tag":622,"props":49735,"children":49736},{"style":1202},[49737],{"type":259,"value":14785},{"type":254,"tag":622,"props":49739,"children":49740},{"style":629},[49741],{"type":259,"value":9909},{"type":254,"tag":622,"props":49743,"children":49744},{"class":624,"line":798},[49745],{"type":254,"tag":622,"props":49746,"children":49747},{"emptyLinePlaceholder":1699},[49748],{"type":259,"value":1702},{"type":254,"tag":622,"props":49750,"children":49751},{"class":624,"line":836},[49752,49757,49761,49765,49769,49773,49777,49781,49785],{"type":254,"tag":622,"props":49753,"children":49754},{"style":1202},[49755],{"type":259,"value":49756},"az group create ",{"type":254,"tag":622,"props":49758,"children":49759},{"style":629},[49760],{"type":259,"value":17065},{"type":254,"tag":622,"props":49762,"children":49763},{"style":1202},[49764],{"type":259,"value":48602},{"type":254,"tag":622,"props":49766,"children":49767},{"style":629},[49768],{"type":259,"value":16922},{"type":254,"tag":622,"props":49770,"children":49771},{"style":1202},[49772],{"type":259,"value":48629},{"type":254,"tag":622,"props":49774,"children":49775},{"style":629},[49776],{"type":259,"value":17065},{"type":254,"tag":622,"props":49778,"children":49779},{"style":1202},[49780],{"type":259,"value":46150},{"type":254,"tag":622,"props":49782,"children":49783},{"style":629},[49784],{"type":259,"value":16922},{"type":254,"tag":622,"props":49786,"children":49787},{"style":1202},[49788],{"type":259,"value":49789},"location\n",{"type":254,"tag":622,"props":49791,"children":49792},{"class":624,"line":862},[49793],{"type":254,"tag":622,"props":49794,"children":49795},{"emptyLinePlaceholder":1699},[49796],{"type":259,"value":1702},{"type":254,"tag":622,"props":49798,"children":49799},{"class":624,"line":900},[49800],{"type":254,"tag":622,"props":49801,"children":49802},{"style":3195},[49803],{"type":259,"value":49804},"# Configure the Azure Blob Storage that will contain the state \n",{"type":254,"tag":622,"props":49806,"children":49807},{"class":624,"line":938},[49808,49813,49817,49821,49825,49829,49833,49837,49841,49845,49849,49853,49857,49862,49866],{"type":254,"tag":622,"props":49809,"children":49810},{"style":1202},[49811],{"type":259,"value":49812},"az storage account create ",{"type":254,"tag":622,"props":49814,"children":49815},{"style":629},[49816],{"type":259,"value":17065},{"type":254,"tag":622,"props":49818,"children":49819},{"style":1202},[49820],{"type":259,"value":48620},{"type":254,"tag":622,"props":49822,"children":49823},{"style":629},[49824],{"type":259,"value":16922},{"type":254,"tag":622,"props":49826,"children":49827},{"style":1202},[49828],{"type":259,"value":48629},{"type":254,"tag":622,"props":49830,"children":49831},{"style":629},[49832],{"type":259,"value":17065},{"type":254,"tag":622,"props":49834,"children":49835},{"style":1202},[49836],{"type":259,"value":48602},{"type":254,"tag":622,"props":49838,"children":49839},{"style":629},[49840],{"type":259,"value":16922},{"type":254,"tag":622,"props":49842,"children":49843},{"style":1202},[49844],{"type":259,"value":48611},{"type":254,"tag":622,"props":49846,"children":49847},{"style":629},[49848],{"type":259,"value":17065},{"type":254,"tag":622,"props":49850,"children":49851},{"style":1202},[49852],{"type":259,"value":46150},{"type":254,"tag":622,"props":49854,"children":49855},{"style":629},[49856],{"type":259,"value":16922},{"type":254,"tag":622,"props":49858,"children":49859},{"style":1202},[49860],{"type":259,"value":49861},"location ",{"type":254,"tag":622,"props":49863,"children":49864},{"style":629},[49865],{"type":259,"value":10675},{"type":254,"tag":622,"props":49867,"children":49868},{"style":1202},[49869],{"type":259,"value":49870},"sku Standard_LRS\n",{"type":254,"tag":622,"props":49872,"children":49873},{"class":624,"line":976},[49874],{"type":254,"tag":622,"props":49875,"children":49876},{"style":3195},[49877],{"type":259,"value":49878},"# Set environment variables needed to write on the storage account\n",{"type":254,"tag":622,"props":49880,"children":49881},{"class":624,"line":1014},[49882,49886,49890,49894,49898,49902,49906,49910,49914,49918,49922,49926,49930,49934,49938,49942,49946,49950,49954,49958],{"type":254,"tag":622,"props":49883,"children":49884},{"style":629},[49885],{"type":259,"value":16922},{"type":254,"tag":622,"props":49887,"children":49888},{"style":1202},[49889],{"type":259,"value":48584},{"type":254,"tag":622,"props":49891,"children":49892},{"style":629},[49893],{"type":259,"value":17046},{"type":254,"tag":622,"props":49895,"children":49896},{"style":1202},[49897],{"type":259,"value":48593},{"type":254,"tag":622,"props":49899,"children":49900},{"style":629},[49901],{"type":259,"value":17065},{"type":254,"tag":622,"props":49903,"children":49904},{"style":1202},[49905],{"type":259,"value":48602},{"type":254,"tag":622,"props":49907,"children":49908},{"style":629},[49909],{"type":259,"value":16922},{"type":254,"tag":622,"props":49911,"children":49912},{"style":1202},[49913],{"type":259,"value":48611},{"type":254,"tag":622,"props":49915,"children":49916},{"style":629},[49917],{"type":259,"value":17065},{"type":254,"tag":622,"props":49919,"children":49920},{"style":1202},[49921],{"type":259,"value":48620},{"type":254,"tag":622,"props":49923,"children":49924},{"style":629},[49925],{"type":259,"value":16922},{"type":254,"tag":622,"props":49927,"children":49928},{"style":1202},[49929],{"type":259,"value":48629},{"type":254,"tag":622,"props":49931,"children":49932},{"style":629},[49933],{"type":259,"value":17065},{"type":254,"tag":622,"props":49935,"children":49936},{"style":1202},[49937],{"type":259,"value":48638},{"type":254,"tag":622,"props":49939,"children":49940},{"style":629},[49941],{"type":259,"value":10675},{"type":254,"tag":622,"props":49943,"children":49944},{"style":1202},[49945],{"type":259,"value":17166},{"type":254,"tag":622,"props":49947,"children":49948},{"style":629},[49949],{"type":259,"value":46169},{"type":254,"tag":622,"props":49951,"children":49952},{"style":664},[49953],{"type":259,"value":48556},{"type":254,"tag":622,"props":49955,"children":49956},{"style":629},[49957],{"type":259,"value":46169},{"type":254,"tag":622,"props":49959,"children":49960},{"style":629},[49961],{"type":259,"value":1685},{"type":254,"tag":622,"props":49963,"children":49964},{"class":624,"line":1052},[49965,49969,49973,49977],{"type":254,"tag":622,"props":49966,"children":49967},{"style":629},[49968],{"type":259,"value":16922},{"type":254,"tag":622,"props":49970,"children":49971},{"style":1202},[49972],{"type":259,"value":48674},{"type":254,"tag":622,"props":49974,"children":49975},{"style":629},[49976],{"type":259,"value":48679},{"type":254,"tag":622,"props":49978,"children":49979},{"style":1202},[49980],{"type":259,"value":48684},{"type":254,"tag":622,"props":49982,"children":49983},{"class":624,"line":1078},[49984,49989,49993],{"type":254,"tag":622,"props":49985,"children":49986},{"style":1202},[49987],{"type":259,"value":49988},"az storage container create ",{"type":254,"tag":622,"props":49990,"children":49991},{"style":629},[49992],{"type":259,"value":17065},{"type":254,"tag":622,"props":49994,"children":49995},{"style":1202},[49996],{"type":259,"value":49997},"n iacstate\n",{"type":254,"tag":622,"props":49999,"children":50000},{"class":624,"line":1087},[50001],{"type":254,"tag":622,"props":50002,"children":50003},{"emptyLinePlaceholder":1699},[50004],{"type":259,"value":1702},{"type":254,"tag":622,"props":50006,"children":50007},{"class":624,"line":1096},[50008],{"type":254,"tag":622,"props":50009,"children":50010},{"style":3195},[50011],{"type":259,"value":50012},"# Configure the Key Vault that will be used to encrypt the sensitive data\n",{"type":254,"tag":622,"props":50014,"children":50015},{"class":624,"line":1105},[50016,50020,50024,50028,50032,50036,50040,50044,50048,50052,50056,50060,50064,50068,50072,50076,50080,50084,50088,50092,50096],{"type":254,"tag":622,"props":50017,"children":50018},{"style":629},[50019],{"type":259,"value":16922},{"type":254,"tag":622,"props":50021,"children":50022},{"style":1202},[50023],{"type":259,"value":49038},{"type":254,"tag":622,"props":50025,"children":50026},{"style":629},[50027],{"type":259,"value":1654},{"type":254,"tag":622,"props":50029,"children":50030},{"style":1202},[50031],{"type":259,"value":49047},{"type":254,"tag":622,"props":50033,"children":50034},{"style":629},[50035],{"type":259,"value":17065},{"type":254,"tag":622,"props":50037,"children":50038},{"style":1202},[50039],{"type":259,"value":48620},{"type":254,"tag":622,"props":50041,"children":50042},{"style":629},[50043],{"type":259,"value":16922},{"type":254,"tag":622,"props":50045,"children":50046},{"style":1202},[50047],{"type":259,"value":48629},{"type":254,"tag":622,"props":50049,"children":50050},{"style":629},[50051],{"type":259,"value":17065},{"type":254,"tag":622,"props":50053,"children":50054},{"style":1202},[50055],{"type":259,"value":48602},{"type":254,"tag":622,"props":50057,"children":50058},{"style":629},[50059],{"type":259,"value":16922},{"type":254,"tag":622,"props":50061,"children":50062},{"style":1202},[50063],{"type":259,"value":49080},{"type":254,"tag":622,"props":50065,"children":50066},{"style":629},[50067],{"type":259,"value":10675},{"type":254,"tag":622,"props":50069,"children":50070},{"style":1735},[50071],{"type":259,"value":49089},{"type":254,"tag":622,"props":50073,"children":50074},{"style":629},[50075],{"type":259,"value":17065},{"type":254,"tag":622,"props":50077,"children":50078},{"style":1202},[50079],{"type":259,"value":49098},{"type":254,"tag":622,"props":50081,"children":50082},{"style":629},[50083],{"type":259,"value":10675},{"type":254,"tag":622,"props":50085,"children":50086},{"style":1202},[50087],{"type":259,"value":17166},{"type":254,"tag":622,"props":50089,"children":50090},{"style":629},[50091],{"type":259,"value":652},{"type":254,"tag":622,"props":50093,"children":50094},{"style":664},[50095],{"type":259,"value":17175},{"type":254,"tag":622,"props":50097,"children":50098},{"style":629},[50099],{"type":259,"value":9909},{"type":254,"tag":622,"props":50101,"children":50102},{"class":624,"line":4131},[50103,50107,50111,50115,50119,50123,50127,50131,50135,50139,50143,50147,50151,50155,50159],{"type":254,"tag":622,"props":50104,"children":50105},{"style":629},[50106],{"type":259,"value":16922},{"type":254,"tag":622,"props":50108,"children":50109},{"style":1202},[50110],{"type":259,"value":49155},{"type":254,"tag":622,"props":50112,"children":50113},{"style":629},[50114],{"type":259,"value":1654},{"type":254,"tag":622,"props":50116,"children":50117},{"style":1202},[50118],{"type":259,"value":49164},{"type":254,"tag":622,"props":50120,"children":50121},{"style":629},[50122],{"type":259,"value":17065},{"type":254,"tag":622,"props":50124,"children":50125},{"style":1202},[50126],{"type":259,"value":49173},{"type":254,"tag":622,"props":50128,"children":50129},{"style":629},[50130],{"type":259,"value":17065},{"type":254,"tag":622,"props":50132,"children":50133},{"style":1202},[50134],{"type":259,"value":49182},{"type":254,"tag":622,"props":50136,"children":50137},{"style":629},[50138],{"type":259,"value":10675},{"type":254,"tag":622,"props":50140,"children":50141},{"style":1202},[50142],{"type":259,"value":17166},{"type":254,"tag":622,"props":50144,"children":50145},{"style":629},[50146],{"type":259,"value":652},{"type":254,"tag":622,"props":50148,"children":50149},{"style":664},[50150],{"type":259,"value":25120},{"type":254,"tag":622,"props":50152,"children":50153},{"style":629},[50154],{"type":259,"value":652},{"type":254,"tag":622,"props":50156,"children":50157},{"style":629},[50158],{"type":259,"value":10733},{"type":254,"tag":622,"props":50160,"children":50161},{"style":1202},[50162],{"type":259,"value":50163},"o tsv \n",{"type":254,"tag":622,"props":50165,"children":50166},{"class":624,"line":4183},[50167,50171,50175,50179,50183,50187,50191,50195,50199,50203,50207,50211,50215,50219],{"type":254,"tag":622,"props":50168,"children":50169},{"style":1202},[50170],{"type":259,"value":17471},{"type":254,"tag":622,"props":50172,"children":50173},{"style":629},[50174],{"type":259,"value":10675},{"type":254,"tag":622,"props":50176,"children":50177},{"style":1202},[50178],{"type":259,"value":17569},{"type":254,"tag":622,"props":50180,"children":50181},{"style":629},[50182],{"type":259,"value":16922},{"type":254,"tag":622,"props":50184,"children":50185},{"style":1202},[50186],{"type":259,"value":49255},{"type":254,"tag":622,"props":50188,"children":50189},{"style":629},[50190],{"type":259,"value":10675},{"type":254,"tag":622,"props":50192,"children":50193},{"style":1202},[50194],{"type":259,"value":49264},{"type":254,"tag":622,"props":50196,"children":50197},{"style":629},[50198],{"type":259,"value":652},{"type":254,"tag":622,"props":50200,"children":50201},{"style":664},[50202],{"type":259,"value":49222},{"type":254,"tag":622,"props":50204,"children":50205},{"style":629},[50206],{"type":259,"value":652},{"type":254,"tag":622,"props":50208,"children":50209},{"style":629},[50210],{"type":259,"value":16988},{"type":254,"tag":622,"props":50212,"children":50213},{"style":1202},[50214],{"type":259,"value":49285},{"type":254,"tag":622,"props":50216,"children":50217},{"style":629},[50218],{"type":259,"value":16922},{"type":254,"tag":622,"props":50220,"children":50221},{"style":1202},[50222],{"type":259,"value":50223},"myUserId \n",{"type":254,"tag":622,"props":50225,"children":50226},{"class":624,"line":4208},[50227,50231,50235,50239,50243,50247,50251,50255,50259],{"type":254,"tag":622,"props":50228,"children":50229},{"style":1202},[50230],{"type":259,"value":49314},{"type":254,"tag":622,"props":50232,"children":50233},{"style":629},[50234],{"type":259,"value":17065},{"type":254,"tag":622,"props":50236,"children":50237},{"style":1202},[50238],{"type":259,"value":49323},{"type":254,"tag":622,"props":50240,"children":50241},{"style":629},[50242],{"type":259,"value":10675},{"type":254,"tag":622,"props":50244,"children":50245},{"style":1202},[50246],{"type":259,"value":49332},{"type":254,"tag":622,"props":50248,"children":50249},{"style":629},[50250],{"type":259,"value":17065},{"type":254,"tag":622,"props":50252,"children":50253},{"style":1202},[50254],{"type":259,"value":17326},{"type":254,"tag":622,"props":50256,"children":50257},{"style":629},[50258],{"type":259,"value":16922},{"type":254,"tag":622,"props":50260,"children":50261},{"style":1202},[50262],{"type":259,"value":49349},{"type":254,"tag":622,"props":50264,"children":50265},{"class":624,"line":4216},[50266],{"type":254,"tag":622,"props":50267,"children":50268},{"style":3195},[50269],{"type":259,"value":50270},"# Use az cli to authenticate to key vault instead of using environment variables \n",{"type":254,"tag":622,"props":50272,"children":50273},{"class":624,"line":4224},[50274,50278,50282,50286,50290,50294],{"type":254,"tag":622,"props":50275,"children":50276},{"style":629},[50277],{"type":259,"value":16922},{"type":254,"tag":622,"props":50279,"children":50280},{"style":1202},[50281],{"type":259,"value":49373},{"type":254,"tag":622,"props":50283,"children":50284},{"style":629},[50285],{"type":259,"value":1654},{"type":254,"tag":622,"props":50287,"children":50288},{"style":629},[50289],{"type":259,"value":652},{"type":254,"tag":622,"props":50291,"children":50292},{"style":664},[50293],{"type":259,"value":24747},{"type":254,"tag":622,"props":50295,"children":50296},{"style":629},[50297],{"type":259,"value":9909},{"type":254,"tag":622,"props":50299,"children":50300},{"class":624,"line":4268},[50301],{"type":254,"tag":622,"props":50302,"children":50303},{"emptyLinePlaceholder":1699},[50304],{"type":259,"value":1702},{"type":254,"tag":622,"props":50306,"children":50307},{"class":624,"line":4276},[50308],{"type":254,"tag":622,"props":50309,"children":50310},{"style":3195},[50311],{"type":259,"value":50312},"# Indicate pulumi to use the newly created azure blob storage as a backend\n",{"type":254,"tag":622,"props":50314,"children":50315},{"class":624,"line":4332},[50316,50320,50324],{"type":254,"tag":622,"props":50317,"children":50318},{"style":1202},[50319],{"type":259,"value":48751},{"type":254,"tag":622,"props":50321,"children":50322},{"style":629},[50323],{"type":259,"value":38393},{"type":254,"tag":622,"props":50325,"children":50326},{"style":1202},[50327],{"type":259,"value":48760},{"type":254,"tag":622,"props":50329,"children":50330},{"class":624,"line":4376},[50331],{"type":254,"tag":622,"props":50332,"children":50333},{"style":3195},[50334],{"type":259,"value":50335},"# Create and use a folder to store the infrastructure code\n",{"type":254,"tag":622,"props":50337,"children":50338},{"class":624,"line":4384},[50339,50343,50347,50351],{"type":254,"tag":622,"props":50340,"children":50341},{"style":1202},[50342],{"type":259,"value":48795},{"type":254,"tag":622,"props":50344,"children":50345},{"style":629},[50346],{"type":259,"value":2000},{"type":254,"tag":622,"props":50348,"children":50349},{"style":1202},[50350],{"type":259,"value":48804},{"type":254,"tag":622,"props":50352,"children":50353},{"style":629},[50354],{"type":259,"value":2350},{"type":254,"tag":622,"props":50356,"children":50357},{"class":624,"line":4416},[50358],{"type":254,"tag":622,"props":50359,"children":50360},{"style":3195},[50361],{"type":259,"value":50362},"# Create a new Pulumi project using the azure blob storage as the backend and the keyvault as the encryption provider \n",{"type":254,"tag":622,"props":50364,"children":50365},{"class":624,"line":4456},[50366,50370,50374,50378,50382,50386,50390,50394,50398,50402,50406,50410,50414,50418,50422,50426,50430,50434,50438,50442],{"type":254,"tag":622,"props":50367,"children":50368},{"style":1202},[50369],{"type":259,"value":48816},{"type":254,"tag":622,"props":50371,"children":50372},{"style":629},[50373],{"type":259,"value":17065},{"type":254,"tag":622,"props":50375,"children":50376},{"style":1202},[50377],{"type":259,"value":48825},{"type":254,"tag":622,"props":50379,"children":50380},{"style":629},[50381],{"type":259,"value":17065},{"type":254,"tag":622,"props":50383,"children":50384},{"style":1202},[50385],{"type":259,"value":48834},{"type":254,"tag":622,"props":50387,"children":50388},{"style":629},[50389],{"type":259,"value":17065},{"type":254,"tag":622,"props":50391,"children":50392},{"style":1202},[50393],{"type":259,"value":24174},{"type":254,"tag":622,"props":50395,"children":50396},{"style":629},[50397],{"type":259,"value":17065},{"type":254,"tag":622,"props":50399,"children":50400},{"style":1202},[50401],{"type":259,"value":49441},{"type":254,"tag":622,"props":50403,"children":50404},{"style":629},[50405],{"type":259,"value":10675},{"type":254,"tag":622,"props":50407,"children":50408},{"style":1202},[50409],{"type":259,"value":49450},{"type":254,"tag":622,"props":50411,"children":50412},{"style":629},[50413],{"type":259,"value":17065},{"type":254,"tag":622,"props":50415,"children":50416},{"style":1202},[50417],{"type":259,"value":15928},{"type":254,"tag":622,"props":50419,"children":50420},{"style":629},[50421],{"type":259,"value":1654},{"type":254,"tag":622,"props":50423,"children":50424},{"style":629},[50425],{"type":259,"value":652},{"type":254,"tag":622,"props":50427,"children":50428},{"style":664},[50429],{"type":259,"value":49471},{"type":254,"tag":622,"props":50431,"children":50432},{"style":629},[50433],{"type":259,"value":16922},{"type":254,"tag":622,"props":50435,"children":50436},{"style":1202},[50437],{"type":259,"value":49001},{"type":254,"tag":622,"props":50439,"children":50440},{"style":664},[50441],{"type":259,"value":49484},{"type":254,"tag":622,"props":50443,"children":50444},{"style":629},[50445],{"type":259,"value":9909},{"type":254,"tag":622,"props":50447,"children":50448},{"class":624,"line":4488},[50449],{"type":254,"tag":622,"props":50450,"children":50451},{"style":3195},[50452],{"type":259,"value":50453},"# Deploy the infrastructure\n",{"type":254,"tag":622,"props":50455,"children":50456},{"class":624,"line":4496},[50457,50461,50465],{"type":254,"tag":622,"props":50458,"children":50459},{"style":1202},[50460],{"type":259,"value":48859},{"type":254,"tag":622,"props":50462,"children":50463},{"style":629},[50464],{"type":259,"value":17065},{"type":254,"tag":622,"props":50466,"children":50467},{"style":1202},[50468],{"type":259,"value":48851},{"type":254,"tag":255,"props":50470,"children":50471},{},[50472],{"type":259,"value":50473},"Using Pulumi without Pulumi Service was not complicated as I thought it would be. I like the fact that Pulumi is not limited to being used with Pulumi Service backend and secret provider. It gives us the choice to use what we want: if I want to use Google Cloud Storage as my back-end and AWS Key Management Service as my encryption provider I totally can. Many options are available and well integrated without requiring much work which is nice.",{"type":254,"tag":255,"props":50475,"children":50476},{},[50477,50479,50485],{"type":259,"value":50478},"Yet honestly, I think that using Pulumi Service will be my default choice because of the many built-in features it offers (deployment history, concurrent state locking, collaboration functionalities, ...). It's free for individuals so I would not bother with a self-managed backend for individuals. For teams and companies, you have to pay (even if there is a monthly free grant of credits for the team plan) but I don't have enough perspective to say if it's worth it. You can find the pricing ",{"type":254,"tag":262,"props":50480,"children":50483},{"href":50481,"rel":50482},"https://www.pulumi.com/pricing/",[266],[50484],{"type":259,"value":1414},{"type":259,"value":50486}," if you want to see it by yourself. I guess the choice between that and a self-managed backend will probably depend more on the project and the organization you are working for.",{"type":254,"tag":1260,"props":50488,"children":50489},{},[50490],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":50492},[50493,50497,50502,50507,50508],{"id":47982,"depth":635,"text":47985,"children":50494},[50495,50496],{"id":47988,"depth":679,"text":47991},{"id":48066,"depth":679,"text":48069},{"id":48110,"depth":635,"text":48113,"children":50498},[50499,50500,50501],{"id":48116,"depth":679,"text":48119},{"id":48177,"depth":679,"text":48180},{"id":48728,"depth":679,"text":48731},{"id":48904,"depth":635,"text":48907,"children":50503},[50504,50505,50506],{"id":48910,"depth":679,"text":48913},{"id":48926,"depth":679,"text":48929},{"id":48974,"depth":679,"text":48977},{"id":49491,"depth":635,"text":49494},{"id":10572,"depth":635,"text":10575},"content:1.posts:30.pulumi-azure-backend.md","1.posts/30.pulumi-azure-backend.md",{"_path":94,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":93,"description":50512,"date":50513,"lead":50514,"image":50515,"badge":50517,"tags":50518,"body":50519,"_type":1275,"_id":50824,"_source":231,"_file":50825,"_extension":1278},"Do you often star a GitHub repository and later when you want to get back to it struggle to find it on your stars page? I do! Well, I did... until I discovered Astral, the web application that allows you to Organize Your GitHub Stars With Ease. That is what we are going to talk about in this article.","2021-11-28T00:00:00.000Z","Another way to bookmark GitHub repositories.",{"src":50516},"/images/stars_1.jpg",{"label":243},[245,16352],{"type":251,"children":50520,"toc":50817},[50521,50543,50549,50571,50576,50585,50590,50599,50605,50610,50633,50638,50644,50655,50664,50669,50674,50697,50706,50714,50720,50725,50730,50748,50756,50765,50770,50791,50800,50804],{"type":254,"tag":255,"props":50522,"children":50523},{},[50524,50526,50533,50535,50541],{"type":259,"value":50525},"Do you often star a GitHub repository and later when you want to get back to it struggle to find it on your stars page? I do! Well, I did... until I discovered ",{"type":254,"tag":262,"props":50527,"children":50530},{"href":50528,"rel":50529},"https://astralapp.com/",[266],[50531],{"type":259,"value":50532},"Astral",{"type":259,"value":50534},", the web application that allows you to ",{"type":254,"tag":389,"props":50536,"children":50538},{"className":50537},[],[50539],{"type":259,"value":50540},"Organize Your GitHub Stars With Ease",{"type":259,"value":50542},". That is what we are going to talk about in this article.",{"type":254,"tag":282,"props":50544,"children":50546},{"id":50545},"github-stars-as-a-bookmarking-tool",[50547],{"type":259,"value":50548},"GitHub stars as a bookmarking tool.",{"type":254,"tag":255,"props":50550,"children":50551},{},[50552,50554,50561,50563,50569],{"type":259,"value":50553},"When you find an interesting GitHub repository, instead of adding it to your browser bookmarks among a lot of other websites, you can star it. As ",{"type":254,"tag":262,"props":50555,"children":50558},{"href":50556,"rel":50557},"https://docs.github.com/en/get-started/exploring-projects-on-github/saving-repositories-with-stars",[266],[50559],{"type":259,"value":50560},"GitHub Docs",{"type":259,"value":50562}," say: ",{"type":254,"tag":389,"props":50564,"children":50566},{"className":50565},[],[50567],{"type":259,"value":50568},"Starring make it easy to find a repository again later",{"type":259,"value":50570},". Indeed usually, when I come across a nice GitHub repository, I want to go back to it later (to deep dive into the awesome framework I discover, to try this new open source tool, to have this useful sample somewhere when I will need it...).",{"type":254,"tag":255,"props":50572,"children":50573},{},[50574],{"type":259,"value":50575},"As you can see below, all the GitHub repositories you have starred can be found on your GitHub stars page.",{"type":254,"tag":255,"props":50577,"children":50578},{},[50579],{"type":254,"tag":1161,"props":50580,"children":50584},{"alt":50581,"className":50582,"src":50583},"\"Your stars\" menu in GitHub.",[1165,1166],"/posts/images/astral_github_1.png",[],{"type":254,"tag":255,"props":50586,"children":50587},{},[50588],{"type":259,"value":50589},"In addition to that, you can sort, filter (by language or topic), and search starred repositories:",{"type":254,"tag":255,"props":50591,"children":50592},{},[50593],{"type":254,"tag":1161,"props":50594,"children":50598},{"alt":50595,"className":50596,"src":50597},"Starred GitHub repositories filters.",[1165,1166],"/posts/images/astral_github_2.png",[],{"type":254,"tag":282,"props":50600,"children":50602},{"id":50601},"have-you-seen-that-repository-i-am-looking-for",[50603],{"type":259,"value":50604},"Have you seen that repository I am looking for?",{"type":254,"tag":255,"props":50606,"children":50607},{},[50608],{"type":259,"value":50609},"Using GitHub stars to bookmark the repositories you are interested in is great but if you do it a lot, it can become hard to find later the repository you want. Even if it is possible to sort, filter, and search repositories, it is sometimes impossible to quickly find what you are looking for. And there are good reasons for that:",{"type":254,"tag":332,"props":50611,"children":50612},{},[50613,50618,50623,50628],{"type":254,"tag":336,"props":50614,"children":50615},{},[50616],{"type":259,"value":50617},"You have starred this repository a long time ago, and it does not stand out from other repositories in terms of activity or number of stars so sorting won't help you",{"type":254,"tag":336,"props":50619,"children":50620},{},[50621],{"type":259,"value":50622},"Most of your starred repositories are in the same language so filters are useless",{"type":254,"tag":336,"props":50624,"children":50625},{},[50626],{"type":259,"value":50627},"The repository was not tagged with a topic that could help you to find it",{"type":254,"tag":336,"props":50629,"children":50630},{},[50631],{"type":259,"value":50632},"You don't remember the name of the repository",{"type":254,"tag":255,"props":50634,"children":50635},{},[50636],{"type":259,"value":50637},"The problem is that starred repositories can only be filtered with information that is not particularly useful to you. What we would need is something to organize the starred repositories in a way relevant to us. And here comes Astral.",{"type":254,"tag":282,"props":50639,"children":50641},{"id":50640},"what-is-astral",[50642],{"type":259,"value":50643},"What is Astral?",{"type":254,"tag":255,"props":50645,"children":50646},{},[50647,50653],{"type":254,"tag":262,"props":50648,"children":50651},{"href":50649,"rel":50650},"https://github.com/astralapp/astral",[266],[50652],{"type":259,"value":50532},{"type":259,"value":50654}," is an open source web application that helps you organize your starred repositories.",{"type":254,"tag":255,"props":50656,"children":50657},{},[50658],{"type":254,"tag":1161,"props":50659,"children":50663},{"alt":50660,"className":50661,"src":50662},"Astral home website.",[1165,1166],"/posts/images/astral_app_1.png",[],{"type":254,"tag":255,"props":50665,"children":50666},{},[50667],{"type":259,"value":50668},"It solves the issues I was previously pointing out by allowing us to add tags to our starred repositories. That means I can create a tag with a meaningful name and add this tag to the appropriate repositories.",{"type":254,"tag":255,"props":50670,"children":50671},{},[50672],{"type":259,"value":50673},"Once grouped by tags, it becomes very easy to find the repositories you need. The interface is quite simple but shows all the information we need:",{"type":254,"tag":332,"props":50675,"children":50676},{},[50677,50682,50687,50692],{"type":254,"tag":336,"props":50678,"children":50679},{},[50680],{"type":259,"value":50681},"the list of starred repositories grouped by tags",{"type":254,"tag":336,"props":50683,"children":50684},{},[50685],{"type":259,"value":50686},"basic information for each repository (language, number of stars, number of pull requests, number of days since the last commit, the latest release, ...)",{"type":254,"tag":336,"props":50688,"children":50689},{},[50690],{"type":259,"value":50691},"a preview of the README file of the currently selected repository with the URL to quickly clone the repository",{"type":254,"tag":336,"props":50693,"children":50694},{},[50695],{"type":259,"value":50696},"a notes section where you can write extra information for each repository",{"type":254,"tag":255,"props":50698,"children":50699},{},[50700],{"type":254,"tag":1161,"props":50701,"children":50705},{"alt":50702,"className":50703,"src":50704},"List of starred repositories in Astral.",[1165,1166],"/posts/images/astral_app_2.png",[],{"type":254,"tag":427,"props":50707,"children":50708},{"icon":429},[50709],{"type":254,"tag":255,"props":50710,"children":50711},{},[50712],{"type":259,"value":50713},"Astral can also be self-hosted if you need. As it is a free web application, I don't see the point of self-hosting it but it is possible.",{"type":254,"tag":282,"props":50715,"children":50717},{"id":50716},"how-do-i-use-astral",[50718],{"type":259,"value":50719},"How do I use Astral?",{"type":254,"tag":255,"props":50721,"children":50722},{},[50723],{"type":259,"value":50724},"I organize my starred repositories by grouping them with tags that I create in Astral and that are relevant to my way of categorizing things. That is the power of creating custom tags and assigning repositories to them, everyone can organize its starred repositories as it suits him or her.",{"type":254,"tag":255,"props":50726,"children":50727},{},[50728],{"type":259,"value":50729},"To be honest, I only started to use Astral recently so my organization is subject to change but here are kinds of tags I like to create:",{"type":254,"tag":332,"props":50731,"children":50732},{},[50733,50738,50743],{"type":254,"tag":336,"props":50734,"children":50735},{},[50736],{"type":259,"value":50737},"Tags for different IT areas (like database, cloud, testing, IoT,..)",{"type":254,"tag":336,"props":50739,"children":50740},{},[50741],{"type":259,"value":50742},"Tags for the type of repository (like libraries, training, samples)",{"type":254,"tag":336,"props":50744,"children":50745},{},[50746],{"type":259,"value":50747},"Tags for the different frameworks I am interested in",{"type":254,"tag":427,"props":50749,"children":50750},{"icon":429},[50751],{"type":254,"tag":255,"props":50752,"children":50753},{},[50754],{"type":259,"value":50755},"The language used in a repository is one thing but that does not say what framework is used (like ASP.NET Core, MAUI, Vue.js, React.js,...) and that can be a useful criterion when you are later looking for repositories using a specific framework",{"type":254,"tag":255,"props":50757,"children":50758},{},[50759],{"type":254,"tag":1161,"props":50760,"children":50764},{"alt":50761,"className":50762,"src":50763},"Astral website with tags.",[1165,1166],"/posts/images/astral_app_3.png",[],{"type":254,"tag":255,"props":50766,"children":50767},{},[50768],{"type":259,"value":50769},"Starred repositories I have not yet tagged are located in the top left corner of the application. I try to tag a starred repository with different tags to help the future me looking for some repositories with multiple criteria.",{"type":254,"tag":255,"props":50771,"children":50772},{},[50773,50775,50781,50783,50789],{"type":259,"value":50774},"Speaking of multiple criteria, you can create ",{"type":254,"tag":389,"props":50776,"children":50778},{"className":50777},[],[50779],{"type":259,"value":50780},"Smart Filters",{"type":259,"value":50782}," in Astral to filter your starred repositories with specific rules. For instance, I created a smart filter called ",{"type":254,"tag":389,"props":50784,"children":50786},{"className":50785},[],[50787],{"type":259,"value":50788},"Microsoft 365 samples",{"type":259,"value":50790}," to list active repositories showing samples to build Microsoft 365 applications (Teams applications, applications using Microsoft Graph...). This is just an example but you can create much more complex filters.",{"type":254,"tag":255,"props":50792,"children":50793},{},[50794],{"type":254,"tag":1161,"props":50795,"children":50799},{"alt":50796,"className":50797,"src":50798},"Smart filters in Astral.",[1165,1166],"/posts/images/astral_app_4.png",[],{"type":254,"tag":282,"props":50801,"children":50802},{"id":10572},[50803],{"type":259,"value":10575},{"type":254,"tag":255,"props":50805,"children":50806},{},[50807,50809,50815],{"type":259,"value":50808},"If you are often bookmarking GitHub repositories for future usage, Astral is a nice little web application to add to your toolbox. I found it by chance, but I liked it and I am now regularly using it. So try it and if you like it, give it a star and tag the repo with a ",{"type":254,"tag":389,"props":50810,"children":50812},{"className":50811},[],[50813],{"type":259,"value":50814},"tools",{"type":259,"value":50816}," tag in Astral 😉.",{"title":205,"searchDepth":635,"depth":635,"links":50818},[50819,50820,50821,50822,50823],{"id":50545,"depth":635,"text":50548},{"id":50601,"depth":635,"text":50604},{"id":50640,"depth":635,"text":50643},{"id":50716,"depth":635,"text":50719},{"id":10572,"depth":635,"text":10575},"content:1.posts:29.astral.md","1.posts/29.astral.md",{"_path":91,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":90,"description":50827,"lead":50828,"date":50829,"image":50830,"badge":50831,"tags":50832,"body":50833,"_type":1275,"_id":51095,"_source":231,"_file":51096,"_extension":1278},"Vue Devtools, Visual Studio themes, Git tips, and .NET Conf replays.","A bit of tooling and a bit of git.","2021-11-14T00:00:00.000Z",{"src":9258},{"label":9260},[9262,42358,31320,16850],{"type":251,"children":50834,"toc":51087},[50835,50847,50859,50890,50899,50905,50928,50951,50960,50966,50971,50976,50982,51010,51038,51055,51061,51074,51083],{"type":254,"tag":282,"props":50836,"children":50838},{"id":50837},"vue-devtools-a-must-have-browser-extension-when-using-vuejs",[50839,50845],{"type":254,"tag":389,"props":50840,"children":50842},{"className":50841},[],[50843],{"type":259,"value":50844},"Vue Devtools",{"type":259,"value":50846},", a must-have browser extension when using Vue.js",{"type":254,"tag":255,"props":50848,"children":50849},{},[50850,50852,50857],{"type":259,"value":50851},"Since a colleague showed me the ",{"type":254,"tag":389,"props":50853,"children":50855},{"className":50854},[],[50856],{"type":259,"value":50844},{"type":259,"value":50858}," extension, it has been a game-changer for me to work on Vue.js projects. It is very useful!",{"type":254,"tag":255,"props":50860,"children":50861},{},[50862,50864,50871,50873,50879,50881,50888],{"type":259,"value":50863},"I could describe to you how this extension works and what are its features but there are already very good articles that do that, like this ",{"type":254,"tag":262,"props":50865,"children":50868},{"href":50866,"rel":50867},"https://www.vuemastery.com/blog/whats-new-in-the-vue3-devtools/",[266],[50869],{"type":259,"value":50870},"one",{"type":259,"value":50872}," on Vue Mastery's blog. Check that, have a look at the ",{"type":254,"tag":262,"props":50874,"children":50877},{"href":50875,"rel":50876},"https://devtools.vuejs.org/",[266],[50878],{"type":259,"value":6340},{"type":259,"value":50880},", install ",{"type":254,"tag":262,"props":50882,"children":50885},{"href":50883,"rel":50884},"https://chrome.google.com/webstore/detail/vuejs-devtools/ljjemllljcmogpfapbkkighbhhppjdbg",[266],[50886],{"type":259,"value":50887},"it",{"type":259,"value":50889}," (the beta version), and try it.",{"type":254,"tag":255,"props":50891,"children":50892},{},[50893],{"type":254,"tag":1161,"props":50894,"children":50898},{"alt":50895,"className":50896,"src":50897},"Vue Devtools website.",[1165,1166],"/posts/images/w452021tips_vue_1.png",[],{"type":254,"tag":282,"props":50900,"children":50902},{"id":50901},"cobalt2-theme-is-available-in-visual-studio-2022",[50903],{"type":259,"value":50904},"Cobalt2 theme is available in Visual Studio 2022",{"type":254,"tag":255,"props":50906,"children":50907},{},[50908,50910,50917,50919,50926],{"type":259,"value":50909},"I have used ",{"type":254,"tag":262,"props":50911,"children":50914},{"href":50912,"rel":50913},"https://marketplace.visualstudio.com/items?itemName=wesbos.theme-cobalt2",[266],[50915],{"type":259,"value":50916},"Cobalt2 theme",{"type":259,"value":50918}," for vscode for quite some time now. And this theme is now available in Visual Studio 2022, you can check ",{"type":254,"tag":262,"props":50920,"children":50923},{"href":50921,"rel":50922},"https://marketplace.visualstudio.com/items?itemName=SIBA.Cobalt2Theme",[266],[50924],{"type":259,"value":50925},"this extension",{"type":259,"value":50927}," to get it.",{"type":254,"tag":255,"props":50929,"children":50930},{},[50931,50933,50940,50942,50949],{"type":259,"value":50932},"A lot of vscode themes have been made available in Visual Studio 2022 thanks to a new tool: ",{"type":254,"tag":262,"props":50934,"children":50937},{"href":50935,"rel":50936},"https://github.com/microsoft/theme-converter-for-vs",[266],[50938],{"type":259,"value":50939},"Theme Converter for Visual Studio",{"type":259,"value":50941},". You can read more about that ",{"type":254,"tag":262,"props":50943,"children":50946},{"href":50944,"rel":50945},"https://devblogs.microsoft.com/visualstudio/custom-themes/",[266],[50947],{"type":259,"value":50948},"in this article",{"type":259,"value":50950}," from Visual Studio dev blog.",{"type":254,"tag":255,"props":50952,"children":50953},{},[50954],{"type":254,"tag":1161,"props":50955,"children":50959},{"alt":50956,"className":50957,"src":50958},"Visual Studio 2022 with cobalt theme.",[1165,1166],"/posts/images/w452021tips_vs_1.png",[],{"type":254,"tag":282,"props":50961,"children":50963},{"id":50962},"keep-a-2nd-clone-for-reviewing-pull-requests",[50964],{"type":259,"value":50965},"Keep a 2nd clone for reviewing pull requests",{"type":254,"tag":255,"props":50967,"children":50968},{},[50969],{"type":259,"value":50970},"I think it is often interesting to checkout the branch of a pull request instead of relying only on the web view to review a PR. It allows checking more things and a better comprehension of the code. However, when you are working on a feature, you don't want to mix your current work with the pull request you are reviewing.",{"type":254,"tag":255,"props":50972,"children":50973},{},[50974],{"type":259,"value":50975},"That is why instead of stashing code changes and switching branches I prefer having on my laptop 2 clones of the same repository I am working on, with one clone dedicated to code reviews.",{"type":254,"tag":282,"props":50977,"children":50979},{"id":50978},"git-clrf",[50980],{"type":259,"value":50981},"Git CLRF",{"type":254,"tag":255,"props":50983,"children":50984},{},[50985,50987,50993,50995,51001,51003,51009],{"type":259,"value":50986},"If you are using Git on Windows, it is a good practice to set your ",{"type":254,"tag":389,"props":50988,"children":50990},{"className":50989},[],[50991],{"type":259,"value":50992},"autocrlf",{"type":259,"value":50994}," to true in your Git configuration to avoid line-ending issues (learn more about this topic in the ",{"type":254,"tag":262,"props":50996,"children":50999},{"href":50997,"rel":50998},"https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration",[266],[51000],{"type":259,"value":6340},{"type":259,"value":51002},"). You can do that with the following command ",{"type":254,"tag":389,"props":51004,"children":51006},{"className":51005},[],[51007],{"type":259,"value":51008},"git config --global core.autocrlf true",{"type":259,"value":594},{"type":254,"tag":255,"props":51011,"children":51012},{},[51013,51015,51020,51022,51028,51030,51036],{"type":259,"value":51014},"Unfortunately, maybe not all your colleagues have correctly configured ",{"type":254,"tag":389,"props":51016,"children":51018},{"className":51017},[],[51019],{"type":259,"value":50992},{"type":259,"value":51021}," on their machine. And you will probably forget to ask each newcomer to check that. So something that can be interesting (if all your team is working on Windows) is to directly enforce this setting on your repository by pushing a ",{"type":254,"tag":389,"props":51023,"children":51025},{"className":51024},[],[51026],{"type":259,"value":51027},".gitattributes",{"type":259,"value":51029}," with ",{"type":254,"tag":389,"props":51031,"children":51033},{"className":51032},[],[51034],{"type":259,"value":51035},"text=auto",{"type":259,"value":51037}," in it).",{"type":254,"tag":427,"props":51039,"children":51040},{"icon":429},[51041],{"type":254,"tag":255,"props":51042,"children":51043},{},[51044,51046,51053],{"type":259,"value":51045},"As I was writing these lines, I just saw an article from Scott Hanselman about ",{"type":254,"tag":262,"props":51047,"children":51050},{"href":51048,"rel":51049},"https://www.hanselman.com/blog/carriage-returns-and-line-feeds-will-ultimately-bite-you-some-git-tips",[266],[51051],{"type":259,"value":51052},"Carriage Returns and Line Feeds",{"type":259,"value":51054},", if you want to read more about it.",{"type":254,"tag":282,"props":51056,"children":51058},{"id":51057},"dotnetconf-playlist",[51059],{"type":259,"value":51060},"dotNetConf playlist",{"type":254,"tag":255,"props":51062,"children":51063},{},[51064,51066,51072],{"type":259,"value":51065},"dotNetConf was this week and the replays are available ",{"type":254,"tag":262,"props":51067,"children":51070},{"href":51068,"rel":51069},"https://www.youtube.com/playlist?list=PLdo4fOcmZ0oVFtp9MDEBNbA2sSqYvXSXO",[266],[51071],{"type":259,"value":1414},{"type":259,"value":51073}," if you want to watch some talks you missed.",{"type":254,"tag":255,"props":51075,"children":51076},{},[51077],{"type":254,"tag":1161,"props":51078,"children":51082},{"alt":51079,"className":51080,"src":51081},".NET Conf 2021 YouTube playlist.",[1165,1166],"/posts/images/w452021tips_dotnetconf_1.png",[],{"type":254,"tag":255,"props":51084,"children":51085},{},[51086],{"type":259,"value":10040},{"title":205,"searchDepth":635,"depth":635,"links":51088},[51089,51091,51092,51093,51094],{"id":50837,"depth":635,"text":51090},"Vue Devtools, a must-have browser extension when using Vue.js",{"id":50901,"depth":635,"text":50904},{"id":50962,"depth":635,"text":50965},{"id":50978,"depth":635,"text":50981},{"id":51057,"depth":635,"text":51060},"content:1.posts:28.w45-2021-tips-learned-this-week.md","1.posts/28.w45-2021-tips-learned-this-week.md",{"_path":88,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":87,"description":51098,"lead":51099,"date":51100,"badge":51101,"image":51102,"tags":51104,"body":51106,"_type":1275,"_id":52274,"_source":231,"_file":52275,"_extension":1278},"Today I talk to you about the recent changes I made to my blog when I migrated it to Statiq and open-sourced it.","What changed on my blog - Statiq, GitHub, giscus, and much more.","2021-11-07T00:00:00.000Z",{"label":243},{"src":51103},"/images/migration_1.jpg",[51105,1288,16352,16354,42627],"Statiq",{"type":251,"children":51107,"toc":52257},[51108,51121,51125,51130,51192,51197,51203,51209,51214,51236,51248,51325,51331,51345,51350,51356,51370,51383,51388,51397,51411,51417,51437,51450,51456,51461,51467,51472,51481,51486,51494,51508,51517,51529,51557,51563,51568,51936,51941,51947,51952,51961,51967,51979,52007,52020,52070,52079,52091,52124,52130,52144,52153,52157,52162,52232,52237,52253],{"type":254,"tag":255,"props":51109,"children":51110},{},[51111,51113,51119],{"type":259,"value":51112},"Today I talk to you about the recent changes I made to my blog when I migrated it to ",{"type":254,"tag":262,"props":51114,"children":51117},{"href":51115,"rel":51116},"https://statiq.dev/",[266],[51118],{"type":259,"value":51105},{"type":259,"value":51120}," and open-sourced it.",{"type":254,"tag":282,"props":51122,"children":51123},{"id":1299},[51124],{"type":259,"value":1302},{"type":254,"tag":255,"props":51126,"children":51127},{},[51128],{"type":259,"value":51129},"I initially created my blog in March 2019 using:",{"type":254,"tag":332,"props":51131,"children":51132},{},[51133,51145,51157,51169,51180],{"type":254,"tag":336,"props":51134,"children":51135},{},[51136,51143],{"type":254,"tag":262,"props":51137,"children":51140},{"href":51138,"rel":51139},"https://github.com/Wyamio/Wyam",[266],[51141],{"type":259,"value":51142},"Wyam",{"type":259,"value":51144}," (a .NET static website generator) to generate the website",{"type":254,"tag":336,"props":51146,"children":51147},{},[51148,51155],{"type":254,"tag":262,"props":51149,"children":51152},{"href":51150,"rel":51151},"https://azure.microsoft.com/en-us/services/devops/repos/",[266],[51153],{"type":259,"value":51154},"Azure Repos",{"type":259,"value":51156}," to store the source code on a private git repository",{"type":254,"tag":336,"props":51158,"children":51159},{},[51160,51167],{"type":254,"tag":262,"props":51161,"children":51164},{"href":51162,"rel":51163},"https://azure.microsoft.com/en-us/services/devops/pipelines/",[266],[51165],{"type":259,"value":51166},"Azure Pipelines",{"type":259,"value":51168}," to build and deploy the website",{"type":254,"tag":336,"props":51170,"children":51171},{},[51172,51178],{"type":254,"tag":262,"props":51173,"children":51176},{"href":51174,"rel":51175},"https://www.netlify.com/",[266],[51177],{"type":259,"value":31941},{"type":259,"value":51179}," to host the website",{"type":254,"tag":336,"props":51181,"children":51182},{},[51183,51190],{"type":254,"tag":262,"props":51184,"children":51187},{"href":51185,"rel":51186},"https://disqus.com/",[266],[51188],{"type":259,"value":51189},"Disqus",{"type":259,"value":51191}," to allow visitors to comment on my articles",{"type":254,"tag":255,"props":51193,"children":51194},{},[51195],{"type":259,"value":51196},"A little bit more than 2 years later in October 2021, everything was still working perfectly fine and I could have let everything that way. Yet, I thought some things could be improved and I wanted to change some parts of this stack.",{"type":254,"tag":282,"props":51198,"children":51200},{"id":51199},"migrating-from-wyam-to-statiq",[51201],{"type":259,"value":51202},"Migrating from Wyam to Statiq",{"type":254,"tag":455,"props":51204,"children":51206},{"id":51205},"why",[51207],{"type":259,"value":51208},"Why?",{"type":254,"tag":255,"props":51210,"children":51211},{},[51212],{"type":259,"value":51213},"I am convinced that static website generators have a great future and are one of the best solutions for blogs #ThisIsTheWay.",{"type":254,"tag":255,"props":51215,"children":51216},{},[51217,51219,51225,51227,51234],{"type":259,"value":51218},"Although Wyam was working fine for my blog, it has been \"rebranded and rebooted as ",{"type":254,"tag":262,"props":51220,"children":51223},{"href":51221,"rel":51222},"https://www.statiq.dev/",[266],[51224],{"type":259,"value":51105},{"type":259,"value":51226}," with a ton of improvements\" (as you can read on ",{"type":254,"tag":262,"props":51228,"children":51231},{"href":51229,"rel":51230},"https://wyam.io/",[266],[51232],{"type":259,"value":51233},"Wyam website",{"type":259,"value":51235},") so it made sense to migrate to use an actively maintained tool.",{"type":254,"tag":255,"props":51237,"children":51238},{},[51239,51241,51246],{"type":259,"value":51240},"I could have migrated to another static website generator (there are so many of them!) but I like using one written in .NET. If I want to customize how my website is generated, I can easily modify the code as it's .NET code. So ",{"type":254,"tag":389,"props":51242,"children":51244},{"className":51243},[],[51245],{"type":259,"value":51105},{"type":259,"value":51247}," is a good choice for me.",{"type":254,"tag":427,"props":51249,"children":51250},{"icon":429},[51251,51270],{"type":254,"tag":255,"props":51252,"children":51253},{},[51254,51256,51261,51263,51268],{"type":259,"value":51255},"By the way, in this article when I am talking about ",{"type":254,"tag":389,"props":51257,"children":51259},{"className":51258},[],[51260],{"type":259,"value":51105},{"type":259,"value":51262}," I am talking about the static site generator but technically ",{"type":254,"tag":389,"props":51264,"children":51266},{"className":51265},[],[51267],{"type":259,"value":51105},{"type":259,"value":51269}," is more than that. It is a static generation platform that is divided into 3 parts:",{"type":254,"tag":332,"props":51271,"children":51272},{},[51273,51284,51300],{"type":254,"tag":336,"props":51274,"children":51275},{},[51276,51282],{"type":254,"tag":389,"props":51277,"children":51279},{"className":51278},[],[51280],{"type":259,"value":51281},"Statiq Framework",{"type":259,"value":51283}," ➡ a framework to build a custom static generator application",{"type":254,"tag":336,"props":51285,"children":51286},{},[51287,51293,51295],{"type":254,"tag":389,"props":51288,"children":51290},{"className":51289},[],[51291],{"type":259,"value":51292},"Statiq Web",{"type":259,"value":51294}," ➡ a static website generation toolkit built on top of ",{"type":254,"tag":389,"props":51296,"children":51298},{"className":51297},[],[51299],{"type":259,"value":51281},{"type":254,"tag":336,"props":51301,"children":51302},{},[51303,51309,51311,51316,51318,51324],{"type":254,"tag":389,"props":51304,"children":51306},{"className":51305},[],[51307],{"type":259,"value":51308},"Statiq Docs",{"type":259,"value":51310}," ➡ a toolkit built on top of ",{"type":254,"tag":389,"props":51312,"children":51314},{"className":51313},[],[51315],{"type":259,"value":51292},{"type":259,"value":51317}," to generate a site with .NET API documentation\nYou may not know, but the .NET Foundation website is generated using ",{"type":254,"tag":389,"props":51319,"children":51321},{"className":51320},[],[51322],{"type":259,"value":51323},"Statiq.Web",{"type":259,"value":594},{"type":254,"tag":455,"props":51326,"children":51328},{"id":51327},"how",[51329],{"type":259,"value":51330},"How?",{"type":254,"tag":255,"props":51332,"children":51333},{},[51334,51336,51343],{"type":259,"value":51335},"Migration was quite easy (some CSS classes to replace and some configuration to change and that was it!). Statiq has a ",{"type":254,"tag":262,"props":51337,"children":51340},{"href":51338,"rel":51339},"https://www.statiq.dev/web/porting-from-wyam",[266],[51341],{"type":259,"value":51342},"page",{"type":259,"value":51344}," in its documentation to explain how to do that.",{"type":254,"tag":255,"props":51346,"children":51347},{},[51348],{"type":259,"value":51349},"The main difference is that instead of using the Wyam CLI to generate my blog from my articles in markdown and a config file, I now have a .NET console application that I run to do the same.",{"type":254,"tag":455,"props":51351,"children":51353},{"id":51352},"what-changed",[51354],{"type":259,"value":51355},"What changed?",{"type":254,"tag":255,"props":51357,"children":51358},{},[51359,51361,51368],{"type":259,"value":51360},"Although Statiq is a reboot of Wyam, and a lot has changed internally, my blog is pretty much the same as before. Statiq comes with the theme ",{"type":254,"tag":262,"props":51362,"children":51365},{"href":51363,"rel":51364},"https://github.com/statiqdev/CleanBlog",[266],[51366],{"type":259,"value":51367},"CleanBlog",{"type":259,"value":51369}," that I was using before with Wyam so the UI looks the same. However as it uses a more recent version of the theme, the migration brought a few improvements to my blog:",{"type":254,"tag":332,"props":51371,"children":51372},{},[51373,51378],{"type":254,"tag":336,"props":51374,"children":51375},{},[51376],{"type":259,"value":51377},"there is now a search box to search my blog (everything is indexed so finding an article that might interest you is easier than ever)",{"type":254,"tag":336,"props":51379,"children":51380},{},[51381],{"type":259,"value":51382},"the code highlighting is prettier than before",{"type":254,"tag":255,"props":51384,"children":51385},{},[51386],{"type":259,"value":51387},"You can see below an example of that:",{"type":254,"tag":255,"props":51389,"children":51390},{},[51391],{"type":254,"tag":1161,"props":51392,"children":51396},{"alt":51393,"className":51394,"src":51395},"Screenshot of the techwatching.dev blog.",[1165,1166],"/posts/images/migrating_blog_1.png",[],{"type":254,"tag":255,"props":51398,"children":51399},{},[51400,51402,51409],{"type":259,"value":51401},"I also took the opportunity of migrating my blog to correct a lot of grammar and spelling mistakes there were on my articles. To find the mistakes and correct them I used the free version of ",{"type":254,"tag":262,"props":51403,"children":51406},{"href":51404,"rel":51405},"https://www.grammarly.com/",[266],[51407],{"type":259,"value":51408},"Grammarly",{"type":259,"value":51410}," and I was surprised by how good it was. I intend to use it to write my future articles and I strongly recommend it to you. It is especially useful to me as I am not an English native speaker. Of course, I am not saying there will be no mistake in my articles but I should catch most of them with Grammarly.",{"type":254,"tag":282,"props":51412,"children":51414},{"id":51413},"open-sourcing-my-blog",[51415],{"type":259,"value":51416},"Open-sourcing my blog",{"type":254,"tag":255,"props":51418,"children":51419},{},[51420,51422,51428,51430,51436],{"type":259,"value":51421},"People interested in creating their blog sometimes ask me what stack I used to build my blog. So I thought open-sourcing my blog would be a great way to answer this question and show exactly how this blog is built. If you are a .NET developer and want to create your blog, I strongly recommend you to use ",{"type":254,"tag":262,"props":51423,"children":51426},{"href":51424,"rel":51425},"https://www.statiq.dev/web/",[266],[51427],{"type":259,"value":51292},{"type":259,"value":51429},". You can get started by following the documentation, it is very easy. And now, you have an example of a blog using Statiq that you can look at ",{"type":254,"tag":262,"props":51431,"children":51434},{"href":51432,"rel":51433},"https://github.com/techwatching/techwatching.dev",[266],[51435],{"type":259,"value":1414},{"type":259,"value":594},{"type":254,"tag":255,"props":51438,"children":51439},{},[51440,51442,51448],{"type":259,"value":51441},"I am often a consumer of open source code (as we all are these days) so it feels great to be a contributor this time by sharing the source code of my blog (even if there is currently not much customization). I don't expect many people to look at it but at least it's ",{"type":254,"tag":262,"props":51443,"children":51445},{"href":51432,"rel":51444},[266],[51446],{"type":259,"value":51447},"there",{"type":259,"value":51449}," if you care.",{"type":254,"tag":282,"props":51451,"children":51453},{"id":51452},"all-in-on-github",[51454],{"type":259,"value":51455},"All-in on GitHub",{"type":254,"tag":255,"props":51457,"children":51458},{},[51459],{"type":259,"value":51460},"GitHub is one of the best platforms for open source projects. But whether it be for open source or proprietary software, GitHub is above all a great development platform to build, ship, and maintain software. I am more familiar with Azure DevOps which is a great platform too and that I use at work but using GitHub was the opportunity to learn new things. Therefore, I decided to migrate to GitHub for my blog and not only for hosting the code.",{"type":254,"tag":455,"props":51462,"children":51464},{"id":51463},"hosting-the-source-code-of-my-blog",[51465],{"type":259,"value":51466},"Hosting the source code of my blog",{"type":254,"tag":255,"props":51468,"children":51469},{},[51470],{"type":259,"value":51471},"Speaking of hosting the code, here is my new git repository on GitHub:",{"type":254,"tag":255,"props":51473,"children":51474},{},[51475],{"type":254,"tag":1161,"props":51476,"children":51480},{"alt":51477,"className":51478,"src":51479},"Techwatching.dev GitHub repository.",[1165,1166],"/posts/images/migrating_blog_2.png",[],{"type":254,"tag":255,"props":51482,"children":51483},{},[51484],{"type":259,"value":51485},"To move my code to GitHub, I just created a new git repository in GitHub and copy-pasted my code there. Adding GitHub as a new remote of my repository could have been a better way to migrate my code to GitHub while preserving the commit history. However, I did not care about history and took the opportunity to reorganize my code.",{"type":254,"tag":427,"props":51487,"children":51488},{"icon":429},[51489],{"type":254,"tag":255,"props":51490,"children":51491},{},[51492],{"type":259,"value":51493},"I know that it is possible to have public projects/git repositories in Azure DevOps so I could have kept my code there, but as I said, moving to GitHub to store my code was a choice.",{"type":254,"tag":255,"props":51495,"children":51496},{},[51497,51499,51506],{"type":259,"value":51498},"And now that my git repository is on GitHub, I can use the ",{"type":254,"tag":262,"props":51500,"children":51503},{"href":51501,"rel":51502},"https://docs.github.com/en/codespaces/the-githubdev-web-based-editor",[266],[51504],{"type":259,"value":51505},"github.dev",{"type":259,"value":51507}," web-based editor to modify my repository directly in the browser. How cool is that?",{"type":254,"tag":255,"props":51509,"children":51510},{},[51511],{"type":254,"tag":1161,"props":51512,"children":51516},{"alt":51513,"className":51514,"src":51515},"Blog in github.dev editor.",[1165,1166],"/posts/images/migrating_blog_3.png",[],{"type":254,"tag":255,"props":51518,"children":51519},{},[51520,51522,51527],{"type":259,"value":51521},"I don't know if I will use ",{"type":254,"tag":389,"props":51523,"children":51525},{"className":51524},[],[51526],{"type":259,"value":51505},{"type":259,"value":51528}," a lot but I like the fact of being able to work on my blog from any computer without having to install anything.",{"type":254,"tag":427,"props":51530,"children":51531},{"icon":567},[51532],{"type":254,"tag":255,"props":51533,"children":51534},{},[51535,51537,51542,51544,51549,51550,51555],{"type":259,"value":51536},"If you never heard of github.dev, simply go to any GitHub repository in your favorite web browser and press ",{"type":254,"tag":389,"props":51538,"children":51540},{"className":51539},[],[51541],{"type":259,"value":594},{"type":259,"value":51543}," to open this repository in a vscode-like editor that runs entirely in your browser. You can also replace ",{"type":254,"tag":389,"props":51545,"children":51547},{"className":51546},[],[51548],{"type":259,"value":38398},{"type":259,"value":51029},{"type":254,"tag":389,"props":51551,"children":51553},{"className":51552},[],[51554],{"type":259,"value":51505},{"type":259,"value":51556}," in the URL to open it. It even works on your mobile although I don't find it very convenient.",{"type":254,"tag":455,"props":51558,"children":51560},{"id":51559},"building-and-deploying-my-blog",[51561],{"type":259,"value":51562},"Building and deploying my blog",{"type":254,"tag":255,"props":51564,"children":51565},{},[51566],{"type":259,"value":51567},"Coming from Azure DevOps, moving from Azure Pipelines to GitHub Actions was not a big deal. What changed however is that my pipeline doesn't need to install any specific CLI to build and deploy my blog. Indeed, with Statiq my blog is a .NET console application, so my site can be generated directly with the .NET CLI. Statiq as built-in support for deploying a website on different platforms including Netlify thus my pipeline to generate and deploy my site is quite simple:",{"type":254,"tag":612,"props":51569,"children":51572},{"className":51570,"code":51571,"language":233,"meta":205,"style":205},"language-yml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","name: Deploy Blog to Netlify\n\non:\n  push:\n    branches: \n      - main\n  schedule:\n  - cron: \"0 5 * * *\"\n\njobs:\n  build:\n    runs-on: windows-latest\n    steps:\n    - uses: actions/checkout@v2\n      with:\n        ref: main\n        submodules: recursive\n    - uses: actions/setup-dotnet@v1\n      with:\n        dotnet-version: '5.0.x'\n    - run: dotnet run -c Release -- deploy\n      env:\n        NetlifySiteId: ${{ secrets.NetlifySiteId }}\n        NetlifyAccessToken: ${{ secrets.NetlifyAccessToken }}\n",[51573],{"type":254,"tag":389,"props":51574,"children":51575},{"__ignoreMap":205},[51576,51592,51599,51610,51622,51638,51649,51661,51690,51697,51708,51720,51736,51747,51768,51780,51796,51813,51833,51844,51869,51890,51902,51919],{"type":254,"tag":622,"props":51577,"children":51578},{"class":624,"line":625},[51579,51583,51587],{"type":254,"tag":622,"props":51580,"children":51581},{"style":768},[51582],{"type":259,"value":16394},{"type":254,"tag":622,"props":51584,"children":51585},{"style":629},[51586],{"type":259,"value":474},{"type":254,"tag":622,"props":51588,"children":51589},{"style":664},[51590],{"type":259,"value":51591}," Deploy Blog to Netlify\n",{"type":254,"tag":622,"props":51593,"children":51594},{"class":624,"line":635},[51595],{"type":254,"tag":622,"props":51596,"children":51597},{"emptyLinePlaceholder":1699},[51598],{"type":259,"value":1702},{"type":254,"tag":622,"props":51600,"children":51601},{"class":624,"line":679},[51602,51606],{"type":254,"tag":622,"props":51603,"children":51604},{"style":7715},[51605],{"type":259,"value":16411},{"type":254,"tag":622,"props":51607,"children":51608},{"style":629},[51609],{"type":259,"value":16416},{"type":254,"tag":622,"props":51611,"children":51612},{"class":624,"line":705},[51613,51618],{"type":254,"tag":622,"props":51614,"children":51615},{"style":768},[51616],{"type":259,"value":51617},"  push",{"type":254,"tag":622,"props":51619,"children":51620},{"style":629},[51621],{"type":259,"value":16416},{"type":254,"tag":622,"props":51623,"children":51624},{"class":624,"line":732},[51625,51630,51634],{"type":254,"tag":622,"props":51626,"children":51627},{"style":768},[51628],{"type":259,"value":51629},"    branches",{"type":254,"tag":622,"props":51631,"children":51632},{"style":629},[51633],{"type":259,"value":474},{"type":254,"tag":622,"props":51635,"children":51636},{"style":1202},[51637],{"type":259,"value":2314},{"type":254,"tag":622,"props":51639,"children":51640},{"class":624,"line":759},[51641,51645],{"type":254,"tag":622,"props":51642,"children":51643},{"style":629},[51644],{"type":259,"value":16542},{"type":254,"tag":622,"props":51646,"children":51647},{"style":664},[51648],{"type":259,"value":22528},{"type":254,"tag":622,"props":51650,"children":51651},{"class":624,"line":798},[51652,51657],{"type":254,"tag":622,"props":51653,"children":51654},{"style":768},[51655],{"type":259,"value":51656},"  schedule",{"type":254,"tag":622,"props":51658,"children":51659},{"style":629},[51660],{"type":259,"value":16416},{"type":254,"tag":622,"props":51662,"children":51663},{"class":624,"line":836},[51664,51668,51673,51677,51681,51686],{"type":254,"tag":622,"props":51665,"children":51666},{"style":629},[51667],{"type":259,"value":22523},{"type":254,"tag":622,"props":51669,"children":51670},{"style":768},[51671],{"type":259,"value":51672}," cron",{"type":254,"tag":622,"props":51674,"children":51675},{"style":629},[51676],{"type":259,"value":474},{"type":254,"tag":622,"props":51678,"children":51679},{"style":629},[51680],{"type":259,"value":661},{"type":254,"tag":622,"props":51682,"children":51683},{"style":664},[51684],{"type":259,"value":51685},"0 5 * * *",{"type":254,"tag":622,"props":51687,"children":51688},{"style":629},[51689],{"type":259,"value":9909},{"type":254,"tag":622,"props":51691,"children":51692},{"class":624,"line":862},[51693],{"type":254,"tag":622,"props":51694,"children":51695},{"emptyLinePlaceholder":1699},[51696],{"type":259,"value":1702},{"type":254,"tag":622,"props":51698,"children":51699},{"class":624,"line":900},[51700,51704],{"type":254,"tag":622,"props":51701,"children":51702},{"style":768},[51703],{"type":259,"value":16489},{"type":254,"tag":622,"props":51705,"children":51706},{"style":629},[51707],{"type":259,"value":16416},{"type":254,"tag":622,"props":51709,"children":51710},{"class":624,"line":938},[51711,51716],{"type":254,"tag":622,"props":51712,"children":51713},{"style":768},[51714],{"type":259,"value":51715},"  build",{"type":254,"tag":622,"props":51717,"children":51718},{"style":629},[51719],{"type":259,"value":16416},{"type":254,"tag":622,"props":51721,"children":51722},{"class":624,"line":976},[51723,51727,51731],{"type":254,"tag":622,"props":51724,"children":51725},{"style":768},[51726],{"type":259,"value":16513},{"type":254,"tag":622,"props":51728,"children":51729},{"style":629},[51730],{"type":259,"value":474},{"type":254,"tag":622,"props":51732,"children":51733},{"style":664},[51734],{"type":259,"value":51735}," windows-latest\n",{"type":254,"tag":622,"props":51737,"children":51738},{"class":624,"line":1014},[51739,51743],{"type":254,"tag":622,"props":51740,"children":51741},{"style":768},[51742],{"type":259,"value":16530},{"type":254,"tag":622,"props":51744,"children":51745},{"style":629},[51746],{"type":259,"value":16416},{"type":254,"tag":622,"props":51748,"children":51749},{"class":624,"line":1052},[51750,51755,51759,51763],{"type":254,"tag":622,"props":51751,"children":51752},{"style":629},[51753],{"type":259,"value":51754},"    -",{"type":254,"tag":622,"props":51756,"children":51757},{"style":768},[51758],{"type":259,"value":27220},{"type":254,"tag":622,"props":51760,"children":51761},{"style":629},[51762],{"type":259,"value":474},{"type":254,"tag":622,"props":51764,"children":51765},{"style":664},[51766],{"type":259,"value":51767}," actions/checkout@v2\n",{"type":254,"tag":622,"props":51769,"children":51770},{"class":624,"line":1078},[51771,51776],{"type":254,"tag":622,"props":51772,"children":51773},{"style":768},[51774],{"type":259,"value":51775},"      with",{"type":254,"tag":622,"props":51777,"children":51778},{"style":629},[51779],{"type":259,"value":16416},{"type":254,"tag":622,"props":51781,"children":51782},{"class":624,"line":1087},[51783,51788,51792],{"type":254,"tag":622,"props":51784,"children":51785},{"style":768},[51786],{"type":259,"value":51787},"        ref",{"type":254,"tag":622,"props":51789,"children":51790},{"style":629},[51791],{"type":259,"value":474},{"type":254,"tag":622,"props":51793,"children":51794},{"style":664},[51795],{"type":259,"value":22528},{"type":254,"tag":622,"props":51797,"children":51798},{"class":624,"line":1096},[51799,51804,51808],{"type":254,"tag":622,"props":51800,"children":51801},{"style":768},[51802],{"type":259,"value":51803},"        submodules",{"type":254,"tag":622,"props":51805,"children":51806},{"style":629},[51807],{"type":259,"value":474},{"type":254,"tag":622,"props":51809,"children":51810},{"style":664},[51811],{"type":259,"value":51812}," recursive\n",{"type":254,"tag":622,"props":51814,"children":51815},{"class":624,"line":1105},[51816,51820,51824,51828],{"type":254,"tag":622,"props":51817,"children":51818},{"style":629},[51819],{"type":259,"value":51754},{"type":254,"tag":622,"props":51821,"children":51822},{"style":768},[51823],{"type":259,"value":27220},{"type":254,"tag":622,"props":51825,"children":51826},{"style":629},[51827],{"type":259,"value":474},{"type":254,"tag":622,"props":51829,"children":51830},{"style":664},[51831],{"type":259,"value":51832}," actions/setup-dotnet@v1\n",{"type":254,"tag":622,"props":51834,"children":51835},{"class":624,"line":4131},[51836,51840],{"type":254,"tag":622,"props":51837,"children":51838},{"style":768},[51839],{"type":259,"value":51775},{"type":254,"tag":622,"props":51841,"children":51842},{"style":629},[51843],{"type":259,"value":16416},{"type":254,"tag":622,"props":51845,"children":51846},{"class":624,"line":4183},[51847,51852,51856,51860,51865],{"type":254,"tag":622,"props":51848,"children":51849},{"style":768},[51850],{"type":259,"value":51851},"        dotnet-version",{"type":254,"tag":622,"props":51853,"children":51854},{"style":629},[51855],{"type":259,"value":474},{"type":254,"tag":622,"props":51857,"children":51858},{"style":629},[51859],{"type":259,"value":16556},{"type":254,"tag":622,"props":51861,"children":51862},{"style":664},[51863],{"type":259,"value":51864},"5.0.x",{"type":254,"tag":622,"props":51866,"children":51867},{"style":629},[51868],{"type":259,"value":16566},{"type":254,"tag":622,"props":51870,"children":51871},{"class":624,"line":4208},[51872,51876,51881,51885],{"type":254,"tag":622,"props":51873,"children":51874},{"style":629},[51875],{"type":259,"value":51754},{"type":254,"tag":622,"props":51877,"children":51878},{"style":768},[51879],{"type":259,"value":51880}," run",{"type":254,"tag":622,"props":51882,"children":51883},{"style":629},[51884],{"type":259,"value":474},{"type":254,"tag":622,"props":51886,"children":51887},{"style":664},[51888],{"type":259,"value":51889}," dotnet run -c Release -- deploy\n",{"type":254,"tag":622,"props":51891,"children":51892},{"class":624,"line":4216},[51893,51898],{"type":254,"tag":622,"props":51894,"children":51895},{"style":768},[51896],{"type":259,"value":51897},"      env",{"type":254,"tag":622,"props":51899,"children":51900},{"style":629},[51901],{"type":259,"value":16416},{"type":254,"tag":622,"props":51903,"children":51904},{"class":624,"line":4224},[51905,51910,51914],{"type":254,"tag":622,"props":51906,"children":51907},{"style":768},[51908],{"type":259,"value":51909},"        NetlifySiteId",{"type":254,"tag":622,"props":51911,"children":51912},{"style":629},[51913],{"type":259,"value":474},{"type":254,"tag":622,"props":51915,"children":51916},{"style":664},[51917],{"type":259,"value":51918}," ${{ secrets.NetlifySiteId }}\n",{"type":254,"tag":622,"props":51920,"children":51921},{"class":624,"line":4268},[51922,51927,51931],{"type":254,"tag":622,"props":51923,"children":51924},{"style":768},[51925],{"type":259,"value":51926},"        NetlifyAccessToken",{"type":254,"tag":622,"props":51928,"children":51929},{"style":629},[51930],{"type":259,"value":474},{"type":254,"tag":622,"props":51932,"children":51933},{"style":664},[51934],{"type":259,"value":51935}," ${{ secrets.NetlifyAccessToken }}\n",{"type":254,"tag":255,"props":51937,"children":51938},{},[51939],{"type":259,"value":51940},"This pipeline runs every day and each time something is merged on my main branch. This way my website is redeployed when I publish a new article (which generally corresponds to a commit being made on the main branch through the merge of the branch where I was writing the article). This also allows me to finalize an article but publish it at a future date without having to take care of it.",{"type":254,"tag":455,"props":51942,"children":51944},{"id":51943},"hosting-my-website",[51945],{"type":259,"value":51946},"Hosting my website",{"type":254,"tag":255,"props":51948,"children":51949},{},[51950],{"type":259,"value":51951},"This is the only part where I did not really go all-in on GitHub as I did not use GitHub Pages to host my blog, I kept Netlify. I am happy with it, it does the job, costs me nothing, and allows me to have previews of my blog if I want to so there was no reason to change.",{"type":254,"tag":255,"props":51953,"children":51954},{},[51955],{"type":254,"tag":1161,"props":51956,"children":51960},{"alt":51957,"className":51958,"src":51959},"Netlify dashboard the blog.",[1165,1166],"/posts/images/migrating_blog_4.png",[],{"type":254,"tag":455,"props":51962,"children":51964},{"id":51963},"providing-a-comments-system",[51965],{"type":259,"value":51966},"Providing a comments system",{"type":254,"tag":255,"props":51968,"children":51969},{},[51970,51972,51977],{"type":259,"value":51971},"I was previously using the well-known comments system ",{"type":254,"tag":262,"props":51973,"children":51975},{"href":51185,"rel":51974},[266],[51976],{"type":259,"value":51189},{"type":259,"value":51978}," to let readers of my blog comment on the articles. To be honest, it works well, has a lot of features (moderation, monitoring...), and is used on many blogs. However, I have always found it a bit complex for my needs and was annoyed to \"force\" my readers to create a Disqus account to write comments. So when I read articles of people complaining about Disqus ads, tracking and performance issues I knew it was time to find an alternative.",{"type":254,"tag":255,"props":51980,"children":51981},{},[51982,51984,51991,51993,51999,52000,52005],{"type":259,"value":51983},"And there are many alternatives to Disqus! But one I found very interesting was ",{"type":254,"tag":262,"props":51985,"children":51988},{"href":51986,"rel":51987},"https://utteranc.es/",[266],[51989],{"type":259,"value":51990},"utterances",{"type":259,"value":51992},", a GitHub application that relies on GitHub issues to store blog comments. As the creator of this library explains ",{"type":254,"tag":262,"props":51994,"children":51997},{"href":51995,"rel":51996},"https://danyow.net/using-github-issues-for-blog-comments/",[266],[51998],{"type":259,"value":1414},{"type":259,"value":16280},{"type":254,"tag":389,"props":52001,"children":52003},{"className":52002},[],[52004],{"type":259,"value":51990},{"type":259,"value":52006}," served as a POC for the GitHub-based feedback system in Microsoft Docs. I thought using GitHub issues for the comments of my blog was awesome because:",{"type":254,"tag":332,"props":52008,"children":52009},{},[52010,52015],{"type":254,"tag":336,"props":52011,"children":52012},{},[52013],{"type":259,"value":52014},"my readers will not have to create an account to write comments, my readers are developers so they probably already have a GitHub account",{"type":254,"tag":336,"props":52016,"children":52017},{},[52018],{"type":259,"value":52019},"with comments stored as GitHub issues on my GitHub repository, I have everything at the same place (code, pipelines, and comments)",{"type":254,"tag":255,"props":52021,"children":52022},{},[52023,52025,52030,52032,52039,52041,52046,52048,52055,52057,52062,52064,52069],{"type":259,"value":52024},"But you know what? I finally did not choose ",{"type":254,"tag":389,"props":52026,"children":52028},{"className":52027},[],[52029],{"type":259,"value":51990},{"type":259,"value":52031}," as the comments system for my blog 😀. Instead, I chose to use ",{"type":254,"tag":262,"props":52033,"children":52036},{"href":52034,"rel":52035},"https://giscus.app/",[266],[52037],{"type":259,"value":52038},"giscus",{"type":259,"value":52040}," which is similar to ",{"type":254,"tag":389,"props":52042,"children":52044},{"className":52043},[],[52045],{"type":259,"value":51990},{"type":259,"value":52047}," (in fact it is heavily based on it) but stores comments in ",{"type":254,"tag":262,"props":52049,"children":52052},{"href":52050,"rel":52051},"https://docs.github.com/en/discussions",[266],[52053],{"type":259,"value":52054},"GitHub Discussions",{"type":259,"value":52056},". GitHub Discussions did not exist when ",{"type":254,"tag":389,"props":52058,"children":52060},{"className":52059},[],[52061],{"type":259,"value":51990},{"type":259,"value":52063}," was created but I think discussions are a much more appropriate place than GitHub issues to store comments, that's why I chose it. Apart from that, advantages are the same as ",{"type":254,"tag":389,"props":52065,"children":52067},{"className":52066},[],[52068],{"type":259,"value":51990},{"type":259,"value":594},{"type":254,"tag":255,"props":52071,"children":52072},{},[52073],{"type":254,"tag":1161,"props":52074,"children":52078},{"alt":52075,"className":52076,"src":52077},"Comment on the blog using discuss.",[1165,1166],"/posts/images/migrating_blog_5.png",[],{"type":254,"tag":255,"props":52080,"children":52081},{},[52082,52084,52089],{"type":259,"value":52083},"If you don't have a comments system on your blog yet or that your current one does not satisfy you, you should check ",{"type":254,"tag":262,"props":52085,"children":52087},{"href":52034,"rel":52086},[266],[52088],{"type":259,"value":52038},{"type":259,"value":52090},", I am very happy with it.",{"type":254,"tag":427,"props":52092,"children":52093},{"icon":429},[52094],{"type":254,"tag":255,"props":52095,"children":52096},{},[52097,52099,52104,52106,52113,52115,52122],{"type":259,"value":52098},"If you are currently using Disqus and want to migrate to ",{"type":254,"tag":389,"props":52100,"children":52102},{"className":52101},[],[52103],{"type":259,"value":52038},{"type":259,"value":52105}," (while preserving your existing comments), the easiest way I found was to export the Disqus comments in a file, use this ",{"type":254,"tag":262,"props":52107,"children":52110},{"href":52108,"rel":52109},"https://github.com/JuergenGutsch/disqus-to-github-issues",[266],[52111],{"type":259,"value":52112},"tool",{"type":259,"value":52114}," on GitHub to convert them into issues, and manually convert existing issues to discussions on each issue page. You can read this ",{"type":254,"tag":262,"props":52116,"children":52119},{"href":52117,"rel":52118},"https://ljvmiranda921.github.io/notebook/2021/03/26/migrating-from-disqus-to-utterances/",[266],[52120],{"type":259,"value":52121},"blog post",{"type":259,"value":52123}," that partially explains how to do that.",{"type":254,"tag":455,"props":52125,"children":52127},{"id":52126},"managing-my-blog-as-a-project",[52128],{"type":259,"value":52129},"Managing my blog as a project",{"type":254,"tag":255,"props":52131,"children":52132},{},[52133,52135,52142],{"type":259,"value":52134},"My blog is not very hard to manage but there are always things to do or think about: improvements to do, articles to write, ideas to find. Instead of writing that down on my ToDo app or my OneNote as I used to do, I decided to use the ",{"type":254,"tag":262,"props":52136,"children":52139},{"href":52137,"rel":52138},"https://github.com/features/issues/",[266],[52140],{"type":259,"value":52141},"GitHub Issues beta",{"type":259,"value":52143}," that allows you to create a \"project\" with a backlog, boards, and issues that can be categorized with custom fields.",{"type":254,"tag":255,"props":52145,"children":52146},{},[52147],{"type":254,"tag":1161,"props":52148,"children":52152},{"alt":52149,"className":52150,"src":52151},"Github project for the blog.",[1165,1166],"/posts/images/migrating_blog_6.png",[],{"type":254,"tag":282,"props":52154,"children":52155},{"id":43649},[52156],{"type":259,"value":43652},{"type":254,"tag":255,"props":52158,"children":52159},{},[52160],{"type":259,"value":52161},"In this article, I talked to you about what changed on my blog, and what my new stack is:",{"type":254,"tag":332,"props":52163,"children":52164},{},[52165,52175,52186,52196,52207,52223],{"type":254,"tag":336,"props":52166,"children":52167},{},[52168,52173],{"type":254,"tag":262,"props":52169,"children":52171},{"href":51424,"rel":52170},[266],[52172],{"type":259,"value":51105},{"type":259,"value":52174}," a .NET static website generator to generate the website",{"type":254,"tag":336,"props":52176,"children":52177},{},[52178,52184],{"type":254,"tag":262,"props":52179,"children":52182},{"href":52180,"rel":52181},"https://github.com/",[266],[52183],{"type":259,"value":16352},{"type":259,"value":52185}," to store the source code on a public (open source) git repository",{"type":254,"tag":336,"props":52187,"children":52188},{},[52189,52195],{"type":254,"tag":262,"props":52190,"children":52193},{"href":52191,"rel":52192},"https://github.com/features/actions",[266],[52194],{"type":259,"value":16354},{"type":259,"value":51168},{"type":254,"tag":336,"props":52197,"children":52198},{},[52199,52205],{"type":254,"tag":262,"props":52200,"children":52202},{"href":52137,"rel":52201},[266],[52203],{"type":259,"value":52204},"GitHub Issues (beta)",{"type":259,"value":52206}," to manage my ideas for the blog",{"type":254,"tag":336,"props":52208,"children":52209},{},[52210,52215,52216,52221],{"type":254,"tag":262,"props":52211,"children":52213},{"href":52050,"rel":52212},[266],[52214],{"type":259,"value":52054},{"type":259,"value":51029},{"type":254,"tag":262,"props":52217,"children":52219},{"href":52034,"rel":52218},[266],[52220],{"type":259,"value":52038},{"type":259,"value":52222}," to allow visitors to comment my articles",{"type":254,"tag":336,"props":52224,"children":52225},{},[52226,52231],{"type":254,"tag":262,"props":52227,"children":52229},{"href":51174,"rel":52228},[266],[52230],{"type":259,"value":31941},{"type":259,"value":51179},{"type":254,"tag":255,"props":52233,"children":52234},{},[52235],{"type":259,"value":52236},"I like this new stack where most things are centralized in one place: GitHub. I own the content of this blog, but I love that code to generate this blog is open source. There is still plenty of room for improvement on my blog, but I am confident Statiq will help me to continue customizing my blog easily. I hope you liked this article, do not hesitate to comment (you now know how comments work behind the scenes 😀).",{"type":254,"tag":427,"props":52238,"children":52239},{"icon":429},[52240],{"type":254,"tag":255,"props":52241,"children":52242},{},[52243,52245,52251],{"type":259,"value":52244},"If you are reading this article from hashnode or dev.to and not directly on my ",{"type":254,"tag":262,"props":52246,"children":52249},{"href":52247,"rel":52248},"https://techwatching.dev",[266],[52250],{"type":259,"value":32583},{"type":259,"value":52252},", these changes do not apply to you. But do not worry, I will continue to cross-post my articles on these platforms too which have great communities.",{"type":254,"tag":1260,"props":52254,"children":52255},{},[52256],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":52258},[52259,52260,52265,52266,52273],{"id":1299,"depth":635,"text":1302},{"id":51199,"depth":635,"text":51202,"children":52261},[52262,52263,52264],{"id":51205,"depth":679,"text":51208},{"id":51327,"depth":679,"text":51330},{"id":51352,"depth":679,"text":51355},{"id":51413,"depth":635,"text":51416},{"id":51452,"depth":635,"text":51455,"children":52267},[52268,52269,52270,52271,52272],{"id":51463,"depth":679,"text":51466},{"id":51559,"depth":679,"text":51562},{"id":51943,"depth":679,"text":51946},{"id":51963,"depth":679,"text":51966},{"id":52126,"depth":679,"text":52129},{"id":43649,"depth":635,"text":43652},"content:1.posts:27.migrating-blog.md","1.posts/27.migrating-blog.md",{"_path":85,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":84,"description":52277,"lead":52278,"date":52279,"image":52280,"badge":52281,"tags":52282,"body":52284,"_type":1275,"_id":53510,"_source":231,"_file":53511,"_extension":1278},"This week I worked mainly in vs code to do some web development so tips will mostly be about vscode.","vscode, vscode Jest extension, csharp attributes for nullables.","2021-10-18T00:00:00.000Z",{"src":9258},{"label":9260},[9262,37215,1425,52283],"jest",{"type":251,"children":52285,"toc":53503},[52286,52290,52296,52301,52310,52323,52332,52344,52364,52379,52398,52407,52420,52425,52431,52436,52445,52466,52556,52564,52569,52578,52623,52628,52634,52639,52647,52652,52674,52687,52700,53282,53294,53422,53495,53499],{"type":254,"tag":255,"props":52287,"children":52288},{},[52289],{"type":259,"value":52277},{"type":254,"tag":282,"props":52291,"children":52293},{"id":52292},"work-on-different-parts-of-your-file-with-the-new-split-in-group-command",[52294],{"type":259,"value":52295},"Work on different parts of your file with the new \"split in group\" command",{"type":254,"tag":255,"props":52297,"children":52298},{},[52299],{"type":259,"value":52300},"When you are developing a component in Vue.js, it is convenient to be able to work at the same time on the HTML template and the TypeScript code which happen to be in the same file.",{"type":254,"tag":255,"props":52302,"children":52303},{},[52304],{"type":254,"tag":1161,"props":52305,"children":52309},{"alt":52306,"className":52307,"src":52308},"Split in Group option in vscode.",[1165,1166],"/posts/images/w412021tips_vscode_1.png",[],{"type":254,"tag":255,"props":52311,"children":52312},{},[52313,52315,52321],{"type":259,"value":52314},"The latest release of vscode allows you to do that easily by introducing a new command \"Split in Group\" (the shortcut is ",{"type":254,"tag":389,"props":52316,"children":52318},{"className":52317},[],[52319],{"type":259,"value":52320},"Ctrl+K Ctrl+Shift+*",{"type":259,"value":52322}," on Windows)\" that splits an editor into two sides.",{"type":254,"tag":255,"props":52324,"children":52325},{},[52326],{"type":254,"tag":1161,"props":52327,"children":52331},{"alt":52328,"className":52329,"src":52330},"Vue component split in 2 in vscode.",[1165,1166],"/posts/images/w412021tips_vscode_2.png",[],{"type":254,"tag":282,"props":52333,"children":52335},{"id":52334},"vscode-jest-a-must-have-extension-when-using-jest",[52336,52342],{"type":254,"tag":389,"props":52337,"children":52339},{"className":52338},[],[52340],{"type":259,"value":52341},"vscode-jest",{"type":259,"value":52343},", a must-have extension when using Jest",{"type":254,"tag":255,"props":52345,"children":52346},{},[52347,52349,52355,52357,52362],{"type":259,"value":52348},"Many projects are using ",{"type":254,"tag":389,"props":52350,"children":52352},{"className":52351},[],[52353],{"type":259,"value":52354},"Jest",{"type":259,"value":52356}," as their testing framework for the frontend. But I was quite surprised to see that by default there is no test explorer in vscode to run or debug your ",{"type":254,"tag":389,"props":52358,"children":52360},{"className":52359},[],[52361],{"type":259,"value":52354},{"type":259,"value":52363}," tests. I like the command line but for some things, like debugging a specific test, a UI is way more convenient.",{"type":254,"tag":427,"props":52365,"children":52366},{"icon":429},[52367],{"type":254,"tag":255,"props":52368,"children":52369},{},[52370,52372,52378],{"type":259,"value":52371},"From what I understood, there is a native API/UI in vscode that extensions can use to help you managed your tests. You can learn about that ",{"type":254,"tag":262,"props":52373,"children":52376},{"href":52374,"rel":52375},"https://code.visualstudio.com/api/extension-guides/testing",[266],[52377],{"type":259,"value":1414},{"type":259,"value":594},{"type":254,"tag":255,"props":52380,"children":52381},{},[52382,52384,52389,52390,52396],{"type":259,"value":52383},"So we have to install the ",{"type":254,"tag":389,"props":52385,"children":52387},{"className":52386},[],[52388],{"type":259,"value":52341},{"type":259,"value":28819},{"type":254,"tag":262,"props":52391,"children":52394},{"href":52392,"rel":52393},"https://github.com/jest-community/vscode-jest",[266],[52395],{"type":259,"value":42477},{"type":259,"value":52397}," to be able to discover our tests, run them and debug them.",{"type":254,"tag":255,"props":52399,"children":52400},{},[52401],{"type":254,"tag":1161,"props":52402,"children":52406},{"alt":52403,"className":52404,"src":52405},"Jest vscoe extension.",[1165,1166],"/posts/images/w412021tips_jest_1.png",[],{"type":254,"tag":255,"props":52408,"children":52409},{},[52410,52412,52418],{"type":259,"value":52411},"If you are familiar with full-featured IDEs, you will like this extension that gives you a nice test explorer as well as other interesting features such as automatically running tests when code changes are made (similar to ",{"type":254,"tag":389,"props":52413,"children":52415},{"className":52414},[],[52416],{"type":259,"value":52417},"Live Unit Testing",{"type":259,"value":52419}," in Visual Studio )",{"type":254,"tag":255,"props":52421,"children":52422},{},[52423],{"type":259,"value":52424},"That brings me to talking about sharing your vs code extensions with your colleagues.",{"type":254,"tag":282,"props":52426,"children":52428},{"id":52427},"share-to-your-colleagues-the-vscode-extensions-to-use-on-your-project",[52429],{"type":259,"value":52430},"Share to your colleagues the vscode extensions to use on your project",{"type":254,"tag":255,"props":52432,"children":52433},{},[52434],{"type":259,"value":52435},"When you start working on a new project, you often have to find out which extensions your colleagues are using so that you install everything you need to work on the project. If your colleagues are nice, they would have written this kind of information in the README or the wiki of the project. But what if vscode could directly suggest to you which extensions to install when you open the project on your computer.",{"type":254,"tag":255,"props":52437,"children":52438},{},[52439],{"type":254,"tag":1161,"props":52440,"children":52444},{"alt":52441,"className":52442,"src":52443},"Suggested extensions popin in vscode.",[1165,1166],"/posts/images/w412021tips_vscode_3.png",[],{"type":254,"tag":255,"props":52446,"children":52447},{},[52448,52450,52456,52458,52464],{"type":259,"value":52449},"As you can see on the screenshot above that is exactly what vscode can do for you if someone has specified the recommended vscode extensions of the workspace in the ",{"type":254,"tag":389,"props":52451,"children":52453},{"className":52452},[],[52454],{"type":259,"value":52455},"extensions.json",{"type":259,"value":52457}," file of the ",{"type":254,"tag":389,"props":52459,"children":52461},{"className":52460},[],[52462],{"type":259,"value":52463},".vscode",{"type":259,"value":52465}," folder.\nHere is an example of such a file:",{"type":254,"tag":612,"props":52467,"children":52469},{"className":614,"code":52468,"language":616,"meta":205,"style":205},"{\n    \"recommendations\": [\n        \"orta.vscode-jest\",\n        \"sdras.vue-vscode-extensionpack\"\n    ]\n}\n",[52470],{"type":254,"tag":389,"props":52471,"children":52472},{"__ignoreMap":205},[52473,52480,52505,52525,52541,52549],{"type":254,"tag":622,"props":52474,"children":52475},{"class":624,"line":625},[52476],{"type":254,"tag":622,"props":52477,"children":52478},{"style":629},[52479],{"type":259,"value":632},{"type":254,"tag":622,"props":52481,"children":52482},{"class":624,"line":635},[52483,52487,52492,52496,52500],{"type":254,"tag":622,"props":52484,"children":52485},{"style":629},[52486],{"type":259,"value":711},{"type":254,"tag":622,"props":52488,"children":52489},{"style":644},[52490],{"type":259,"value":52491},"recommendations",{"type":254,"tag":622,"props":52493,"children":52494},{"style":629},[52495],{"type":259,"value":652},{"type":254,"tag":622,"props":52497,"children":52498},{"style":629},[52499],{"type":259,"value":474},{"type":254,"tag":622,"props":52501,"children":52502},{"style":629},[52503],{"type":259,"value":52504}," [\n",{"type":254,"tag":622,"props":52506,"children":52507},{"class":624,"line":679},[52508,52512,52517,52521],{"type":254,"tag":622,"props":52509,"children":52510},{"style":629},[52511],{"type":259,"value":765},{"type":254,"tag":622,"props":52513,"children":52514},{"style":664},[52515],{"type":259,"value":52516},"orta.vscode-jest",{"type":254,"tag":622,"props":52518,"children":52519},{"style":629},[52520],{"type":259,"value":652},{"type":254,"tag":622,"props":52522,"children":52523},{"style":629},[52524],{"type":259,"value":676},{"type":254,"tag":622,"props":52526,"children":52527},{"class":624,"line":705},[52528,52532,52537],{"type":254,"tag":622,"props":52529,"children":52530},{"style":629},[52531],{"type":259,"value":765},{"type":254,"tag":622,"props":52533,"children":52534},{"style":664},[52535],{"type":259,"value":52536},"sdras.vue-vscode-extensionpack",{"type":254,"tag":622,"props":52538,"children":52539},{"style":629},[52540],{"type":259,"value":9909},{"type":254,"tag":622,"props":52542,"children":52543},{"class":624,"line":732},[52544],{"type":254,"tag":622,"props":52545,"children":52546},{"style":629},[52547],{"type":259,"value":52548},"    ]\n",{"type":254,"tag":622,"props":52550,"children":52551},{"class":624,"line":759},[52552],{"type":254,"tag":622,"props":52553,"children":52554},{"style":629},[52555],{"type":259,"value":1111},{"type":254,"tag":427,"props":52557,"children":52558},{"icon":429},[52559],{"type":254,"tag":255,"props":52560,"children":52561},{},[52562],{"type":259,"value":52563},"If you don't want vscode to recommend you some extensions, you can also specify a list of unwanted recommendations in this file.",{"type":254,"tag":255,"props":52565,"children":52566},{},[52567],{"type":259,"value":52568},"This is just a list of vscode extensions identifiers. You can find the identifier of an extension on its details page in vscode. You will also find there a button to directly add the extension to your recommended list of extensions.",{"type":254,"tag":255,"props":52570,"children":52571},{},[52572],{"type":254,"tag":1161,"props":52573,"children":52577},{"alt":52574,"className":52575,"src":52576},"Add to workspace recommendation example.",[1165,1166],"/posts/images/w412021tips_vscode_4.png",[],{"type":254,"tag":427,"props":52579,"children":52580},{"icon":429},[52581],{"type":254,"tag":255,"props":52582,"children":52583},{},[52584,52586,52591,52593,52599,52601,52607,52609,52614,52616,52621],{"type":259,"value":52585},"If the ",{"type":254,"tag":389,"props":52587,"children":52589},{"className":52588},[],[52590],{"type":259,"value":52463},{"type":259,"value":52592}," folder is in the ",{"type":254,"tag":389,"props":52594,"children":52596},{"className":52595},[],[52597],{"type":259,"value":52598},"gitignore",{"type":259,"value":52600}," of your repository, do not forget to add this line ",{"type":254,"tag":389,"props":52602,"children":52604},{"className":52603},[],[52605],{"type":259,"value":52606},"!.vscode/extensions.json",{"type":259,"value":52608}," to your ",{"type":254,"tag":389,"props":52610,"children":52612},{"className":52611},[],[52613],{"type":259,"value":52598},{"type":259,"value":52615}," file to be able to add your ",{"type":254,"tag":389,"props":52617,"children":52619},{"className":52618},[],[52620],{"type":259,"value":52455},{"type":259,"value":52622}," file in your repository.",{"type":254,"tag":255,"props":52624,"children":52625},{},[52626],{"type":259,"value":52627},"And finally, let's talk a little bit about C#.",{"type":254,"tag":282,"props":52629,"children":52631},{"id":52630},"help-the-c-compiler-helps-you-with-nullable-reference-types",[52632],{"type":259,"value":52633},"Help the C# compiler helps you with nullable reference types.",{"type":254,"tag":255,"props":52635,"children":52636},{},[52637],{"type":259,"value":52638},"I am a big fan of enabling nullable on c# projects because I am convinced that it enforces you to write better code.",{"type":254,"tag":427,"props":52640,"children":52641},{"icon":429},[52642],{"type":254,"tag":255,"props":52643,"children":52644},{},[52645],{"type":259,"value":52646},"As you can guess I am very happy to see that nullable will be enabled by default on new projects in .NET 6.  I think that warnings as errors for nullable should be set by default as well but that is another story.",{"type":254,"tag":255,"props":52648,"children":52649},{},[52650],{"type":259,"value":52651},"However, sometimes the compiler is not enough clever or does not have enough context to know that some code is fine and that it should not raise a warning. When something like that happens, we often solve this by using one of these 2 solutions:",{"type":254,"tag":332,"props":52653,"children":52654},{},[52655,52669],{"type":254,"tag":336,"props":52656,"children":52657},{},[52658,52660,52667],{"type":259,"value":52659},"Use the ",{"type":254,"tag":262,"props":52661,"children":52664},{"href":52662,"rel":52663},"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-forgiving",[266],[52665],{"type":259,"value":52666},"null forgiving operator",{"type":259,"value":52668}," that should not be used in most cases",{"type":254,"tag":336,"props":52670,"children":52671},{},[52672],{"type":259,"value":52673},"Use an additional and unnecessary check just to please the compiler",{"type":254,"tag":255,"props":52675,"children":52676},{},[52677,52679,52685],{"type":259,"value":52678},"It's because we often forget that we can add attributes on our code for null-state analysis as it is described in the ",{"type":254,"tag":262,"props":52680,"children":52683},{"href":52681,"rel":52682},"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/nullable-analysis",[266],[52684],{"type":259,"value":6340},{"type":259,"value":52686},". These attributes will provide more information to the compiler which will be able to provide more accurate warnings.",{"type":254,"tag":255,"props":52688,"children":52689},{},[52690,52692,52698],{"type":259,"value":52691},"I recently read a Pull Request where the developer was doing an additional check on the nullability of an out parameter coming from a ",{"type":254,"tag":389,"props":52693,"children":52695},{"className":52694},[],[52696],{"type":259,"value":52697},"TryGet",{"type":259,"value":52699}," method like in the example below.",{"type":254,"tag":612,"props":52701,"children":52703},{"className":1423,"code":52702,"language":1425,"meta":205,"style":205},"string name = \".NET 6\";\nvar myStore = new MyAwesomeStore();\nmyStore.Add(\"WelcomeMessage\", \"  Hello \");\n\nif (myStore.TryGet(\"WelcomeMessage\", out var value) && value is not null)\n{\n    var welcomeMessage = value.TrimStart();\n    string message = welcomeMessage + name;\n}\n\npublic class MyAwesomeStore\n{\n    private IDictionary\u003Cstring, string> _store = new Dictionary\u003Cstring, string>();\n\n    public void Add(string key, string value) => _store.Add(key, value);\n    public bool TryGet(string key, out string? value) => _store.TryGetValue(key, out value);\n}\n",[52704],{"type":254,"tag":389,"props":52705,"children":52706},{"__ignoreMap":205},[52707,52739,52768,52823,52830,52912,52919,52952,52986,52993,53000,53016,53023,53092,53099,53181,53275],{"type":254,"tag":622,"props":52708,"children":52709},{"class":624,"line":625},[52710,52714,52718,52722,52726,52731,52735],{"type":254,"tag":622,"props":52711,"children":52712},{"style":629},[52713],{"type":259,"value":2851},{"type":254,"tag":622,"props":52715,"children":52716},{"style":714},[52717],{"type":259,"value":16547},{"type":254,"tag":622,"props":52719,"children":52720},{"style":629},[52721],{"type":259,"value":1619},{"type":254,"tag":622,"props":52723,"children":52724},{"style":629},[52725],{"type":259,"value":661},{"type":254,"tag":622,"props":52727,"children":52728},{"style":664},[52729],{"type":259,"value":52730},".NET 6",{"type":254,"tag":622,"props":52732,"children":52733},{"style":629},[52734],{"type":259,"value":652},{"type":254,"tag":622,"props":52736,"children":52737},{"style":629},[52738],{"type":259,"value":2350},{"type":254,"tag":622,"props":52740,"children":52741},{"class":624,"line":635},[52742,52746,52751,52755,52759,52764],{"type":254,"tag":622,"props":52743,"children":52744},{"style":714},[52745],{"type":259,"value":1710},{"type":254,"tag":622,"props":52747,"children":52748},{"style":714},[52749],{"type":259,"value":52750}," myStore",{"type":254,"tag":622,"props":52752,"children":52753},{"style":629},[52754],{"type":259,"value":1619},{"type":254,"tag":622,"props":52756,"children":52757},{"style":629},[52758],{"type":259,"value":1624},{"type":254,"tag":622,"props":52760,"children":52761},{"style":714},[52762],{"type":259,"value":52763}," MyAwesomeStore",{"type":254,"tag":622,"props":52765,"children":52766},{"style":629},[52767],{"type":259,"value":2798},{"type":254,"tag":622,"props":52769,"children":52770},{"class":624,"line":679},[52771,52776,52780,52785,52789,52793,52798,52802,52806,52810,52815,52819],{"type":254,"tag":622,"props":52772,"children":52773},{"style":1202},[52774],{"type":259,"value":52775},"myStore",{"type":254,"tag":622,"props":52777,"children":52778},{"style":629},[52779],{"type":259,"value":594},{"type":254,"tag":622,"props":52781,"children":52782},{"style":1735},[52783],{"type":259,"value":52784},"Add",{"type":254,"tag":622,"props":52786,"children":52787},{"style":629},[52788],{"type":259,"value":1668},{"type":254,"tag":622,"props":52790,"children":52791},{"style":629},[52792],{"type":259,"value":652},{"type":254,"tag":622,"props":52794,"children":52795},{"style":664},[52796],{"type":259,"value":52797},"WelcomeMessage",{"type":254,"tag":622,"props":52799,"children":52800},{"style":629},[52801],{"type":259,"value":652},{"type":254,"tag":622,"props":52803,"children":52804},{"style":629},[52805],{"type":259,"value":3149},{"type":254,"tag":622,"props":52807,"children":52808},{"style":629},[52809],{"type":259,"value":661},{"type":254,"tag":622,"props":52811,"children":52812},{"style":664},[52813],{"type":259,"value":52814},"  Hello ",{"type":254,"tag":622,"props":52816,"children":52817},{"style":629},[52818],{"type":259,"value":652},{"type":254,"tag":622,"props":52820,"children":52821},{"style":629},[52822],{"type":259,"value":1554},{"type":254,"tag":622,"props":52824,"children":52825},{"class":624,"line":705},[52826],{"type":254,"tag":622,"props":52827,"children":52828},{"emptyLinePlaceholder":1699},[52829],{"type":259,"value":1702},{"type":254,"tag":622,"props":52831,"children":52832},{"class":624,"line":732},[52833,52838,52842,52846,52850,52854,52858,52862,52866,52870,52874,52878,52882,52886,52890,52895,52900,52904,52908],{"type":254,"tag":622,"props":52834,"children":52835},{"style":1196},[52836],{"type":259,"value":52837},"if",{"type":254,"tag":622,"props":52839,"children":52840},{"style":629},[52841],{"type":259,"value":3547},{"type":254,"tag":622,"props":52843,"children":52844},{"style":1202},[52845],{"type":259,"value":52775},{"type":254,"tag":622,"props":52847,"children":52848},{"style":629},[52849],{"type":259,"value":594},{"type":254,"tag":622,"props":52851,"children":52852},{"style":1735},[52853],{"type":259,"value":52697},{"type":254,"tag":622,"props":52855,"children":52856},{"style":629},[52857],{"type":259,"value":1668},{"type":254,"tag":622,"props":52859,"children":52860},{"style":629},[52861],{"type":259,"value":652},{"type":254,"tag":622,"props":52863,"children":52864},{"style":664},[52865],{"type":259,"value":52797},{"type":254,"tag":622,"props":52867,"children":52868},{"style":629},[52869],{"type":259,"value":652},{"type":254,"tag":622,"props":52871,"children":52872},{"style":629},[52873],{"type":259,"value":3149},{"type":254,"tag":622,"props":52875,"children":52876},{"style":644},[52877],{"type":259,"value":2295},{"type":254,"tag":622,"props":52879,"children":52880},{"style":714},[52881],{"type":259,"value":1609},{"type":254,"tag":622,"props":52883,"children":52884},{"style":714},[52885],{"type":259,"value":40978},{"type":254,"tag":622,"props":52887,"children":52888},{"style":629},[52889],{"type":259,"value":2309},{"type":254,"tag":622,"props":52891,"children":52892},{"style":629},[52893],{"type":259,"value":52894}," &&",{"type":254,"tag":622,"props":52896,"children":52897},{"style":1202},[52898],{"type":259,"value":52899}," value ",{"type":254,"tag":622,"props":52901,"children":52902},{"style":629},[52903],{"type":259,"value":2534},{"type":254,"tag":622,"props":52905,"children":52906},{"style":629},[52907],{"type":259,"value":3561},{"type":254,"tag":622,"props":52909,"children":52910},{"style":629},[52911],{"type":259,"value":3566},{"type":254,"tag":622,"props":52913,"children":52914},{"class":624,"line":759},[52915],{"type":254,"tag":622,"props":52916,"children":52917},{"style":629},[52918],{"type":259,"value":632},{"type":254,"tag":622,"props":52920,"children":52921},{"class":624,"line":798},[52922,52926,52931,52935,52939,52943,52948],{"type":254,"tag":622,"props":52923,"children":52924},{"style":714},[52925],{"type":259,"value":2099},{"type":254,"tag":622,"props":52927,"children":52928},{"style":714},[52929],{"type":259,"value":52930}," welcomeMessage",{"type":254,"tag":622,"props":52932,"children":52933},{"style":629},[52934],{"type":259,"value":1619},{"type":254,"tag":622,"props":52936,"children":52937},{"style":1202},[52938],{"type":259,"value":40978},{"type":254,"tag":622,"props":52940,"children":52941},{"style":629},[52942],{"type":259,"value":594},{"type":254,"tag":622,"props":52944,"children":52945},{"style":1735},[52946],{"type":259,"value":52947},"TrimStart",{"type":254,"tag":622,"props":52949,"children":52950},{"style":629},[52951],{"type":259,"value":2798},{"type":254,"tag":622,"props":52953,"children":52954},{"class":624,"line":836},[52955,52959,52964,52968,52973,52978,52982],{"type":254,"tag":622,"props":52956,"children":52957},{"style":629},[52958],{"type":259,"value":1460},{"type":254,"tag":622,"props":52960,"children":52961},{"style":714},[52962],{"type":259,"value":52963}," message",{"type":254,"tag":622,"props":52965,"children":52966},{"style":629},[52967],{"type":259,"value":1619},{"type":254,"tag":622,"props":52969,"children":52970},{"style":1202},[52971],{"type":259,"value":52972}," welcomeMessage ",{"type":254,"tag":622,"props":52974,"children":52975},{"style":629},[52976],{"type":259,"value":52977},"+",{"type":254,"tag":622,"props":52979,"children":52980},{"style":1202},[52981],{"type":259,"value":16547},{"type":254,"tag":622,"props":52983,"children":52984},{"style":629},[52985],{"type":259,"value":2350},{"type":254,"tag":622,"props":52987,"children":52988},{"class":624,"line":862},[52989],{"type":254,"tag":622,"props":52990,"children":52991},{"style":629},[52992],{"type":259,"value":1111},{"type":254,"tag":622,"props":52994,"children":52995},{"class":624,"line":900},[52996],{"type":254,"tag":622,"props":52997,"children":52998},{"emptyLinePlaceholder":1699},[52999],{"type":259,"value":1702},{"type":254,"tag":622,"props":53001,"children":53002},{"class":624,"line":938},[53003,53007,53011],{"type":254,"tag":622,"props":53004,"children":53005},{"style":644},[53006],{"type":259,"value":1437},{"type":254,"tag":622,"props":53008,"children":53009},{"style":714},[53010],{"type":259,"value":6761},{"type":254,"tag":622,"props":53012,"children":53013},{"style":714},[53014],{"type":259,"value":53015}," MyAwesomeStore\n",{"type":254,"tag":622,"props":53017,"children":53018},{"class":624,"line":976},[53019],{"type":254,"tag":622,"props":53020,"children":53021},{"style":629},[53022],{"type":259,"value":632},{"type":254,"tag":622,"props":53024,"children":53025},{"class":624,"line":1014},[53026,53030,53035,53039,53043,53047,53051,53055,53060,53064,53068,53072,53076,53080,53084,53088],{"type":254,"tag":622,"props":53027,"children":53028},{"style":644},[53029],{"type":259,"value":6790},{"type":254,"tag":622,"props":53031,"children":53032},{"style":714},[53033],{"type":259,"value":53034}," IDictionary",{"type":254,"tag":622,"props":53036,"children":53037},{"style":629},[53038],{"type":259,"value":1743},{"type":254,"tag":622,"props":53040,"children":53041},{"style":629},[53042],{"type":259,"value":2851},{"type":254,"tag":622,"props":53044,"children":53045},{"style":629},[53046],{"type":259,"value":3149},{"type":254,"tag":622,"props":53048,"children":53049},{"style":629},[53050],{"type":259,"value":1975},{"type":254,"tag":622,"props":53052,"children":53053},{"style":629},[53054],{"type":259,"value":6813},{"type":254,"tag":622,"props":53056,"children":53057},{"style":714},[53058],{"type":259,"value":53059}," _store",{"type":254,"tag":622,"props":53061,"children":53062},{"style":629},[53063],{"type":259,"value":1619},{"type":254,"tag":622,"props":53065,"children":53066},{"style":629},[53067],{"type":259,"value":1624},{"type":254,"tag":622,"props":53069,"children":53070},{"style":714},[53071],{"type":259,"value":23483},{"type":254,"tag":622,"props":53073,"children":53074},{"style":629},[53075],{"type":259,"value":1743},{"type":254,"tag":622,"props":53077,"children":53078},{"style":629},[53079],{"type":259,"value":2851},{"type":254,"tag":622,"props":53081,"children":53082},{"style":629},[53083],{"type":259,"value":3149},{"type":254,"tag":622,"props":53085,"children":53086},{"style":629},[53087],{"type":259,"value":1975},{"type":254,"tag":622,"props":53089,"children":53090},{"style":629},[53091],{"type":259,"value":6531},{"type":254,"tag":622,"props":53093,"children":53094},{"class":624,"line":1052},[53095],{"type":254,"tag":622,"props":53096,"children":53097},{"emptyLinePlaceholder":1699},[53098],{"type":259,"value":1702},{"type":254,"tag":622,"props":53100,"children":53101},{"class":624,"line":1078},[53102,53106,53110,53115,53119,53123,53128,53132,53136,53140,53144,53148,53152,53156,53160,53164,53169,53173,53177],{"type":254,"tag":622,"props":53103,"children":53104},{"style":644},[53105],{"type":259,"value":1970},{"type":254,"tag":622,"props":53107,"children":53108},{"style":629},[53109],{"type":259,"value":9525},{"type":254,"tag":622,"props":53111,"children":53112},{"style":1735},[53113],{"type":259,"value":53114}," Add",{"type":254,"tag":622,"props":53116,"children":53117},{"style":629},[53118],{"type":259,"value":1668},{"type":254,"tag":622,"props":53120,"children":53121},{"style":629},[53122],{"type":259,"value":2851},{"type":254,"tag":622,"props":53124,"children":53125},{"style":714},[53126],{"type":259,"value":53127}," key",{"type":254,"tag":622,"props":53129,"children":53130},{"style":629},[53131],{"type":259,"value":3149},{"type":254,"tag":622,"props":53133,"children":53134},{"style":629},[53135],{"type":259,"value":1975},{"type":254,"tag":622,"props":53137,"children":53138},{"style":714},[53139],{"type":259,"value":40978},{"type":254,"tag":622,"props":53141,"children":53142},{"style":629},[53143],{"type":259,"value":2309},{"type":254,"tag":622,"props":53145,"children":53146},{"style":629},[53147],{"type":259,"value":2697},{"type":254,"tag":622,"props":53149,"children":53150},{"style":1202},[53151],{"type":259,"value":53059},{"type":254,"tag":622,"props":53153,"children":53154},{"style":629},[53155],{"type":259,"value":594},{"type":254,"tag":622,"props":53157,"children":53158},{"style":1735},[53159],{"type":259,"value":52784},{"type":254,"tag":622,"props":53161,"children":53162},{"style":629},[53163],{"type":259,"value":1668},{"type":254,"tag":622,"props":53165,"children":53166},{"style":1202},[53167],{"type":259,"value":53168},"key",{"type":254,"tag":622,"props":53170,"children":53171},{"style":629},[53172],{"type":259,"value":3149},{"type":254,"tag":622,"props":53174,"children":53175},{"style":1202},[53176],{"type":259,"value":40978},{"type":254,"tag":622,"props":53178,"children":53179},{"style":629},[53180],{"type":259,"value":1554},{"type":254,"tag":622,"props":53182,"children":53183},{"class":624,"line":1087},[53184,53188,53193,53198,53202,53206,53210,53214,53218,53222,53226,53230,53234,53238,53242,53246,53251,53255,53259,53263,53267,53271],{"type":254,"tag":622,"props":53185,"children":53186},{"style":644},[53187],{"type":259,"value":1970},{"type":254,"tag":622,"props":53189,"children":53190},{"style":629},[53191],{"type":259,"value":53192}," bool",{"type":254,"tag":622,"props":53194,"children":53195},{"style":1735},[53196],{"type":259,"value":53197}," TryGet",{"type":254,"tag":622,"props":53199,"children":53200},{"style":629},[53201],{"type":259,"value":1668},{"type":254,"tag":622,"props":53203,"children":53204},{"style":629},[53205],{"type":259,"value":2851},{"type":254,"tag":622,"props":53207,"children":53208},{"style":714},[53209],{"type":259,"value":53127},{"type":254,"tag":622,"props":53211,"children":53212},{"style":629},[53213],{"type":259,"value":3149},{"type":254,"tag":622,"props":53215,"children":53216},{"style":644},[53217],{"type":259,"value":2295},{"type":254,"tag":622,"props":53219,"children":53220},{"style":629},[53221],{"type":259,"value":1975},{"type":254,"tag":622,"props":53223,"children":53224},{"style":629},[53225],{"type":259,"value":1980},{"type":254,"tag":622,"props":53227,"children":53228},{"style":714},[53229],{"type":259,"value":40978},{"type":254,"tag":622,"props":53231,"children":53232},{"style":629},[53233],{"type":259,"value":2309},{"type":254,"tag":622,"props":53235,"children":53236},{"style":629},[53237],{"type":259,"value":2697},{"type":254,"tag":622,"props":53239,"children":53240},{"style":1202},[53241],{"type":259,"value":53059},{"type":254,"tag":622,"props":53243,"children":53244},{"style":629},[53245],{"type":259,"value":594},{"type":254,"tag":622,"props":53247,"children":53248},{"style":1735},[53249],{"type":259,"value":53250},"TryGetValue",{"type":254,"tag":622,"props":53252,"children":53253},{"style":629},[53254],{"type":259,"value":1668},{"type":254,"tag":622,"props":53256,"children":53257},{"style":1202},[53258],{"type":259,"value":53168},{"type":254,"tag":622,"props":53260,"children":53261},{"style":629},[53262],{"type":259,"value":3149},{"type":254,"tag":622,"props":53264,"children":53265},{"style":644},[53266],{"type":259,"value":2295},{"type":254,"tag":622,"props":53268,"children":53269},{"style":1202},[53270],{"type":259,"value":40978},{"type":254,"tag":622,"props":53272,"children":53273},{"style":629},[53274],{"type":259,"value":1554},{"type":254,"tag":622,"props":53276,"children":53277},{"class":624,"line":1096},[53278],{"type":254,"tag":622,"props":53279,"children":53280},{"style":629},[53281],{"type":259,"value":1111},{"type":254,"tag":255,"props":53283,"children":53284},{},[53285,53287,53292],{"type":259,"value":53286},"But if we annotate correctly the ",{"type":254,"tag":389,"props":53288,"children":53290},{"className":53289},[],[53291],{"type":259,"value":52697},{"type":259,"value":53293}," method we don't need this check:",{"type":254,"tag":612,"props":53295,"children":53297},{"className":1423,"code":53296,"language":1425,"meta":205,"style":205},"public bool TryGet(string key, [NotNullWhen(returnValue: true)] out string? value) => _store.TryGetValue(key, out value);\n",[53298],{"type":254,"tag":389,"props":53299,"children":53300},{"__ignoreMap":205},[53301],{"type":254,"tag":622,"props":53302,"children":53303},{"class":624,"line":625},[53304,53308,53312,53316,53320,53324,53328,53332,53336,53341,53345,53350,53354,53358,53362,53366,53370,53374,53378,53382,53386,53390,53394,53398,53402,53406,53410,53414,53418],{"type":254,"tag":622,"props":53305,"children":53306},{"style":644},[53307],{"type":259,"value":1437},{"type":254,"tag":622,"props":53309,"children":53310},{"style":629},[53311],{"type":259,"value":53192},{"type":254,"tag":622,"props":53313,"children":53314},{"style":1735},[53315],{"type":259,"value":53197},{"type":254,"tag":622,"props":53317,"children":53318},{"style":629},[53319],{"type":259,"value":1668},{"type":254,"tag":622,"props":53321,"children":53322},{"style":629},[53323],{"type":259,"value":2851},{"type":254,"tag":622,"props":53325,"children":53326},{"style":714},[53327],{"type":259,"value":53127},{"type":254,"tag":622,"props":53329,"children":53330},{"style":629},[53331],{"type":259,"value":3149},{"type":254,"tag":622,"props":53333,"children":53334},{"style":629},[53335],{"type":259,"value":13676},{"type":254,"tag":622,"props":53337,"children":53338},{"style":714},[53339],{"type":259,"value":53340},"NotNullWhen",{"type":254,"tag":622,"props":53342,"children":53343},{"style":629},[53344],{"type":259,"value":1668},{"type":254,"tag":622,"props":53346,"children":53347},{"style":1202},[53348],{"type":259,"value":53349},"returnValue",{"type":254,"tag":622,"props":53351,"children":53352},{"style":629},[53353],{"type":259,"value":474},{"type":254,"tag":622,"props":53355,"children":53356},{"style":7715},[53357],{"type":259,"value":7718},{"type":254,"tag":622,"props":53359,"children":53360},{"style":629},[53361],{"type":259,"value":9562},{"type":254,"tag":622,"props":53363,"children":53364},{"style":644},[53365],{"type":259,"value":2295},{"type":254,"tag":622,"props":53367,"children":53368},{"style":629},[53369],{"type":259,"value":1975},{"type":254,"tag":622,"props":53371,"children":53372},{"style":629},[53373],{"type":259,"value":1980},{"type":254,"tag":622,"props":53375,"children":53376},{"style":714},[53377],{"type":259,"value":40978},{"type":254,"tag":622,"props":53379,"children":53380},{"style":629},[53381],{"type":259,"value":2309},{"type":254,"tag":622,"props":53383,"children":53384},{"style":629},[53385],{"type":259,"value":2697},{"type":254,"tag":622,"props":53387,"children":53388},{"style":1202},[53389],{"type":259,"value":53059},{"type":254,"tag":622,"props":53391,"children":53392},{"style":629},[53393],{"type":259,"value":594},{"type":254,"tag":622,"props":53395,"children":53396},{"style":1735},[53397],{"type":259,"value":53250},{"type":254,"tag":622,"props":53399,"children":53400},{"style":629},[53401],{"type":259,"value":1668},{"type":254,"tag":622,"props":53403,"children":53404},{"style":1202},[53405],{"type":259,"value":53168},{"type":254,"tag":622,"props":53407,"children":53408},{"style":629},[53409],{"type":259,"value":3149},{"type":254,"tag":622,"props":53411,"children":53412},{"style":644},[53413],{"type":259,"value":2295},{"type":254,"tag":622,"props":53415,"children":53416},{"style":1202},[53417],{"type":259,"value":40978},{"type":254,"tag":622,"props":53419,"children":53420},{"style":629},[53421],{"type":259,"value":1554},{"type":254,"tag":612,"props":53423,"children":53425},{"className":1423,"code":53424,"language":1425,"meta":205,"style":205},"if (myStore.TryGet(\"WelcomeMessage\", out var value))\n{\n",[53426],{"type":254,"tag":389,"props":53427,"children":53428},{"__ignoreMap":205},[53429,53488],{"type":254,"tag":622,"props":53430,"children":53431},{"class":624,"line":625},[53432,53436,53440,53444,53448,53452,53456,53460,53464,53468,53472,53476,53480,53484],{"type":254,"tag":622,"props":53433,"children":53434},{"style":1196},[53435],{"type":259,"value":52837},{"type":254,"tag":622,"props":53437,"children":53438},{"style":629},[53439],{"type":259,"value":3547},{"type":254,"tag":622,"props":53441,"children":53442},{"style":1202},[53443],{"type":259,"value":52775},{"type":254,"tag":622,"props":53445,"children":53446},{"style":629},[53447],{"type":259,"value":594},{"type":254,"tag":622,"props":53449,"children":53450},{"style":1735},[53451],{"type":259,"value":52697},{"type":254,"tag":622,"props":53453,"children":53454},{"style":629},[53455],{"type":259,"value":1668},{"type":254,"tag":622,"props":53457,"children":53458},{"style":629},[53459],{"type":259,"value":652},{"type":254,"tag":622,"props":53461,"children":53462},{"style":664},[53463],{"type":259,"value":52797},{"type":254,"tag":622,"props":53465,"children":53466},{"style":629},[53467],{"type":259,"value":652},{"type":254,"tag":622,"props":53469,"children":53470},{"style":629},[53471],{"type":259,"value":3149},{"type":254,"tag":622,"props":53473,"children":53474},{"style":644},[53475],{"type":259,"value":2295},{"type":254,"tag":622,"props":53477,"children":53478},{"style":714},[53479],{"type":259,"value":1609},{"type":254,"tag":622,"props":53481,"children":53482},{"style":714},[53483],{"type":259,"value":40978},{"type":254,"tag":622,"props":53485,"children":53486},{"style":629},[53487],{"type":259,"value":7086},{"type":254,"tag":622,"props":53489,"children":53490},{"class":624,"line":635},[53491],{"type":254,"tag":622,"props":53492,"children":53493},{"style":629},[53494],{"type":259,"value":632},{"type":254,"tag":255,"props":53496,"children":53497},{},[53498],{"type":259,"value":10040},{"type":254,"tag":1260,"props":53500,"children":53501},{},[53502],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":53504},[53505,53506,53508,53509],{"id":52292,"depth":635,"text":52295},{"id":52334,"depth":635,"text":53507},"vscode-jest, a must-have extension when using Jest",{"id":52427,"depth":635,"text":52430},{"id":52630,"depth":635,"text":52633},"content:1.posts:26.w41-2021-tips-learned-this-week.md","1.posts/26.w41-2021-tips-learned-this-week.md",{"_path":82,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":81,"description":53513,"lead":53514,"date":53515,"image":53516,"badge":53517,"tags":53518,"body":53521,"_type":1275,"_id":53879,"_source":231,"_file":53880,"_extension":1278},"This week is mainly about learning Azure tooling.","Azure emulators, Use my current account Edge extension, Azure icons in Visio, and Azure Charts.","2021-10-04T00:00:00.000Z",{"src":9258},{"label":9260},[9262,6260,47973,53519,53520],"Azure SignalR","Visio",{"type":251,"children":53522,"toc":53870},[53523,53527,53533,53539,53544,53553,53567,53632,53645,53651,53663,53676,53685,53690,53699,53713,53730,53736,53741,53750,53780,53786,53791,53812,53828,53834,53848,53857,53862,53866],{"type":254,"tag":255,"props":53524,"children":53525},{},[53526],{"type":259,"value":53513},{"type":254,"tag":282,"props":53528,"children":53530},{"id":53529},"playing-with-new-emulators",[53531],{"type":259,"value":53532},"Playing with \"new\" emulators",{"type":254,"tag":455,"props":53534,"children":53536},{"id":53535},"azure-signalr-local-emulator",[53537],{"type":259,"value":53538},"Azure SignalR Local Emulator",{"type":254,"tag":255,"props":53540,"children":53541},{},[53542],{"type":259,"value":53543},"This week I was playing with SignalR Service (using the serverless service mode) and I needed to debug my Azure Functions locally. While I was implementing the infrastructure as code to deploy the Azure SignalR Service resource to Azure, I realized that I did not need to do that to test my code as there was a local emulator for SignalR Service. This emulator is currently in preview but looks promising.",{"type":254,"tag":255,"props":53545,"children":53546},{},[53547],{"type":254,"tag":1161,"props":53548,"children":53552},{"alt":53549,"className":53550,"src":53551},"Azure SignalR Service emulator website.",[1165,1166],"/posts/images/w392021tips_nuget_1.png",[],{"type":254,"tag":255,"props":53554,"children":53555},{},[53556,53558,53565],{"type":259,"value":53557},"It is available as a ",{"type":254,"tag":262,"props":53559,"children":53562},{"href":53560,"rel":53561},"https://www.nuget.org/packages/Microsoft.Azure.SignalR.Emulator",[266],[53563],{"type":259,"value":53564},"dotnet tool",{"type":259,"value":53566}," here so you can just install it by using the following command:",{"type":254,"tag":612,"props":53568,"children":53570},{"className":10656,"code":53569,"language":10658,"meta":205,"style":205},"dotnet tool install -g Microsoft.Azure.SignalR.Emulator --version 1.0.0-preview1-10809\n",[53571],{"type":254,"tag":389,"props":53572,"children":53573},{"__ignoreMap":205},[53574],{"type":254,"tag":622,"props":53575,"children":53576},{"class":624,"line":625},[53577,53582,53586,53591,53595,53600,53605,53609,53614,53618,53623,53627],{"type":254,"tag":622,"props":53578,"children":53579},{"style":1202},[53580],{"type":259,"value":53581},"dotnet tool install ",{"type":254,"tag":622,"props":53583,"children":53584},{"style":629},[53585],{"type":259,"value":17065},{"type":254,"tag":622,"props":53587,"children":53588},{"style":1202},[53589],{"type":259,"value":53590},"g Microsoft.Azure.SignalR.Emulator ",{"type":254,"tag":622,"props":53592,"children":53593},{"style":629},[53594],{"type":259,"value":10675},{"type":254,"tag":622,"props":53596,"children":53597},{"style":1202},[53598],{"type":259,"value":53599},"version ",{"type":254,"tag":622,"props":53601,"children":53602},{"style":741},[53603],{"type":259,"value":53604},"1.0",{"type":254,"tag":622,"props":53606,"children":53607},{"style":1202},[53608],{"type":259,"value":594},{"type":254,"tag":622,"props":53610,"children":53611},{"style":741},[53612],{"type":259,"value":53613},"0",{"type":254,"tag":622,"props":53615,"children":53616},{"style":629},[53617],{"type":259,"value":17065},{"type":254,"tag":622,"props":53619,"children":53620},{"style":1202},[53621],{"type":259,"value":53622},"preview1",{"type":254,"tag":622,"props":53624,"children":53625},{"style":629},[53626],{"type":259,"value":17065},{"type":254,"tag":622,"props":53628,"children":53629},{"style":741},[53630],{"type":259,"value":53631},"10809\n",{"type":254,"tag":255,"props":53633,"children":53634},{},[53635,53637,53643],{"type":259,"value":53636},"Have a look at the ",{"type":254,"tag":262,"props":53638,"children":53641},{"href":53639,"rel":53640},"https://github.com/Azure/azure-signalr/blob/dev/docs/emulator.md",[266],[53642],{"type":259,"value":6340},{"type":259,"value":53644}," if you want to learn more about it.",{"type":254,"tag":455,"props":53646,"children":53648},{"id":53647},"azurite",[53649],{"type":259,"value":53650},"Azurite",{"type":254,"tag":255,"props":53652,"children":53653},{},[53654,53656,53661],{"type":259,"value":53655},"You probably have already heard about ",{"type":254,"tag":262,"props":53657,"children":53659},{"href":9681,"rel":53658},[266],[53660],{"type":259,"value":53650},{"type":259,"value":53662}," the cross-platform emulator for local Azure Storage development. It is really useful when you are using queues, tables, or blobs from Azure Storage in your project and you want to debug your code with emulated versions of these services running locally. If you are developing Azure Functions you are certainly using an emulator for the storage but maybe not be aware of it.",{"type":254,"tag":255,"props":53664,"children":53665},{},[53666,53668,53674],{"type":259,"value":53667},"Azurite is the successor of Azure Storage Emulator but as the ",{"type":254,"tag":262,"props":53669,"children":53672},{"href":53670,"rel":53671},"https://docs.microsoft.com/en-us/azure/storage/common/storage-use-emulator",[266],[53673],{"type":259,"value":6340},{"type":259,"value":53675}," says:",{"type":254,"tag":255,"props":53677,"children":53678},{},[53679],{"type":254,"tag":1161,"props":53680,"children":53684},{"alt":53681,"className":53682,"src":53683},"Documentation about Azure Storage emulator deprecation.",[1165,1166],"/posts/images/w392021tips_azurite_1.png",[],{"type":254,"tag":255,"props":53686,"children":53687},{},[53688],{"type":259,"value":53689},"It's funny because I try to keep myself up-to-date on this kind of news but I did not know at all Azure Storage Emulator had been already deprecated in favor of Azurite for quite some time. Yet, as you can see in the following screenshot when you create a Function App with the \"storage emulator\" option selected you can see the Azurite emulator in your connected services:",{"type":254,"tag":255,"props":53691,"children":53692},{},[53693],{"type":254,"tag":1161,"props":53694,"children":53698},{"alt":53695,"className":53696,"src":53697},"Service dependencies in Visual Studio.",[1165,1166],"/posts/images/w392021tips_vs_1.png",[],{"type":254,"tag":255,"props":53700,"children":53701},{},[53702,53704,53711],{"type":259,"value":53703},"You can check this ",{"type":254,"tag":262,"props":53705,"children":53708},{"href":53706,"rel":53707},"https://docs.microsoft.com/en-us/azure/storage/common/storage-use-azurite?tabs=visual-studio",[266],[53709],{"type":259,"value":53710},"tutorial",{"type":259,"value":53712}," to see how to install and use Azurite.",{"type":254,"tag":427,"props":53714,"children":53715},{"icon":429},[53716],{"type":254,"tag":255,"props":53717,"children":53718},{},[53719,53721,53728],{"type":259,"value":53720},"Be aware that Azurite is also available in a ",{"type":254,"tag":262,"props":53722,"children":53725},{"href":53723,"rel":53724},"https://marketplace.visualstudio.com/items?itemName=Azurite.azurite",[266],[53726],{"type":259,"value":53727},"vscode extension",{"type":259,"value":53729}," which allows you to start and stop storage services individually. If you don't want to bother with the command line, using Azurite from vscode is a good option.",{"type":254,"tag":282,"props":53731,"children":53733},{"id":53732},"using-your-edge-profile-and-skipping-the-account-prompt-screen-when-signing-into-microsoft-sites",[53734],{"type":259,"value":53735},"Using your Edge profile and skipping the account prompt screen when signing into Microsoft sites.",{"type":254,"tag":255,"props":53737,"children":53738},{},[53739],{"type":259,"value":53740},"As I have different Azure Active Directory accounts (for my company, for some of the clients I am working for, for testing purposes) it can be quite challenging to manage so I am using different Microsoft Edge profiles to keep things separated and to avoid signing out when switching from one account to another. However, even with different profiles, when you access a Microsoft site (Azure DevOps for instance) the browser prompts you to choose the account you want to use.",{"type":254,"tag":255,"props":53742,"children":53743},{},[53744],{"type":254,"tag":1161,"props":53745,"children":53749},{"alt":53746,"className":53747,"src":53748},"Use my current account extension page.",[1165,1166],"/posts/images/w392021tips_extension_1.png",[],{"type":254,"tag":255,"props":53751,"children":53752},{},[53753,53755,53761,53763,53769,53771,53778],{"type":259,"value":53754},"Claire Novotny (currently executive director at the .NET Foundation and working in the .NET team at Microsoft) developed a Microsoft Edge extension ",{"type":254,"tag":389,"props":53756,"children":53758},{"className":53757},[],[53759],{"type":259,"value":53760},"Use my current account",{"type":259,"value":53762}," that solves this problem. This plugin is available ",{"type":254,"tag":262,"props":53764,"children":53767},{"href":53765,"rel":53766},"https://microsoftedge.microsoft.com/addons/detail/use-my-current-account/hbfacnnpimgddoojjaonnnbeljegicfl",[266],[53768],{"type":259,"value":1414},{"type":259,"value":53770}," to download and you can check the source code on ",{"type":254,"tag":262,"props":53772,"children":53775},{"href":53773,"rel":53774},"https://github.com/novotnyllc/UseMyCurrentAccount",[266],[53776],{"type":259,"value":53777},"this GitHub repository",{"type":259,"value":53779},". It is a simple but very useful extension that I strongly encourage you to download if you use multiple Azure AD accounts.",{"type":254,"tag":282,"props":53781,"children":53783},{"id":53782},"using-up-to-date-azure-icons-in-visio",[53784],{"type":259,"value":53785},"Using up-to-date Azure icons in Visio.",{"type":254,"tag":255,"props":53787,"children":53788},{},[53789],{"type":259,"value":53790},"I use Visio from time to time to create nice-looking Azure architecture diagrams (at least I try 😀). To do that I use Visio packages about Azure some people provide on GitHub and that contain all the Azure resources icons I need.",{"type":254,"tag":255,"props":53792,"children":53793},{},[53794,53796,53802,53804,53810],{"type":259,"value":53795},"Azure icons often change, and there are always new resources added so always downloading the latest version of these Visio packages is a bit cumbersome. Fortunately, my friend Xavier Mignot shared on his ",{"type":254,"tag":262,"props":53797,"children":53800},{"href":53798,"rel":53799},"https://blog.xmi.fr/posts/visio-tips-git-clone/",[266],[53801],{"type":259,"value":32583},{"type":259,"value":53803}," a nice and simple solution for that problem: cloning the Visio packages git repositories under the ",{"type":254,"tag":389,"props":53805,"children":53807},{"className":53806},[],[53808],{"type":259,"value":53809},"My Shapes",{"type":259,"value":53811}," folder and regularly pulling new changes to have always up-to-date Azure icons.",{"type":254,"tag":427,"props":53813,"children":53814},{"icon":429},[53815],{"type":254,"tag":255,"props":53816,"children":53817},{},[53818,53820,53826],{"type":259,"value":53819},"If you want more tips about Visio, check the other articles of Xavier on his ",{"type":254,"tag":262,"props":53821,"children":53824},{"href":53822,"rel":53823},"https://blog.xmi.fr/",[266],[53825],{"type":259,"value":32583},{"type":259,"value":53827},", there are also articles about Azure, IoT, .NET...",{"type":254,"tag":282,"props":53829,"children":53831},{"id":53830},"explore-azure-services-easily-with-azure-charts",[53832],{"type":259,"value":53833},"Explore Azure services easily with Azure Charts",{"type":254,"tag":255,"props":53835,"children":53836},{},[53837,53839,53846],{"type":259,"value":53838},"Microsoft's documentation is great but it is sometimes not easy to have a good overview of the services Azure provides. Finding basic things like the SLA of a service, the regions where it is available, and the learning resources to getting started on it can often take time. Unless you know about Alexey Polkovnikov's website ",{"type":254,"tag":262,"props":53840,"children":53843},{"href":53841,"rel":53842},"https://azurecharts.com/",[266],[53844],{"type":259,"value":53845},"Azure Charts",{"type":259,"value":53847}," which gives you all that and much more in a visual way.",{"type":254,"tag":255,"props":53849,"children":53850},{},[53851],{"type":254,"tag":1161,"props":53852,"children":53856},{"alt":53853,"className":53854,"src":53855},"Azure Charts website.",[1165,1166],"/posts/images/w392021tips_charts_1.png",[],{"type":254,"tag":255,"props":53858,"children":53859},{},[53860],{"type":259,"value":53861},"It's not a website I discovered this week but I showed it to a colleague this week and I thought (as each time I go on this website) that I should use it more often as it is a very useful tool.",{"type":254,"tag":255,"props":53863,"children":53864},{},[53865],{"type":259,"value":10040},{"type":254,"tag":1260,"props":53867,"children":53868},{},[53869],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":53871},[53872,53876,53877,53878],{"id":53529,"depth":635,"text":53532,"children":53873},[53874,53875],{"id":53535,"depth":679,"text":53538},{"id":53647,"depth":679,"text":53650},{"id":53732,"depth":635,"text":53735},{"id":53782,"depth":635,"text":53785},{"id":53830,"depth":635,"text":53833},"content:1.posts:25.w39-2021-tips-learned-this-week.md","1.posts/25.w39-2021-tips-learned-this-week.md",{"_path":79,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":78,"description":53882,"lead":53883,"date":53884,"image":53885,"badge":53887,"tags":53888,"body":53889,"_type":1275,"_id":55859,"_source":231,"_file":55860,"_extension":1278},"Have you ever felt a bit overwhelmed by the configuration in a project, not knowing where to look for the settings between the command line parameters, the environment variables, the configuration files in code, the configuration in Azure, ... ? When developing an ASP.NET Core application there are many places where you can put your configuration which makes it difficult to know where you should put it. Even if the official documentation about configuration in ASP.NET Core is very complete and well written, it only describes what you can use and how to use it, not what you should use and when. In this article,     I will try to answer these questions and give you my opinion about how we should use configuration providers in ASP.NET Core.","How should you use configuration providers in ASP.NET Core?","2021-09-27T00:00:00.000Z",{"src":53886},"/images/tokyo.jpg",{"label":1286},[10627,16047,40204,6260],{"type":251,"children":53890,"toc":55846},[53891,53904,53910,53915,53920,53929,53935,53940,53949,53962,54616,54643,54652,54657,54666,54692,54698,54703,54715,54741,54765,54771,54776,54785,54790,54796,54801,54818,54861,54870,54876,54881,54918,54927,54932,54937,55233,55238,55244,55258,55267,55272,55642,55648,55658,55672,55719,55728,55759,55765,55770,55775,55837,55842],{"type":254,"tag":255,"props":53892,"children":53893},{},[53894,53896,53902],{"type":259,"value":53895},"Have you ever felt a bit overwhelmed by the configuration in a project, not knowing where to look for the settings between the command line parameters, the environment variables, the configuration files in code, the configuration in Azure, ... ? When developing an ASP.NET Core application there are many places where you can put your configuration which makes it difficult to know where you should put it. Even if the ",{"type":254,"tag":262,"props":53897,"children":53900},{"href":53898,"rel":53899},"https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/",[266],[53901],{"type":259,"value":47276},{"type":259,"value":53903}," about configuration in ASP.NET Core is very complete and well written, it only describes what you can use and how to use it, not what you should use and when. In this article,     I will try to answer these questions and give you my opinion about how we should use configuration providers in ASP.NET Core.",{"type":254,"tag":282,"props":53905,"children":53907},{"id":53906},"a-quick-reminder-about-configuration",[53908],{"type":259,"value":53909},"A quick reminder about configuration",{"type":254,"tag":255,"props":53911,"children":53912},{},[53913],{"type":259,"value":53914},"Configuration is what allows you to quickly change how an application behaves by modifying a setting instead of rewriting the code. Configuration/settings are sometimes linked to the environment where your application runs but not always. Generally, when something is likely to change, a good practice is to define a setting for it in the configuration instead of hard coding it in the source code.",{"type":254,"tag":255,"props":53916,"children":53917},{},[53918],{"type":259,"value":53919},"Configuration can take many forms and ASP.NET Core has this powerful concept of configurations providers that read configuration data from different sources. But in the end, configuration is just a collection of key-value pairs.",{"type":254,"tag":255,"props":53921,"children":53922},{},[53923],{"type":254,"tag":1161,"props":53924,"children":53928},{"alt":53925,"className":53926,"src":53927},"Values in Startup.cs configuration in debug.",[1165,1166],"/posts/images/lostinconfiguration_providers_2.png",[],{"type":254,"tag":282,"props":53930,"children":53932},{"id":53931},"why-do-i-have-so-many-configuration-providers-by-default",[53933],{"type":259,"value":53934},"Why do I have so many configuration providers by default?",{"type":254,"tag":255,"props":53936,"children":53937},{},[53938],{"type":259,"value":53939},"When you create a new ASP.NET Core project from a template and run it, you have probably noticed that your configuration is not empty and that by default, multiple configuration providers are already registered.",{"type":254,"tag":255,"props":53941,"children":53942},{},[53943],{"type":254,"tag":1161,"props":53944,"children":53948},{"alt":53945,"className":53946,"src":53947},"Configuration providers in Startup.cs configuration in debug.",[1165,1166],"/posts/images/lostinconfiguration_providers_1.pngpng",[],{"type":254,"tag":255,"props":53950,"children":53951},{},[53952,53954,53960],{"type":259,"value":53953},"Do not worry there is no magic here, it just comes from the call ",{"type":254,"tag":389,"props":53955,"children":53957},{"className":53956},[],[53958],{"type":259,"value":53959},"Host.CreateDefaultBuilder(args)",{"type":259,"value":53961}," in your Program.cs.\nHere is an extract of what does this method:",{"type":254,"tag":612,"props":53963,"children":53965},{"className":1423,"code":53964,"language":1425,"meta":205,"style":205},"builder.ConfigureAppConfiguration((hostingContext, config) =>\n{\n    IHostEnvironment env = hostingContext.HostingEnvironment;\n    bool reloadOnChange = GetReloadConfigOnChangeValue(hostingContext);\n    config.AddJsonFile(\"appsettings.json\", optional: true, reloadOnChange: reloadOnChange)\n          .AddJsonFile($\"appsettings.{env.EnvironmentName}.json\", optional: true, reloadOnChange:reloadOnChange);\n    if (env.IsDevelopment() && env.ApplicationName is { Length: > 0 })\n    {\n        var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));\n        if (appAssembly is not null)\n        {\n            config.AddUserSecrets(appAssembly, optional: true, reloadOnChange: reloadOnChange);\n        }\n    }\n    config.AddEnvironmentVariables();\n    if (args is { Length: > 0 })\n    {\n        config.AddCommandLine(args);\n    }\n})\n",[53966],{"type":254,"tag":389,"props":53967,"children":53968},{"__ignoreMap":205},[53969,54009,54016,54050,54084,54155,54248,54323,54330,54390,54419,54426,54488,54495,54502,54522,54566,54573,54602,54609],{"type":254,"tag":622,"props":53970,"children":53971},{"class":624,"line":625},[53972,53976,53980,53984,53988,53993,53997,54001,54005],{"type":254,"tag":622,"props":53973,"children":53974},{"style":1202},[53975],{"type":259,"value":6499},{"type":254,"tag":622,"props":53977,"children":53978},{"style":629},[53979],{"type":259,"value":594},{"type":254,"tag":622,"props":53981,"children":53982},{"style":1735},[53983],{"type":259,"value":47053},{"type":254,"tag":622,"props":53985,"children":53986},{"style":629},[53987],{"type":259,"value":15923},{"type":254,"tag":622,"props":53989,"children":53990},{"style":714},[53991],{"type":259,"value":53992},"hostingContext",{"type":254,"tag":622,"props":53994,"children":53995},{"style":629},[53996],{"type":259,"value":3149},{"type":254,"tag":622,"props":53998,"children":53999},{"style":714},[54000],{"type":259,"value":20010},{"type":254,"tag":622,"props":54002,"children":54003},{"style":629},[54004],{"type":259,"value":2309},{"type":254,"tag":622,"props":54006,"children":54007},{"style":629},[54008],{"type":259,"value":6569},{"type":254,"tag":622,"props":54010,"children":54011},{"class":624,"line":635},[54012],{"type":254,"tag":622,"props":54013,"children":54014},{"style":629},[54015],{"type":259,"value":632},{"type":254,"tag":622,"props":54017,"children":54018},{"class":624,"line":679},[54019,54024,54028,54032,54037,54041,54046],{"type":254,"tag":622,"props":54020,"children":54021},{"style":714},[54022],{"type":259,"value":54023},"    IHostEnvironment",{"type":254,"tag":622,"props":54025,"children":54026},{"style":714},[54027],{"type":259,"value":32116},{"type":254,"tag":622,"props":54029,"children":54030},{"style":629},[54031],{"type":259,"value":1619},{"type":254,"tag":622,"props":54033,"children":54034},{"style":1202},[54035],{"type":259,"value":54036}," hostingContext",{"type":254,"tag":622,"props":54038,"children":54039},{"style":629},[54040],{"type":259,"value":594},{"type":254,"tag":622,"props":54042,"children":54043},{"style":1202},[54044],{"type":259,"value":54045},"HostingEnvironment",{"type":254,"tag":622,"props":54047,"children":54048},{"style":629},[54049],{"type":259,"value":2350},{"type":254,"tag":622,"props":54051,"children":54052},{"class":624,"line":705},[54053,54058,54063,54067,54072,54076,54080],{"type":254,"tag":622,"props":54054,"children":54055},{"style":629},[54056],{"type":259,"value":54057},"    bool",{"type":254,"tag":622,"props":54059,"children":54060},{"style":714},[54061],{"type":259,"value":54062}," reloadOnChange",{"type":254,"tag":622,"props":54064,"children":54065},{"style":629},[54066],{"type":259,"value":1619},{"type":254,"tag":622,"props":54068,"children":54069},{"style":1735},[54070],{"type":259,"value":54071}," GetReloadConfigOnChangeValue",{"type":254,"tag":622,"props":54073,"children":54074},{"style":629},[54075],{"type":259,"value":1668},{"type":254,"tag":622,"props":54077,"children":54078},{"style":1202},[54079],{"type":259,"value":53992},{"type":254,"tag":622,"props":54081,"children":54082},{"style":629},[54083],{"type":259,"value":1554},{"type":254,"tag":622,"props":54085,"children":54086},{"class":624,"line":732},[54087,54092,54096,54101,54105,54109,54114,54118,54122,54127,54131,54135,54139,54143,54147,54151],{"type":254,"tag":622,"props":54088,"children":54089},{"style":1202},[54090],{"type":259,"value":54091},"    config",{"type":254,"tag":622,"props":54093,"children":54094},{"style":629},[54095],{"type":259,"value":594},{"type":254,"tag":622,"props":54097,"children":54098},{"style":1735},[54099],{"type":259,"value":54100},"AddJsonFile",{"type":254,"tag":622,"props":54102,"children":54103},{"style":629},[54104],{"type":259,"value":1668},{"type":254,"tag":622,"props":54106,"children":54107},{"style":629},[54108],{"type":259,"value":652},{"type":254,"tag":622,"props":54110,"children":54111},{"style":664},[54112],{"type":259,"value":54113},"appsettings.json",{"type":254,"tag":622,"props":54115,"children":54116},{"style":629},[54117],{"type":259,"value":652},{"type":254,"tag":622,"props":54119,"children":54120},{"style":629},[54121],{"type":259,"value":3149},{"type":254,"tag":622,"props":54123,"children":54124},{"style":714},[54125],{"type":259,"value":54126}," optional",{"type":254,"tag":622,"props":54128,"children":54129},{"style":629},[54130],{"type":259,"value":474},{"type":254,"tag":622,"props":54132,"children":54133},{"style":7715},[54134],{"type":259,"value":7718},{"type":254,"tag":622,"props":54136,"children":54137},{"style":629},[54138],{"type":259,"value":3149},{"type":254,"tag":622,"props":54140,"children":54141},{"style":714},[54142],{"type":259,"value":54062},{"type":254,"tag":622,"props":54144,"children":54145},{"style":629},[54146],{"type":259,"value":474},{"type":254,"tag":622,"props":54148,"children":54149},{"style":1202},[54150],{"type":259,"value":54062},{"type":254,"tag":622,"props":54152,"children":54153},{"style":629},[54154],{"type":259,"value":1685},{"type":254,"tag":622,"props":54156,"children":54157},{"class":624,"line":759},[54158,54163,54167,54171,54175,54180,54184,54189,54193,54198,54202,54207,54211,54215,54219,54223,54227,54231,54235,54239,54244],{"type":254,"tag":622,"props":54159,"children":54160},{"style":629},[54161],{"type":259,"value":54162},"          .",{"type":254,"tag":622,"props":54164,"children":54165},{"style":1735},[54166],{"type":259,"value":54100},{"type":254,"tag":622,"props":54168,"children":54169},{"style":629},[54170],{"type":259,"value":1668},{"type":254,"tag":622,"props":54172,"children":54173},{"style":629},[54174],{"type":259,"value":9607},{"type":254,"tag":622,"props":54176,"children":54177},{"style":664},[54178],{"type":259,"value":54179},"appsettings.",{"type":254,"tag":622,"props":54181,"children":54182},{"style":629},[54183],{"type":259,"value":3234},{"type":254,"tag":622,"props":54185,"children":54186},{"style":1202},[54187],{"type":259,"value":54188},"env",{"type":254,"tag":622,"props":54190,"children":54191},{"style":629},[54192],{"type":259,"value":594},{"type":254,"tag":622,"props":54194,"children":54195},{"style":1202},[54196],{"type":259,"value":54197},"EnvironmentName",{"type":254,"tag":622,"props":54199,"children":54200},{"style":629},[54201],{"type":259,"value":2421},{"type":254,"tag":622,"props":54203,"children":54204},{"style":664},[54205],{"type":259,"value":54206},".json",{"type":254,"tag":622,"props":54208,"children":54209},{"style":629},[54210],{"type":259,"value":652},{"type":254,"tag":622,"props":54212,"children":54213},{"style":629},[54214],{"type":259,"value":3149},{"type":254,"tag":622,"props":54216,"children":54217},{"style":714},[54218],{"type":259,"value":54126},{"type":254,"tag":622,"props":54220,"children":54221},{"style":629},[54222],{"type":259,"value":474},{"type":254,"tag":622,"props":54224,"children":54225},{"style":7715},[54226],{"type":259,"value":7718},{"type":254,"tag":622,"props":54228,"children":54229},{"style":629},[54230],{"type":259,"value":3149},{"type":254,"tag":622,"props":54232,"children":54233},{"style":714},[54234],{"type":259,"value":54062},{"type":254,"tag":622,"props":54236,"children":54237},{"style":629},[54238],{"type":259,"value":474},{"type":254,"tag":622,"props":54240,"children":54241},{"style":1202},[54242],{"type":259,"value":54243},"reloadOnChange",{"type":254,"tag":622,"props":54245,"children":54246},{"style":629},[54247],{"type":259,"value":1554},{"type":254,"tag":622,"props":54249,"children":54250},{"class":624,"line":798},[54251,54255,54259,54263,54267,54272,54276,54280,54284,54288,54293,54297,54301,54306,54310,54314,54318],{"type":254,"tag":622,"props":54252,"children":54253},{"style":1196},[54254],{"type":259,"value":3542},{"type":254,"tag":622,"props":54256,"children":54257},{"style":629},[54258],{"type":259,"value":3547},{"type":254,"tag":622,"props":54260,"children":54261},{"style":1202},[54262],{"type":259,"value":54188},{"type":254,"tag":622,"props":54264,"children":54265},{"style":629},[54266],{"type":259,"value":594},{"type":254,"tag":622,"props":54268,"children":54269},{"style":1735},[54270],{"type":259,"value":54271},"IsDevelopment",{"type":254,"tag":622,"props":54273,"children":54274},{"style":629},[54275],{"type":259,"value":2724},{"type":254,"tag":622,"props":54277,"children":54278},{"style":629},[54279],{"type":259,"value":52894},{"type":254,"tag":622,"props":54281,"children":54282},{"style":1202},[54283],{"type":259,"value":32116},{"type":254,"tag":622,"props":54285,"children":54286},{"style":629},[54287],{"type":259,"value":594},{"type":254,"tag":622,"props":54289,"children":54290},{"style":1202},[54291],{"type":259,"value":54292},"ApplicationName ",{"type":254,"tag":622,"props":54294,"children":54295},{"style":629},[54296],{"type":259,"value":2534},{"type":254,"tag":622,"props":54298,"children":54299},{"style":629},[54300],{"type":259,"value":1990},{"type":254,"tag":622,"props":54302,"children":54303},{"style":1202},[54304],{"type":259,"value":54305}," Length",{"type":254,"tag":622,"props":54307,"children":54308},{"style":629},[54309],{"type":259,"value":474},{"type":254,"tag":622,"props":54311,"children":54312},{"style":629},[54313],{"type":259,"value":2729},{"type":254,"tag":622,"props":54315,"children":54316},{"style":741},[54317],{"type":259,"value":6038},{"type":254,"tag":622,"props":54319,"children":54320},{"style":629},[54321],{"type":259,"value":54322}," })\n",{"type":254,"tag":622,"props":54324,"children":54325},{"class":624,"line":836},[54326],{"type":254,"tag":622,"props":54327,"children":54328},{"style":629},[54329],{"type":259,"value":3574},{"type":254,"tag":622,"props":54331,"children":54332},{"class":624,"line":862},[54333,54337,54342,54346,54351,54355,54360,54364,54369,54373,54377,54381,54386],{"type":254,"tag":622,"props":54334,"children":54335},{"style":714},[54336],{"type":259,"value":3582},{"type":254,"tag":622,"props":54338,"children":54339},{"style":714},[54340],{"type":259,"value":54341}," appAssembly",{"type":254,"tag":622,"props":54343,"children":54344},{"style":629},[54345],{"type":259,"value":1619},{"type":254,"tag":622,"props":54347,"children":54348},{"style":1202},[54349],{"type":259,"value":54350}," Assembly",{"type":254,"tag":622,"props":54352,"children":54353},{"style":629},[54354],{"type":259,"value":594},{"type":254,"tag":622,"props":54356,"children":54357},{"style":1735},[54358],{"type":259,"value":54359},"Load",{"type":254,"tag":622,"props":54361,"children":54362},{"style":629},[54363],{"type":259,"value":6598},{"type":254,"tag":622,"props":54365,"children":54366},{"style":714},[54367],{"type":259,"value":54368}," AssemblyName",{"type":254,"tag":622,"props":54370,"children":54371},{"style":629},[54372],{"type":259,"value":1668},{"type":254,"tag":622,"props":54374,"children":54375},{"style":1202},[54376],{"type":259,"value":54188},{"type":254,"tag":622,"props":54378,"children":54379},{"style":629},[54380],{"type":259,"value":594},{"type":254,"tag":622,"props":54382,"children":54383},{"style":1202},[54384],{"type":259,"value":54385},"ApplicationName",{"type":254,"tag":622,"props":54387,"children":54388},{"style":629},[54389],{"type":259,"value":6624},{"type":254,"tag":622,"props":54391,"children":54392},{"class":624,"line":900},[54393,54398,54402,54407,54411,54415],{"type":254,"tag":622,"props":54394,"children":54395},{"style":1196},[54396],{"type":259,"value":54397},"        if",{"type":254,"tag":622,"props":54399,"children":54400},{"style":629},[54401],{"type":259,"value":3547},{"type":254,"tag":622,"props":54403,"children":54404},{"style":1202},[54405],{"type":259,"value":54406},"appAssembly ",{"type":254,"tag":622,"props":54408,"children":54409},{"style":629},[54410],{"type":259,"value":2534},{"type":254,"tag":622,"props":54412,"children":54413},{"style":629},[54414],{"type":259,"value":3561},{"type":254,"tag":622,"props":54416,"children":54417},{"style":629},[54418],{"type":259,"value":3566},{"type":254,"tag":622,"props":54420,"children":54421},{"class":624,"line":938},[54422],{"type":254,"tag":622,"props":54423,"children":54424},{"style":629},[54425],{"type":259,"value":7094},{"type":254,"tag":622,"props":54427,"children":54428},{"class":624,"line":976},[54429,54434,54438,54443,54447,54452,54456,54460,54464,54468,54472,54476,54480,54484],{"type":254,"tag":622,"props":54430,"children":54431},{"style":1202},[54432],{"type":259,"value":54433},"            config",{"type":254,"tag":622,"props":54435,"children":54436},{"style":629},[54437],{"type":259,"value":594},{"type":254,"tag":622,"props":54439,"children":54440},{"style":1735},[54441],{"type":259,"value":54442},"AddUserSecrets",{"type":254,"tag":622,"props":54444,"children":54445},{"style":629},[54446],{"type":259,"value":1668},{"type":254,"tag":622,"props":54448,"children":54449},{"style":1202},[54450],{"type":259,"value":54451},"appAssembly",{"type":254,"tag":622,"props":54453,"children":54454},{"style":629},[54455],{"type":259,"value":3149},{"type":254,"tag":622,"props":54457,"children":54458},{"style":714},[54459],{"type":259,"value":54126},{"type":254,"tag":622,"props":54461,"children":54462},{"style":629},[54463],{"type":259,"value":474},{"type":254,"tag":622,"props":54465,"children":54466},{"style":7715},[54467],{"type":259,"value":7718},{"type":254,"tag":622,"props":54469,"children":54470},{"style":629},[54471],{"type":259,"value":3149},{"type":254,"tag":622,"props":54473,"children":54474},{"style":714},[54475],{"type":259,"value":54062},{"type":254,"tag":622,"props":54477,"children":54478},{"style":629},[54479],{"type":259,"value":474},{"type":254,"tag":622,"props":54481,"children":54482},{"style":1202},[54483],{"type":259,"value":54062},{"type":254,"tag":622,"props":54485,"children":54486},{"style":629},[54487],{"type":259,"value":1554},{"type":254,"tag":622,"props":54489,"children":54490},{"class":624,"line":1014},[54491],{"type":254,"tag":622,"props":54492,"children":54493},{"style":629},[54494],{"type":259,"value":7144},{"type":254,"tag":622,"props":54496,"children":54497},{"class":624,"line":1052},[54498],{"type":254,"tag":622,"props":54499,"children":54500},{"style":629},[54501],{"type":259,"value":1093},{"type":254,"tag":622,"props":54503,"children":54504},{"class":624,"line":1078},[54505,54509,54513,54518],{"type":254,"tag":622,"props":54506,"children":54507},{"style":1202},[54508],{"type":259,"value":54091},{"type":254,"tag":622,"props":54510,"children":54511},{"style":629},[54512],{"type":259,"value":594},{"type":254,"tag":622,"props":54514,"children":54515},{"style":1735},[54516],{"type":259,"value":54517},"AddEnvironmentVariables",{"type":254,"tag":622,"props":54519,"children":54520},{"style":629},[54521],{"type":259,"value":2798},{"type":254,"tag":622,"props":54523,"children":54524},{"class":624,"line":1087},[54525,54529,54533,54538,54542,54546,54550,54554,54558,54562],{"type":254,"tag":622,"props":54526,"children":54527},{"style":1196},[54528],{"type":259,"value":3542},{"type":254,"tag":622,"props":54530,"children":54531},{"style":629},[54532],{"type":259,"value":3547},{"type":254,"tag":622,"props":54534,"children":54535},{"style":1202},[54536],{"type":259,"value":54537},"args ",{"type":254,"tag":622,"props":54539,"children":54540},{"style":629},[54541],{"type":259,"value":2534},{"type":254,"tag":622,"props":54543,"children":54544},{"style":629},[54545],{"type":259,"value":1990},{"type":254,"tag":622,"props":54547,"children":54548},{"style":1202},[54549],{"type":259,"value":54305},{"type":254,"tag":622,"props":54551,"children":54552},{"style":629},[54553],{"type":259,"value":474},{"type":254,"tag":622,"props":54555,"children":54556},{"style":629},[54557],{"type":259,"value":2729},{"type":254,"tag":622,"props":54559,"children":54560},{"style":741},[54561],{"type":259,"value":6038},{"type":254,"tag":622,"props":54563,"children":54564},{"style":629},[54565],{"type":259,"value":54322},{"type":254,"tag":622,"props":54567,"children":54568},{"class":624,"line":1096},[54569],{"type":254,"tag":622,"props":54570,"children":54571},{"style":629},[54572],{"type":259,"value":3574},{"type":254,"tag":622,"props":54574,"children":54575},{"class":624,"line":1105},[54576,54581,54585,54590,54594,54598],{"type":254,"tag":622,"props":54577,"children":54578},{"style":1202},[54579],{"type":259,"value":54580},"        config",{"type":254,"tag":622,"props":54582,"children":54583},{"style":629},[54584],{"type":259,"value":594},{"type":254,"tag":622,"props":54586,"children":54587},{"style":1735},[54588],{"type":259,"value":54589},"AddCommandLine",{"type":254,"tag":622,"props":54591,"children":54592},{"style":629},[54593],{"type":259,"value":1668},{"type":254,"tag":622,"props":54595,"children":54596},{"style":1202},[54597],{"type":259,"value":6487},{"type":254,"tag":622,"props":54599,"children":54600},{"style":629},[54601],{"type":259,"value":1554},{"type":254,"tag":622,"props":54603,"children":54604},{"class":624,"line":4131},[54605],{"type":254,"tag":622,"props":54606,"children":54607},{"style":629},[54608],{"type":259,"value":1093},{"type":254,"tag":622,"props":54610,"children":54611},{"class":624,"line":4183},[54612],{"type":254,"tag":622,"props":54613,"children":54614},{"style":629},[54615],{"type":259,"value":9148},{"type":254,"tag":255,"props":54617,"children":54618},{},[54619,54621,54627,54629,54634,54636,54641],{"type":259,"value":54620},"As you can see, by default this ",{"type":254,"tag":389,"props":54622,"children":54624},{"className":54623},[],[54625],{"type":259,"value":54626},"CreateDefaultBuilder",{"type":259,"value":54628}," method loads configuration data using different configuration providers. You can load additional configuration by calling the ",{"type":254,"tag":389,"props":54630,"children":54632},{"className":54631},[],[54633],{"type":259,"value":47053},{"type":259,"value":54635}," in the ",{"type":254,"tag":389,"props":54637,"children":54639},{"className":54638},[],[54640],{"type":259,"value":3667},{"type":259,"value":54642}," like in the example which uses Azure Key Vault configuration provider.",{"type":254,"tag":255,"props":54644,"children":54645},{},[54646],{"type":254,"tag":1161,"props":54647,"children":54651},{"alt":54648,"className":54649,"src":54650},"Code for using ConfigureAppConfiguration in Program.cs.",[1165,1166],"/posts/images/lostinconfiguration_providers_4.png",[],{"type":254,"tag":255,"props":54653,"children":54654},{},[54655],{"type":259,"value":54656},"It is important to know that order in which the configuration provider is specified matters: in case multiple providers load different values for the same setting, the value of the last provider specifying this setting is the one that will be used in the configuration. You can see in which order the configuration is loaded from different sources in the method documentation.",{"type":254,"tag":255,"props":54658,"children":54659},{},[54660],{"type":254,"tag":1161,"props":54661,"children":54665},{"alt":54662,"className":54663,"src":54664},"CreateDefaultBuilder documentation in IDE.",[1165,1166],"/posts/images/lostinconfiguration_providers_3.png",[],{"type":254,"tag":255,"props":54667,"children":54668},{},[54669,54671,54677,54679,54684,54686,54691],{"type":259,"value":54670},"If the order in which configuration providers are registered by default does not suit you, then you can simply create the ",{"type":254,"tag":389,"props":54672,"children":54674},{"className":54673},[],[54675],{"type":259,"value":54676},"HostBuilder",{"type":259,"value":54678}," yourself in ",{"type":254,"tag":389,"props":54680,"children":54682},{"className":54681},[],[54683],{"type":259,"value":3667},{"type":259,"value":54685}," instead of using ",{"type":254,"tag":389,"props":54687,"children":54689},{"className":54688},[],[54690],{"type":259,"value":54626},{"type":259,"value":7544},{"type":254,"tag":282,"props":54693,"children":54695},{"id":54694},"how-to-have-a-global-view-of-the-configuration-used-by-your-application",[54696],{"type":259,"value":54697},"How to have a global view of the configuration used by your application?",{"type":254,"tag":255,"props":54699,"children":54700},{},[54701],{"type":259,"value":54702},"Having different providers to load the configuration in your application is great but one drawback is that the configuration is scattered all over the place. Indeed sometimes we want to have the complete list of settings an application uses and if we have to look everywhere (key vault, environment variables, JSON files,...) it becomes impossible to manage.",{"type":254,"tag":255,"props":54704,"children":54705},{},[54706,54708,54713],{"type":259,"value":54707},"What I advise is to put all the settings you use in your application in the ",{"type":254,"tag":389,"props":54709,"children":54711},{"className":54710},[],[54712],{"type":259,"value":54113},{"type":259,"value":54714}," file. It does not mean you will put all the values there too or that the values you put there will be the ones that your application will use. But it means that you will have one place where you can quickly look what are the different settings keys. And concerning the values of these settings, if they should come from another file or provider (because they are relative to an environment or because they are secrets for instance), that is not a problem just put a blank value.",{"type":254,"tag":255,"props":54716,"children":54717},{},[54718,54720,54725,54727,54732,54734,54739],{"type":259,"value":54719},"So why using the ",{"type":254,"tag":389,"props":54721,"children":54723},{"className":54722},[],[54724],{"type":259,"value":54113},{"type":259,"value":54726}," file for that? In the last section, I showed the code that loads some configuration by default and you may have noticed that this settings file is the first loaded into configuration. That means any following configuration source will override the existing values coming from the ",{"type":254,"tag":389,"props":54728,"children":54730},{"className":54729},[],[54731],{"type":259,"value":54113},{"type":259,"value":54733}," file, so having all the settings declared in the ",{"type":254,"tag":389,"props":54735,"children":54737},{"className":54736},[],[54738],{"type":259,"value":54113},{"type":259,"value":54740}," file won't be a problem even if some have blank values (they will be overridden by the values loaded after).",{"type":254,"tag":427,"props":54742,"children":54743},{"icon":429},[54744],{"type":254,"tag":255,"props":54745,"children":54746},{},[54747,54749,54756,54758,54764],{"type":259,"value":54748},"When running your application locally, if you want to display what are the values of the settings in your configuration and where their values come from you can read ",{"type":254,"tag":262,"props":54750,"children":54753},{"href":54751,"rel":54752},"https://andrewlock.net/debugging-configuration-values-in-aspnetcore/",[266],[54754],{"type":259,"value":54755},"this article",{"type":259,"value":54757}," by Andrew Lock that explains how to do that using the ",{"type":254,"tag":389,"props":54759,"children":54761},{"className":54760},[],[54762],{"type":259,"value":54763},"IConfigurationRoot.GetDebugView()",{"type":259,"value":7544},{"type":254,"tag":282,"props":54766,"children":54768},{"id":54767},"what-about-configuration-in-an-app-service-or-an-azure-function-is-it-a-specific-configuration-provider",[54769],{"type":259,"value":54770},"What about configuration in an App Service or an Azure Function, is it a specific configuration provider?",{"type":254,"tag":255,"props":54772,"children":54773},{},[54774],{"type":259,"value":54775},"Well, the answer is no. Application settings in the configuration of an App Service or a Function App (the settings you can see in the Azure portal) are passed as environment variables to the application.",{"type":254,"tag":255,"props":54777,"children":54778},{},[54779],{"type":254,"tag":1161,"props":54780,"children":54784},{"alt":54781,"className":54782,"src":54783},"Application settings in the configuration of an App Service.",[1165,1166],"/posts/images/lostinconfiguration_azureconfig_1.png",[],{"type":254,"tag":255,"props":54786,"children":54787},{},[54788],{"type":259,"value":54789},"If you remember in which order the providers are registered, it means that configuration in Azure will override most of the configuration coming from other providers.",{"type":254,"tag":282,"props":54791,"children":54793},{"id":54792},"where-to-put-environment-dependent-configuration",[54794],{"type":259,"value":54795},"Where to put environment-dependent configuration?",{"type":254,"tag":255,"props":54797,"children":54798},{},[54799],{"type":259,"value":54800},"As the name suggests, environment variables are a good place to set your environment-dependent configuration. If you are deploying your ASP.NET Core application in an Azure App Service, you can set these environment variables in the application settings section of your App Service in the Azure portal. However, I guess you are probably using an Infrastructure as Code tool (like Pulumi or Terraform) instead of manually modifying your Azure resources in the portal, so that means your environment-dependent configuration will be stored among your infrastructure code and deployed to Azure with the rest of the infrastructure.",{"type":254,"tag":427,"props":54802,"children":54803},{"icon":429},[54804],{"type":254,"tag":255,"props":54805,"children":54806},{},[54807,54809,54816],{"type":259,"value":54808},"To know more about how are managed Environments in ASP.NET Core you can read ",{"type":254,"tag":262,"props":54810,"children":54813},{"href":54811,"rel":54812},"https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments",[266],[54814],{"type":259,"value":54815},"this page of the official documentation",{"type":259,"value":54817}," that talks about that.",{"type":254,"tag":255,"props":54819,"children":54820},{},[54821,54823,54828,54830,54836,54838,54844,54846,54852,54854,54859],{"type":259,"value":54822},"Using environment variables is a good approach but it is less convenient than having settings in your JSON settings files. Moreover, if these environment settings are only in your cloud resources, you will miss some settings when you debug your application locally. That's why I think it is a good idea to take advantage of ASP.NET Core default behavior  (see ",{"type":254,"tag":389,"props":54824,"children":54826},{"className":54825},[],[54827],{"type":259,"value":54626},{"type":259,"value":54829}," implementation above) of loading the ",{"type":254,"tag":389,"props":54831,"children":54833},{"className":54832},[],[54834],{"type":259,"value":54835},"appsettings",{"type":259,"value":54837}," JSON file corresponding to the current environment. That way, if you have for example a staging environment you can set the environment variable ",{"type":254,"tag":389,"props":54839,"children":54841},{"className":54840},[],[54842],{"type":259,"value":54843},"ASPNETCORE_ENVIRONMENT",{"type":259,"value":54845}," to staging and put all your configuration for staging in the ",{"type":254,"tag":389,"props":54847,"children":54849},{"className":54848},[],[54850],{"type":259,"value":54851},"appsettings.staging.json",{"type":259,"value":54853},". This may not work for everything as some settings depend on your infrastructure deployment (maybe you only know the value of a setting after the deployment of the infrastructure), but in that case you will put these settings in your environment variables thanks to your infrastructure code and anyway that should not prevent you from putting them in your ",{"type":254,"tag":389,"props":54855,"children":54857},{"className":54856},[],[54858],{"type":259,"value":54113},{"type":259,"value":54860}," files afterward to make local debug easier.",{"type":254,"tag":255,"props":54862,"children":54863},{},[54864],{"type":254,"tag":1161,"props":54865,"children":54869},{"alt":54866,"className":54867,"src":54868},"App settings files in files explorer of an IDE.",[1165,1166],"/posts/images/lostinconfiguration_appsettings_1.png",[],{"type":254,"tag":282,"props":54871,"children":54873},{"id":54872},"how-to-deal-with-secrets-in-my-application-configuration",[54874],{"type":259,"value":54875},"How to deal with secrets in my application configuration?",{"type":254,"tag":255,"props":54877,"children":54878},{},[54879],{"type":259,"value":54880},"There are multiple ways of handling secrets but there is one fundamental rule: 'never commit a secret in source control'. Whatever the context, there is no valid reason that justifies putting a value in source code, that's it.",{"type":254,"tag":255,"props":54882,"children":54883},{},[54884,54886,54893,54895,54901,54903,54910,54912,54917],{"type":259,"value":54885},"One easy way to avoid having secrets in your source code is to use ",{"type":254,"tag":262,"props":54887,"children":54890},{"href":54888,"rel":54889},"https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets#secret-manager",[266],[54891],{"type":259,"value":54892},"Secret Manager",{"type":259,"value":54894},". It is a nice tool that allows you to put your secrets in a ",{"type":254,"tag":389,"props":54896,"children":54898},{"className":54897},[],[54899],{"type":259,"value":54900},"secrets.json",{"type":259,"value":54902}," file stored in your user profile directory (thus not committed with the rest of the code). When your application runs locally, secrets are loaded from this file in your application configuration thanks to the ",{"type":254,"tag":262,"props":54904,"children":54907},{"href":54905,"rel":54906},"https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#file-configuration-provider",[266],[54908],{"type":259,"value":54909},"File configuration provider",{"type":259,"value":54911}," and everything works fine without having to put secrets in the configuration files in source control (",{"type":254,"tag":389,"props":54913,"children":54915},{"className":54914},[],[54916],{"type":259,"value":54113},{"type":259,"value":24291},{"type":254,"tag":255,"props":54919,"children":54920},{},[54921],{"type":254,"tag":1161,"props":54922,"children":54926},{"alt":54923,"className":54924,"src":54925},"Secrets.json file and option to generate it in Visual Studio.",[1165,1166],"/posts/images/lostinconfiguration_secrets_1.png",[],{"type":254,"tag":255,"props":54928,"children":54929},{},[54930],{"type":259,"value":54931},"Secret manager is interesting to debug your application locally when developing but apart from that you will want to use a vault to safely store your secrets and make them available to your application. Each cloud provider has its vault solution: Azure Key Vault, AWS Secret Manager, Google Cloud Secret Manager, and there are also third-party vaults like HashiCorp Vault for instance. To integrate with a vault, you could write your own custom provider that loads the secrets from the vault into configuration but for each of these vaults, there is a configuration provider package supported by the editor or the community.",{"type":254,"tag":255,"props":54933,"children":54934},{},[54935],{"type":259,"value":54936},"For instance, to inject secrets from an Azure KeyVault in your configuration you should use the following code:",{"type":254,"tag":612,"props":54938,"children":54940},{"className":1423,"code":54939,"language":1425,"meta":205,"style":205},"public static IHostBuilder CreateHostBuilder(string[] args) =>\n    Host.CreateDefaultBuilder()\n        .ConfigureAppConfiguration((context, config) =>\n        {\n            var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable(\"VaultUri\"));\n            config.AddAzureKeyVault(keyVaultEndpoint, new DefaultAzureCredential());\n        })\n        .ConfigureWebHostDefaults(webBuilder =>\n        {\n            webBuilder.UseStartup\u003CStartup>();\n        });\n",[54941],{"type":254,"tag":389,"props":54942,"children":54943},{"__ignoreMap":205},[54944,54990,55010,55045,55052,55116,55156,55164,55189,55196,55226],{"type":254,"tag":622,"props":54945,"children":54946},{"class":624,"line":625},[54947,54951,54955,54960,54965,54969,54973,54977,54982,54986],{"type":254,"tag":622,"props":54948,"children":54949},{"style":644},[54950],{"type":259,"value":1437},{"type":254,"tag":622,"props":54952,"children":54953},{"style":644},[54954],{"type":259,"value":11637},{"type":254,"tag":622,"props":54956,"children":54957},{"style":714},[54958],{"type":259,"value":54959}," IHostBuilder",{"type":254,"tag":622,"props":54961,"children":54962},{"style":1735},[54963],{"type":259,"value":54964}," CreateHostBuilder",{"type":254,"tag":622,"props":54966,"children":54967},{"style":629},[54968],{"type":259,"value":1668},{"type":254,"tag":622,"props":54970,"children":54971},{"style":629},[54972],{"type":259,"value":2851},{"type":254,"tag":622,"props":54974,"children":54975},{"style":629},[54976],{"type":259,"value":13436},{"type":254,"tag":622,"props":54978,"children":54979},{"style":714},[54980],{"type":259,"value":54981}," args",{"type":254,"tag":622,"props":54983,"children":54984},{"style":629},[54985],{"type":259,"value":2309},{"type":254,"tag":622,"props":54987,"children":54988},{"style":629},[54989],{"type":259,"value":6569},{"type":254,"tag":622,"props":54991,"children":54992},{"class":624,"line":635},[54993,54998,55002,55006],{"type":254,"tag":622,"props":54994,"children":54995},{"style":1202},[54996],{"type":259,"value":54997},"    Host",{"type":254,"tag":622,"props":54999,"children":55000},{"style":629},[55001],{"type":259,"value":594},{"type":254,"tag":622,"props":55003,"children":55004},{"style":1735},[55005],{"type":259,"value":54626},{"type":254,"tag":622,"props":55007,"children":55008},{"style":629},[55009],{"type":259,"value":1634},{"type":254,"tag":622,"props":55011,"children":55012},{"class":624,"line":679},[55013,55017,55021,55025,55029,55033,55037,55041],{"type":254,"tag":622,"props":55014,"children":55015},{"style":629},[55016],{"type":259,"value":7310},{"type":254,"tag":622,"props":55018,"children":55019},{"style":1735},[55020],{"type":259,"value":47053},{"type":254,"tag":622,"props":55022,"children":55023},{"style":629},[55024],{"type":259,"value":15923},{"type":254,"tag":622,"props":55026,"children":55027},{"style":714},[55028],{"type":259,"value":284},{"type":254,"tag":622,"props":55030,"children":55031},{"style":629},[55032],{"type":259,"value":3149},{"type":254,"tag":622,"props":55034,"children":55035},{"style":714},[55036],{"type":259,"value":20010},{"type":254,"tag":622,"props":55038,"children":55039},{"style":629},[55040],{"type":259,"value":2309},{"type":254,"tag":622,"props":55042,"children":55043},{"style":629},[55044],{"type":259,"value":6569},{"type":254,"tag":622,"props":55046,"children":55047},{"class":624,"line":705},[55048],{"type":254,"tag":622,"props":55049,"children":55050},{"style":629},[55051],{"type":259,"value":7094},{"type":254,"tag":622,"props":55053,"children":55054},{"class":624,"line":732},[55055,55060,55065,55069,55073,55077,55081,55086,55090,55095,55099,55103,55108,55112],{"type":254,"tag":622,"props":55056,"children":55057},{"style":714},[55058],{"type":259,"value":55059},"            var",{"type":254,"tag":622,"props":55061,"children":55062},{"style":714},[55063],{"type":259,"value":55064}," keyVaultEndpoint",{"type":254,"tag":622,"props":55066,"children":55067},{"style":629},[55068],{"type":259,"value":1619},{"type":254,"tag":622,"props":55070,"children":55071},{"style":629},[55072],{"type":259,"value":1624},{"type":254,"tag":622,"props":55074,"children":55075},{"style":714},[55076],{"type":259,"value":1663},{"type":254,"tag":622,"props":55078,"children":55079},{"style":629},[55080],{"type":259,"value":1668},{"type":254,"tag":622,"props":55082,"children":55083},{"style":1202},[55084],{"type":259,"value":55085},"Environment",{"type":254,"tag":622,"props":55087,"children":55088},{"style":629},[55089],{"type":259,"value":594},{"type":254,"tag":622,"props":55091,"children":55092},{"style":1735},[55093],{"type":259,"value":55094},"GetEnvironmentVariable",{"type":254,"tag":622,"props":55096,"children":55097},{"style":629},[55098],{"type":259,"value":1668},{"type":254,"tag":622,"props":55100,"children":55101},{"style":629},[55102],{"type":259,"value":652},{"type":254,"tag":622,"props":55104,"children":55105},{"style":664},[55106],{"type":259,"value":55107},"VaultUri",{"type":254,"tag":622,"props":55109,"children":55110},{"style":629},[55111],{"type":259,"value":652},{"type":254,"tag":622,"props":55113,"children":55114},{"style":629},[55115],{"type":259,"value":6624},{"type":254,"tag":622,"props":55117,"children":55118},{"class":624,"line":759},[55119,55123,55127,55131,55135,55140,55144,55148,55152],{"type":254,"tag":622,"props":55120,"children":55121},{"style":1202},[55122],{"type":259,"value":54433},{"type":254,"tag":622,"props":55124,"children":55125},{"style":629},[55126],{"type":259,"value":594},{"type":254,"tag":622,"props":55128,"children":55129},{"style":1735},[55130],{"type":259,"value":47130},{"type":254,"tag":622,"props":55132,"children":55133},{"style":629},[55134],{"type":259,"value":1668},{"type":254,"tag":622,"props":55136,"children":55137},{"style":1202},[55138],{"type":259,"value":55139},"keyVaultEndpoint",{"type":254,"tag":622,"props":55141,"children":55142},{"style":629},[55143],{"type":259,"value":3149},{"type":254,"tag":622,"props":55145,"children":55146},{"style":629},[55147],{"type":259,"value":1624},{"type":254,"tag":622,"props":55149,"children":55150},{"style":714},[55151],{"type":259,"value":6649},{"type":254,"tag":622,"props":55153,"children":55154},{"style":629},[55155],{"type":259,"value":2154},{"type":254,"tag":622,"props":55157,"children":55158},{"class":624,"line":798},[55159],{"type":254,"tag":622,"props":55160,"children":55161},{"style":629},[55162],{"type":259,"value":55163},"        })\n",{"type":254,"tag":622,"props":55165,"children":55166},{"class":624,"line":836},[55167,55171,55176,55180,55185],{"type":254,"tag":622,"props":55168,"children":55169},{"style":629},[55170],{"type":259,"value":7310},{"type":254,"tag":622,"props":55172,"children":55173},{"style":1735},[55174],{"type":259,"value":55175},"ConfigureWebHostDefaults",{"type":254,"tag":622,"props":55177,"children":55178},{"style":629},[55179],{"type":259,"value":1668},{"type":254,"tag":622,"props":55181,"children":55182},{"style":714},[55183],{"type":259,"value":55184},"webBuilder",{"type":254,"tag":622,"props":55186,"children":55187},{"style":629},[55188],{"type":259,"value":6569},{"type":254,"tag":622,"props":55190,"children":55191},{"class":624,"line":862},[55192],{"type":254,"tag":622,"props":55193,"children":55194},{"style":629},[55195],{"type":259,"value":7094},{"type":254,"tag":622,"props":55197,"children":55198},{"class":624,"line":900},[55199,55204,55208,55213,55217,55222],{"type":254,"tag":622,"props":55200,"children":55201},{"style":1202},[55202],{"type":259,"value":55203},"            webBuilder",{"type":254,"tag":622,"props":55205,"children":55206},{"style":629},[55207],{"type":259,"value":594},{"type":254,"tag":622,"props":55209,"children":55210},{"style":1735},[55211],{"type":259,"value":55212},"UseStartup",{"type":254,"tag":622,"props":55214,"children":55215},{"style":629},[55216],{"type":259,"value":1743},{"type":254,"tag":622,"props":55218,"children":55219},{"style":714},[55220],{"type":259,"value":55221},"Startup",{"type":254,"tag":622,"props":55223,"children":55224},{"style":629},[55225],{"type":259,"value":6531},{"type":254,"tag":622,"props":55227,"children":55228},{"class":624,"line":938},[55229],{"type":254,"tag":622,"props":55230,"children":55231},{"style":629},[55232],{"type":259,"value":7806},{"type":254,"tag":255,"props":55234,"children":55235},{},[55236],{"type":259,"value":55237},"Vaults are generally cheap and anyway it is not as if security was an optional feature we should choose to add or not, therefore there is no reason not to use a vault to keep your secrets safe.",{"type":254,"tag":282,"props":55239,"children":55241},{"id":55240},"how-to-share-configuration-between-different-applications",[55242],{"type":259,"value":55243},"How to share configuration between different applications?",{"type":254,"tag":255,"props":55245,"children":55246},{},[55247,55249,55256],{"type":259,"value":55248},"There is a specific Azure component for that which is ",{"type":254,"tag":262,"props":55250,"children":55253},{"href":55251,"rel":55252},"https://docs.microsoft.com/en-us/azure/azure-app-configuration/overview",[266],[55254],{"type":259,"value":55255},"Azure App Configuration",{"type":259,"value":55257},". It provides you a centralized place to manage your configuration for different applications, environments and locations. It also provides you other interesting features like the ability to dynamically change the value of an application setting without the need to restart this application.",{"type":254,"tag":255,"props":55259,"children":55260},{},[55261],{"type":254,"tag":1161,"props":55262,"children":55266},{"alt":55263,"className":55264,"src":55265},"Configuration explorer in an Azure App Configuration resource.",[1165,1166],"/posts/images/lostinconfiguration_appconfig_1.png",[],{"type":254,"tag":255,"props":55268,"children":55269},{},[55270],{"type":259,"value":55271},"Azure App Configuration has its configuration provider that you can register like that:",{"type":254,"tag":612,"props":55273,"children":55275},{"className":1423,"code":55274,"language":1425,"meta":205,"style":205},"public static IHostBuilder CreateHostBuilder(string[] args) =>\n    Host.CreateDefaultBuilder()\n        .ConfigureAppConfiguration((context, config) =>\n        {\n            var settings = config.Build();\n            var appConfigurationConnectionUri = new Uri(settings.GetValue\u003Cstring>(\"Endpoints:AppConfig\"));\n            config.AddAzureAppConfiguration(options =>\n            {\n                options.Connect(appConfigurationConnectionUri, new DefaultAzureCredential());\n            });\n        })\n        .ConfigureWebHostDefaults(webBuilder =>\n        {\n            webBuilder.UseStartup\u003CStartup>();\n        });\n",[55276],{"type":254,"tag":389,"props":55277,"children":55278},{"__ignoreMap":205},[55279,55322,55341,55376,55383,55415,55486,55514,55521,55563,55571,55578,55601,55608,55635],{"type":254,"tag":622,"props":55280,"children":55281},{"class":624,"line":625},[55282,55286,55290,55294,55298,55302,55306,55310,55314,55318],{"type":254,"tag":622,"props":55283,"children":55284},{"style":644},[55285],{"type":259,"value":1437},{"type":254,"tag":622,"props":55287,"children":55288},{"style":644},[55289],{"type":259,"value":11637},{"type":254,"tag":622,"props":55291,"children":55292},{"style":714},[55293],{"type":259,"value":54959},{"type":254,"tag":622,"props":55295,"children":55296},{"style":1735},[55297],{"type":259,"value":54964},{"type":254,"tag":622,"props":55299,"children":55300},{"style":629},[55301],{"type":259,"value":1668},{"type":254,"tag":622,"props":55303,"children":55304},{"style":629},[55305],{"type":259,"value":2851},{"type":254,"tag":622,"props":55307,"children":55308},{"style":629},[55309],{"type":259,"value":13436},{"type":254,"tag":622,"props":55311,"children":55312},{"style":714},[55313],{"type":259,"value":54981},{"type":254,"tag":622,"props":55315,"children":55316},{"style":629},[55317],{"type":259,"value":2309},{"type":254,"tag":622,"props":55319,"children":55320},{"style":629},[55321],{"type":259,"value":6569},{"type":254,"tag":622,"props":55323,"children":55324},{"class":624,"line":635},[55325,55329,55333,55337],{"type":254,"tag":622,"props":55326,"children":55327},{"style":1202},[55328],{"type":259,"value":54997},{"type":254,"tag":622,"props":55330,"children":55331},{"style":629},[55332],{"type":259,"value":594},{"type":254,"tag":622,"props":55334,"children":55335},{"style":1735},[55336],{"type":259,"value":54626},{"type":254,"tag":622,"props":55338,"children":55339},{"style":629},[55340],{"type":259,"value":1634},{"type":254,"tag":622,"props":55342,"children":55343},{"class":624,"line":679},[55344,55348,55352,55356,55360,55364,55368,55372],{"type":254,"tag":622,"props":55345,"children":55346},{"style":629},[55347],{"type":259,"value":7310},{"type":254,"tag":622,"props":55349,"children":55350},{"style":1735},[55351],{"type":259,"value":47053},{"type":254,"tag":622,"props":55353,"children":55354},{"style":629},[55355],{"type":259,"value":15923},{"type":254,"tag":622,"props":55357,"children":55358},{"style":714},[55359],{"type":259,"value":284},{"type":254,"tag":622,"props":55361,"children":55362},{"style":629},[55363],{"type":259,"value":3149},{"type":254,"tag":622,"props":55365,"children":55366},{"style":714},[55367],{"type":259,"value":20010},{"type":254,"tag":622,"props":55369,"children":55370},{"style":629},[55371],{"type":259,"value":2309},{"type":254,"tag":622,"props":55373,"children":55374},{"style":629},[55375],{"type":259,"value":6569},{"type":254,"tag":622,"props":55377,"children":55378},{"class":624,"line":705},[55379],{"type":254,"tag":622,"props":55380,"children":55381},{"style":629},[55382],{"type":259,"value":7094},{"type":254,"tag":622,"props":55384,"children":55385},{"class":624,"line":732},[55386,55390,55395,55399,55403,55407,55411],{"type":254,"tag":622,"props":55387,"children":55388},{"style":714},[55389],{"type":259,"value":55059},{"type":254,"tag":622,"props":55391,"children":55392},{"style":714},[55393],{"type":259,"value":55394}," settings",{"type":254,"tag":622,"props":55396,"children":55397},{"style":629},[55398],{"type":259,"value":1619},{"type":254,"tag":622,"props":55400,"children":55401},{"style":1202},[55402],{"type":259,"value":20010},{"type":254,"tag":622,"props":55404,"children":55405},{"style":629},[55406],{"type":259,"value":594},{"type":254,"tag":622,"props":55408,"children":55409},{"style":1735},[55410],{"type":259,"value":6697},{"type":254,"tag":622,"props":55412,"children":55413},{"style":629},[55414],{"type":259,"value":2798},{"type":254,"tag":622,"props":55416,"children":55417},{"class":624,"line":759},[55418,55422,55427,55431,55435,55439,55443,55448,55452,55457,55461,55465,55469,55473,55478,55482],{"type":254,"tag":622,"props":55419,"children":55420},{"style":714},[55421],{"type":259,"value":55059},{"type":254,"tag":622,"props":55423,"children":55424},{"style":714},[55425],{"type":259,"value":55426}," appConfigurationConnectionUri",{"type":254,"tag":622,"props":55428,"children":55429},{"style":629},[55430],{"type":259,"value":1619},{"type":254,"tag":622,"props":55432,"children":55433},{"style":629},[55434],{"type":259,"value":1624},{"type":254,"tag":622,"props":55436,"children":55437},{"style":714},[55438],{"type":259,"value":1663},{"type":254,"tag":622,"props":55440,"children":55441},{"style":629},[55442],{"type":259,"value":1668},{"type":254,"tag":622,"props":55444,"children":55445},{"style":1202},[55446],{"type":259,"value":55447},"settings",{"type":254,"tag":622,"props":55449,"children":55450},{"style":629},[55451],{"type":259,"value":594},{"type":254,"tag":622,"props":55453,"children":55454},{"style":1735},[55455],{"type":259,"value":55456},"GetValue",{"type":254,"tag":622,"props":55458,"children":55459},{"style":629},[55460],{"type":259,"value":1743},{"type":254,"tag":622,"props":55462,"children":55463},{"style":629},[55464],{"type":259,"value":2851},{"type":254,"tag":622,"props":55466,"children":55467},{"style":629},[55468],{"type":259,"value":3517},{"type":254,"tag":622,"props":55470,"children":55471},{"style":629},[55472],{"type":259,"value":652},{"type":254,"tag":622,"props":55474,"children":55475},{"style":664},[55476],{"type":259,"value":55477},"Endpoints:AppConfig",{"type":254,"tag":622,"props":55479,"children":55480},{"style":629},[55481],{"type":259,"value":652},{"type":254,"tag":622,"props":55483,"children":55484},{"style":629},[55485],{"type":259,"value":6624},{"type":254,"tag":622,"props":55487,"children":55488},{"class":624,"line":798},[55489,55493,55497,55502,55506,55510],{"type":254,"tag":622,"props":55490,"children":55491},{"style":1202},[55492],{"type":259,"value":54433},{"type":254,"tag":622,"props":55494,"children":55495},{"style":629},[55496],{"type":259,"value":594},{"type":254,"tag":622,"props":55498,"children":55499},{"style":1735},[55500],{"type":259,"value":55501},"AddAzureAppConfiguration",{"type":254,"tag":622,"props":55503,"children":55504},{"style":629},[55505],{"type":259,"value":1668},{"type":254,"tag":622,"props":55507,"children":55508},{"style":714},[55509],{"type":259,"value":7680},{"type":254,"tag":622,"props":55511,"children":55512},{"style":629},[55513],{"type":259,"value":6569},{"type":254,"tag":622,"props":55515,"children":55516},{"class":624,"line":836},[55517],{"type":254,"tag":622,"props":55518,"children":55519},{"style":629},[55520],{"type":259,"value":11957},{"type":254,"tag":622,"props":55522,"children":55523},{"class":624,"line":862},[55524,55529,55533,55538,55542,55547,55551,55555,55559],{"type":254,"tag":622,"props":55525,"children":55526},{"style":1202},[55527],{"type":259,"value":55528},"                options",{"type":254,"tag":622,"props":55530,"children":55531},{"style":629},[55532],{"type":259,"value":594},{"type":254,"tag":622,"props":55534,"children":55535},{"style":1735},[55536],{"type":259,"value":55537},"Connect",{"type":254,"tag":622,"props":55539,"children":55540},{"style":629},[55541],{"type":259,"value":1668},{"type":254,"tag":622,"props":55543,"children":55544},{"style":1202},[55545],{"type":259,"value":55546},"appConfigurationConnectionUri",{"type":254,"tag":622,"props":55548,"children":55549},{"style":629},[55550],{"type":259,"value":3149},{"type":254,"tag":622,"props":55552,"children":55553},{"style":629},[55554],{"type":259,"value":1624},{"type":254,"tag":622,"props":55556,"children":55557},{"style":714},[55558],{"type":259,"value":6649},{"type":254,"tag":622,"props":55560,"children":55561},{"style":629},[55562],{"type":259,"value":2154},{"type":254,"tag":622,"props":55564,"children":55565},{"class":624,"line":900},[55566],{"type":254,"tag":622,"props":55567,"children":55568},{"style":629},[55569],{"type":259,"value":55570},"            });\n",{"type":254,"tag":622,"props":55572,"children":55573},{"class":624,"line":938},[55574],{"type":254,"tag":622,"props":55575,"children":55576},{"style":629},[55577],{"type":259,"value":55163},{"type":254,"tag":622,"props":55579,"children":55580},{"class":624,"line":976},[55581,55585,55589,55593,55597],{"type":254,"tag":622,"props":55582,"children":55583},{"style":629},[55584],{"type":259,"value":7310},{"type":254,"tag":622,"props":55586,"children":55587},{"style":1735},[55588],{"type":259,"value":55175},{"type":254,"tag":622,"props":55590,"children":55591},{"style":629},[55592],{"type":259,"value":1668},{"type":254,"tag":622,"props":55594,"children":55595},{"style":714},[55596],{"type":259,"value":55184},{"type":254,"tag":622,"props":55598,"children":55599},{"style":629},[55600],{"type":259,"value":6569},{"type":254,"tag":622,"props":55602,"children":55603},{"class":624,"line":1014},[55604],{"type":254,"tag":622,"props":55605,"children":55606},{"style":629},[55607],{"type":259,"value":7094},{"type":254,"tag":622,"props":55609,"children":55610},{"class":624,"line":1052},[55611,55615,55619,55623,55627,55631],{"type":254,"tag":622,"props":55612,"children":55613},{"style":1202},[55614],{"type":259,"value":55203},{"type":254,"tag":622,"props":55616,"children":55617},{"style":629},[55618],{"type":259,"value":594},{"type":254,"tag":622,"props":55620,"children":55621},{"style":1735},[55622],{"type":259,"value":55212},{"type":254,"tag":622,"props":55624,"children":55625},{"style":629},[55626],{"type":259,"value":1743},{"type":254,"tag":622,"props":55628,"children":55629},{"style":714},[55630],{"type":259,"value":55221},{"type":254,"tag":622,"props":55632,"children":55633},{"style":629},[55634],{"type":259,"value":6531},{"type":254,"tag":622,"props":55636,"children":55637},{"class":624,"line":1078},[55638],{"type":254,"tag":622,"props":55639,"children":55640},{"style":629},[55641],{"type":259,"value":7806},{"type":254,"tag":282,"props":55643,"children":55645},{"id":55644},"which-configuration-provider-can-help-me-to-use-ab-testing-and-feature-flags-in-my-code",[55646],{"type":259,"value":55647},"Which configuration provider can help me to use A/B testing and feature flags in my code?",{"type":254,"tag":255,"props":55649,"children":55650},{},[55651,55656],{"type":254,"tag":262,"props":55652,"children":55654},{"href":55251,"rel":55653},[266],[55655],{"type":259,"value":55255},{"type":259,"value":55657}," is also the configuration provider to use when you want to use A/B testing or feature flags.\nThere are probably other configuration providers that allow you to do that but this is the one I know.",{"type":254,"tag":282,"props":55659,"children":55661},{"id":55660},"what-is-the-point-of-using-the-launchsettingsjson-file",[55662,55664,55670],{"type":259,"value":55663},"What is the point of using the ",{"type":254,"tag":389,"props":55665,"children":55667},{"className":55666},[],[55668],{"type":259,"value":55669},"launchsettings.json",{"type":259,"value":55671}," file?",{"type":254,"tag":255,"props":55673,"children":55674},{},[55675,55677,55682,55684,55689,55691,55696,55698,55704,55706,55711,55713,55718],{"type":259,"value":55676},"Unlike the ",{"type":254,"tag":389,"props":55678,"children":55680},{"className":55679},[],[55681],{"type":259,"value":54835},{"type":259,"value":55683}," files, the ",{"type":254,"tag":389,"props":55685,"children":55687},{"className":55686},[],[55688],{"type":259,"value":55669},{"type":259,"value":55690}," file is not used when you build and deploy your application somewhere. It is only useful to debug your application code locally. The tools you use to run your application on your local machine (your IDE or the .NET Core CLI) will use the settings in this file to provide configuration to your apps through environments variables or command line arguments. By default, this file will set the ",{"type":254,"tag":389,"props":55692,"children":55694},{"className":55693},[],[55695],{"type":259,"value":54843},{"type":259,"value":55697}," environment variable (or ",{"type":254,"tag":389,"props":55699,"children":55701},{"className":55700},[],[55702],{"type":259,"value":55703},"DOTNET_ENVIRONMENT",{"type":259,"value":55705}," depending on whether your application is an ASP.NET Core application or not) to ",{"type":254,"tag":389,"props":55707,"children":55709},{"className":55708},[],[55710],{"type":259,"value":1286},{"type":259,"value":55712}," so that you debug your code with the ",{"type":254,"tag":389,"props":55714,"children":55716},{"className":55715},[],[55717],{"type":259,"value":1286},{"type":259,"value":15964},{"type":254,"tag":255,"props":55720,"children":55721},{},[55722],{"type":254,"tag":1161,"props":55723,"children":55727},{"alt":55724,"className":55725,"src":55726},"Example of a launchsettings.json file.",[1165,1166],"/posts/images/lostinconfiguration_launchsettings_1.png",[],{"type":254,"tag":427,"props":55729,"children":55730},{"icon":429},[55731],{"type":254,"tag":255,"props":55732,"children":55733},{},[55734,55736,55741,55743,55749,55751,55757],{"type":259,"value":55735},"You can define multiple profiles in the ",{"type":254,"tag":389,"props":55737,"children":55739},{"className":55738},[],[55740],{"type":259,"value":55669},{"type":259,"value":55742}," file to be able to run your application with multiple configurations. It can be interesting to create a ",{"type":254,"tag":389,"props":55744,"children":55746},{"className":55745},[],[55747],{"type":259,"value":55748},"Staging profile",{"type":259,"value":55750}," to debug your application with the ",{"type":254,"tag":389,"props":55752,"children":55754},{"className":55753},[],[55755],{"type":259,"value":55756},"Staging",{"type":259,"value":55758}," configuration; for instance to reproduce a bug that only happens in Staging environment.",{"type":254,"tag":282,"props":55760,"children":55762},{"id":55761},"lets-recap",[55763],{"type":259,"value":55764},"Let's recap.",{"type":254,"tag":255,"props":55766,"children":55767},{},[55768],{"type":259,"value":55769},"To know where to put your settings in an ASP.NET Core project you have to ask yourself some questions: is this setting secret or not, does its value depends on the environment, will it be shared with other applications... ?",{"type":254,"tag":255,"props":55771,"children":55772},{},[55773],{"type":259,"value":55774},"My point of view about how to handle the configuration in an ASP.NET Core project is the following:",{"type":254,"tag":332,"props":55776,"children":55777},{},[55778,55790,55803,55808,55820,55832],{"type":254,"tag":336,"props":55779,"children":55780},{},[55781,55783,55788],{"type":259,"value":55782},"put all your settings in the ",{"type":254,"tag":389,"props":55784,"children":55786},{"className":55785},[],[55787],{"type":259,"value":54113},{"type":259,"value":55789}," file (without necessarily putting the values of the settings) to have a global view of the configuration used by your application",{"type":254,"tag":336,"props":55791,"children":55792},{},[55793,55795,55801],{"type":259,"value":55794},"keep a ",{"type":254,"tag":389,"props":55796,"children":55798},{"className":55797},[],[55799],{"type":259,"value":55800},"appsettings.{EnvironmentName}.json",{"type":259,"value":55802}," file by environment in your project with only the settings that are dependent on the environment",{"type":254,"tag":336,"props":55804,"children":55805},{},[55806],{"type":259,"value":55807},"never store secrets in source code and use an Azure Key Vault to store the settings that are secrets",{"type":254,"tag":336,"props":55809,"children":55810},{},[55811,55813,55818],{"type":259,"value":55812},"create different profiles in your ",{"type":254,"tag":389,"props":55814,"children":55816},{"className":55815},[],[55817],{"type":259,"value":55669},{"type":259,"value":55819},"file to debug your application locally with different configurations",{"type":254,"tag":336,"props":55821,"children":55822},{},[55823,55825,55830],{"type":259,"value":55824},"use environment variables (through ",{"type":254,"tag":389,"props":55826,"children":55828},{"className":55827},[],[55829],{"type":259,"value":55669},{"type":259,"value":55831}," locally and application settings in Azure) to override settings previously defined in other configuration sources",{"type":254,"tag":336,"props":55833,"children":55834},{},[55835],{"type":259,"value":55836},"think about Azure App Configuration if you need to share some settings among different applications",{"type":254,"tag":255,"props":55838,"children":55839},{},[55840],{"type":259,"value":55841},"I hope this article has answered some of your questions about configuration ASP.NET Core and that you feel less lost now. Have fun with configuration providers!",{"type":254,"tag":1260,"props":55843,"children":55844},{},[55845],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":55847},[55848,55849,55850,55851,55852,55853,55854,55855,55856,55858],{"id":53906,"depth":635,"text":53909},{"id":53931,"depth":635,"text":53934},{"id":54694,"depth":635,"text":54697},{"id":54767,"depth":635,"text":54770},{"id":54792,"depth":635,"text":54795},{"id":54872,"depth":635,"text":54875},{"id":55240,"depth":635,"text":55243},{"id":55644,"depth":635,"text":55647},{"id":55660,"depth":635,"text":55857},"What is the point of using the launchsettings.json file?",{"id":55761,"depth":635,"text":55764},"content:1.posts:24.lost-in-configuration.md","1.posts/24.lost-in-configuration.md",{"_path":76,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":75,"description":55862,"date":55863,"image":55864,"badge":55866,"tags":55867,"body":55868,"_type":1275,"_id":57792,"_source":231,"_file":57793,"_extension":1278},"If you are using Azure Functions chances are you are using the setting AzureWebJobsStorage in your Function App configuration. And it is quite likely that the value of this setting which is a secret is stored in a non-secured way directly in your Function App configuration, available to anyone who has access to this configuration. But do not worry, we will see in this article how we can make your Function App more secure by removing this secret.","2021-09-15T00:00:00.000Z",{"src":55865},"/images/lightning_2.jpg",{"label":16349},[1362,10225,19569,23935,6260],{"type":251,"children":55869,"toc":57781},[55870,55883,55888,55894,55907,55919,55928,55934,55939,55948,55953,55959,55973,55982,55995,56008,56023,56029,56041,56047,56052,56070,56075,56081,56094,56099,56205,56218,56428,56433,56703,56715,57369,57391,57608,57616,57635,57648,57654,57659,57694,57703,57716,57721,57750,57755,57763,57767,57772,57777],{"type":254,"tag":255,"props":55871,"children":55872},{},[55873,55875,55881],{"type":259,"value":55874},"If you are using Azure Functions chances are you are using the setting ",{"type":254,"tag":389,"props":55876,"children":55878},{"className":55877},[],[55879],{"type":259,"value":55880},"AzureWebJobsStorage",{"type":259,"value":55882}," in your Function App configuration. And it is quite likely that the value of this setting which is a secret is stored in a non-secured way directly in your Function App configuration, available to anyone who has access to this configuration. But do not worry, we will see in this article how we can make your Function App more secure by removing this secret.",{"type":254,"tag":255,"props":55884,"children":55885},{},[55886],{"type":259,"value":55887},"But first, let's start at the beginning.",{"type":254,"tag":282,"props":55889,"children":55891},{"id":55890},"what-is-this-azurewebjobsstorage-setting",[55892],{"type":259,"value":55893},"What is this AzureWebJobsStorage setting?",{"type":254,"tag":255,"props":55895,"children":55896},{},[55897,55899,55905],{"type":259,"value":55898},"As explained in the ",{"type":254,"tag":262,"props":55900,"children":55903},{"href":55901,"rel":55902},"https://docs.microsoft.com/en-us/azure/azure-functions/storage-considerations#storage-account-requirements",[266],[55904],{"type":259,"value":6340},{"type":259,"value":55906},", Azure Functions \"rely on Azure Storage for operations such as managing triggers and logging function executions\" which explains why you must associate a storage account to your Function App when you create one.",{"type":254,"tag":255,"props":55908,"children":55909},{},[55910,55912,55917],{"type":259,"value":55911},"By default when you create a Function App with its storage account from Azure Portal, the setting ",{"type":254,"tag":389,"props":55913,"children":55915},{"className":55914},[],[55916],{"type":259,"value":55880},{"type":259,"value":55918}," is automatically created in the Function App configuration and its value contains the secret connection string of the storage account. Thanks to that it will allow your Function App to have access to this storage and to work properly.",{"type":254,"tag":255,"props":55920,"children":55921},{},[55922],{"type":254,"tag":1161,"props":55923,"children":55927},{"alt":55924,"className":55925,"src":55926},"AzureWebJobsStorage setting with a secret value in Function App settings.",[1165,1166],"/posts/images/functionsidentity_portal_1.png",[],{"type":254,"tag":282,"props":55929,"children":55931},{"id":55930},"why-azurewebjobsstorage-poses-a-security-risk",[55932],{"type":259,"value":55933},"Why AzureWebJobsStorage poses a security risk?",{"type":254,"tag":255,"props":55935,"children":55936},{},[55937],{"type":259,"value":55938},"App settings of your Function App are stored encrypted in Azure so having secrets in a Function App configuration in Azure does not seem a big security threat. Yet,  secrets in Azure application settings will be available to anyone who has access to the configuration screen of your Function App (or to Kudu) which does not seem a great idea. Moreover in the application settings of a Function App, there is no proper access monitoring, alerting, and auditing as you would have in an Azure Key Vault. So your secret is not really \"safe\" there.",{"type":254,"tag":255,"props":55940,"children":55941},{},[55942],{"type":254,"tag":1161,"props":55943,"children":55947},{"alt":55944,"className":55945,"src":55946},"Padlock on a keyboard.",[1165,1166],"/posts/images/functionsidentity_padlock_1.jpgpng",[],{"type":254,"tag":255,"props":55949,"children":55950},{},[55951],{"type":259,"value":55952},"To avoid having someone gaining access to your storage account without you knowing, you probably do not want your storage account connection string to stay in a Function App configuration on Azure Portal.",{"type":254,"tag":282,"props":55954,"children":55956},{"id":55955},"what-can-we-do-about-it",[55957],{"type":259,"value":55958},"What can we do about it?",{"type":254,"tag":255,"props":55960,"children":55961},{},[55962,55964,55971],{"type":259,"value":55963},"A solution could be to store the AzureWebJobsStorage secret value in an Azure Key Vault and use a ",{"type":254,"tag":262,"props":55965,"children":55968},{"href":55966,"rel":55967},"https://docs.microsoft.com/en-us/azure/app-service/app-service-key-vault-references",[266],[55969],{"type":259,"value":55970},"Key Vault reference",{"type":259,"value":55972}," to link the secret in Key Vault to the AzureWebJobsStorage setting like on the example below.",{"type":254,"tag":255,"props":55974,"children":55975},{},[55976],{"type":254,"tag":1161,"props":55977,"children":55981},{"alt":55978,"className":55979,"src":55980},"AzureWebJobsStorage setting as a keyvault reference in Function App settings.",[1165,1166],"/posts/images/functionsidentity_portal_2.png",[],{"type":254,"tag":255,"props":55983,"children":55984},{},[55985,55987,55993],{"type":259,"value":55986},"Another solution that is far more interesting I think (as it does not require any secret) is to assign the Storage Blob Data Owner role to your Function App identity and to replace the AzureWebJobsStorage connection string setting by the setting ",{"type":254,"tag":389,"props":55988,"children":55990},{"className":55989},[],[55991],{"type":259,"value":55992},"AzureWebJobsStorage__accountName",{"type":259,"value":55994}," that only contains the name of the storage account and no secret value at all.",{"type":254,"tag":255,"props":55996,"children":55997},{},[55998,56000,56006],{"type":259,"value":55999},"If you want more details about connecting to the storage with the Function App identity you can find it ",{"type":254,"tag":262,"props":56001,"children":56004},{"href":56002,"rel":56003},"https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference#connecting-to-host-storage-with-an-identity",[266],[56005],{"type":259,"value":1414},{"type":259,"value":56007},". There is no point for me to paraphrase the documentation just to explain how you can set this up. However, I can show you how to implement that using Infrastructure as Code.",{"type":254,"tag":427,"props":56009,"children":56010},{"icon":429},[56011],{"type":254,"tag":255,"props":56012,"children":56013},{},[56014,56016,56021],{"type":259,"value":56015},"If you have read ",{"type":254,"tag":262,"props":56017,"children":56019},{"href":43874,"rel":56018},[266],[56020],{"type":259,"value":43584},{"type":259,"value":56022}," about connecting to an Azure SQL Database using Azure AD to authenticate instead of a secret connection, you probably know that I am not a big fan of secrets when we can avoid using them. From a security perspective, I think it is always a gain to remove the need for secrets while ensuring a resource can only be accessed by authorized people/applications.",{"type":254,"tag":282,"props":56024,"children":56026},{"id":56025},"how-to-configure-a-function-app-to-work-with-its-storage-account-without-a-secret-connection-string",[56027],{"type":259,"value":56028},"How to configure a Function App to work with its storage account without a secret connection string?",{"type":254,"tag":255,"props":56030,"children":56031},{},[56032,56034,56039],{"type":259,"value":56033},"To do that, I will use ",{"type":254,"tag":262,"props":56035,"children":56037},{"href":10221,"rel":56036},[266],[56038],{"type":259,"value":10225},{"type":259,"value":56040}," which is an Infrastructure as Code platform that uses programming languages instead of DSL to deploy infrastructure. As I am usually programming in C# for my application code, I will use C# as well for my infrastructure code.",{"type":254,"tag":455,"props":56042,"children":56044},{"id":56043},"what-resources-do-we-need-to-create",[56045],{"type":259,"value":56046},"What resources do we need to create?",{"type":254,"tag":255,"props":56048,"children":56049},{},[56050],{"type":259,"value":56051},"We need to create 3 different Azure resources:",{"type":254,"tag":332,"props":56053,"children":56054},{},[56055,56060,56065],{"type":254,"tag":336,"props":56056,"children":56057},{},[56058],{"type":259,"value":56059},"a consumption App Service Plan",{"type":254,"tag":336,"props":56061,"children":56062},{},[56063],{"type":259,"value":56064},"a Function App",{"type":254,"tag":336,"props":56066,"children":56067},{},[56068],{"type":259,"value":56069},"a Storage Account",{"type":254,"tag":255,"props":56071,"children":56072},{},[56073],{"type":259,"value":56074},"A resource group will also be created to contain these resources.\nAnd we will also need to assign the Storage Blob Data Owner role to the Function App, so to create a Role Assignment \"resource\".",{"type":254,"tag":455,"props":56076,"children":56078},{"id":56077},"what-the-infrastructure-code-looks-like",[56079],{"type":259,"value":56080},"What the infrastructure code looks like?",{"type":254,"tag":255,"props":56082,"children":56083},{},[56084,56086,56092],{"type":259,"value":56085},"The infrastructure code looks like standard C# code, but it describes the Azure resources we need using the ",{"type":254,"tag":262,"props":56087,"children":56090},{"href":56088,"rel":56089},"https://www.pulumi.com/blog/full-coverage-of-azure-resources-with-azure-native/",[266],[56091],{"type":259,"value":19906},{"type":259,"value":56093}," for Pulumi.",{"type":254,"tag":255,"props":56095,"children":56096},{},[56097],{"type":259,"value":56098},"Declaring a resource group is quite easy. Here we use C# string interpolation to build the resource group name from the project name and the stack name (two Pulumi notions that correspond to the name of the project and the environment):",{"type":254,"tag":612,"props":56100,"children":56102},{"className":1423,"code":56101,"language":1425,"meta":205,"style":205},"var resourceGroup = new ResourceGroup($\"rg-{Deployment.Instance.ProjectName}-{Deployment.Instance.StackName}\");\n",[56103],{"type":254,"tag":389,"props":56104,"children":56105},{"__ignoreMap":205},[56106],{"type":254,"tag":622,"props":56107,"children":56108},{"class":624,"line":625},[56109,56113,56117,56121,56125,56129,56133,56137,56141,56145,56149,56153,56157,56161,56165,56169,56173,56177,56181,56185,56189,56193,56197,56201],{"type":254,"tag":622,"props":56110,"children":56111},{"style":714},[56112],{"type":259,"value":1710},{"type":254,"tag":622,"props":56114,"children":56115},{"style":714},[56116],{"type":259,"value":34689},{"type":254,"tag":622,"props":56118,"children":56119},{"style":629},[56120],{"type":259,"value":1619},{"type":254,"tag":622,"props":56122,"children":56123},{"style":629},[56124],{"type":259,"value":1624},{"type":254,"tag":622,"props":56126,"children":56127},{"style":714},[56128],{"type":259,"value":34702},{"type":254,"tag":622,"props":56130,"children":56131},{"style":629},[56132],{"type":259,"value":1668},{"type":254,"tag":622,"props":56134,"children":56135},{"style":629},[56136],{"type":259,"value":9607},{"type":254,"tag":622,"props":56138,"children":56139},{"style":664},[56140],{"type":259,"value":34715},{"type":254,"tag":622,"props":56142,"children":56143},{"style":629},[56144],{"type":259,"value":3234},{"type":254,"tag":622,"props":56146,"children":56147},{"style":1202},[56148],{"type":259,"value":34724},{"type":254,"tag":622,"props":56150,"children":56151},{"style":629},[56152],{"type":259,"value":594},{"type":254,"tag":622,"props":56154,"children":56155},{"style":1202},[56156],{"type":259,"value":34733},{"type":254,"tag":622,"props":56158,"children":56159},{"style":629},[56160],{"type":259,"value":594},{"type":254,"tag":622,"props":56162,"children":56163},{"style":1202},[56164],{"type":259,"value":34742},{"type":254,"tag":622,"props":56166,"children":56167},{"style":629},[56168],{"type":259,"value":2421},{"type":254,"tag":622,"props":56170,"children":56171},{"style":664},[56172],{"type":259,"value":17065},{"type":254,"tag":622,"props":56174,"children":56175},{"style":629},[56176],{"type":259,"value":3234},{"type":254,"tag":622,"props":56178,"children":56179},{"style":1202},[56180],{"type":259,"value":34724},{"type":254,"tag":622,"props":56182,"children":56183},{"style":629},[56184],{"type":259,"value":594},{"type":254,"tag":622,"props":56186,"children":56187},{"style":1202},[56188],{"type":259,"value":34733},{"type":254,"tag":622,"props":56190,"children":56191},{"style":629},[56192],{"type":259,"value":594},{"type":254,"tag":622,"props":56194,"children":56195},{"style":1202},[56196],{"type":259,"value":34775},{"type":254,"tag":622,"props":56198,"children":56199},{"style":629},[56200],{"type":259,"value":2443},{"type":254,"tag":622,"props":56202,"children":56203},{"style":629},[56204],{"type":259,"value":1554},{"type":254,"tag":255,"props":56206,"children":56207},{},[56208,56210,56216],{"type":259,"value":56209},"To declare the resource group in which we want to create the storage account, we can use the property name of variable ",{"type":254,"tag":389,"props":56211,"children":56213},{"className":56212},[],[56214],{"type":259,"value":56215},"resourceGroup",{"type":259,"value":56217}," previously declared. We can see that for some arguments like the SKU names, Pulumi has types to help us choose between different possible values instead of specifying a magic string. It is not always the case but it is pretty handy when such things are available.",{"type":254,"tag":612,"props":56219,"children":56221},{"className":1423,"code":56220,"language":1425,"meta":205,"style":205},"var storageAccount = new StorageAccount($\"stnosecretfun{Deployment.Instance.StackName}\", new StorageAccountArgs\n{\n    ResourceGroupName = resourceGroup.Name,\n    Sku = new SkuArgs\n    {\n        Name = SkuName.Standard_LRS\n    },\n    Kind = Kind.StorageV2\n});\n",[56222],{"type":254,"tag":389,"props":56223,"children":56224},{"__ignoreMap":205},[56225,56304,56311,56338,56357,56364,56389,56396,56421],{"type":254,"tag":622,"props":56226,"children":56227},{"class":624,"line":625},[56228,56232,56237,56241,56245,56250,56254,56258,56263,56267,56271,56275,56279,56283,56287,56291,56295,56299],{"type":254,"tag":622,"props":56229,"children":56230},{"style":714},[56231],{"type":259,"value":1710},{"type":254,"tag":622,"props":56233,"children":56234},{"style":714},[56235],{"type":259,"value":56236}," storageAccount",{"type":254,"tag":622,"props":56238,"children":56239},{"style":629},[56240],{"type":259,"value":1619},{"type":254,"tag":622,"props":56242,"children":56243},{"style":629},[56244],{"type":259,"value":1624},{"type":254,"tag":622,"props":56246,"children":56247},{"style":714},[56248],{"type":259,"value":56249}," StorageAccount",{"type":254,"tag":622,"props":56251,"children":56252},{"style":629},[56253],{"type":259,"value":1668},{"type":254,"tag":622,"props":56255,"children":56256},{"style":629},[56257],{"type":259,"value":9607},{"type":254,"tag":622,"props":56259,"children":56260},{"style":664},[56261],{"type":259,"value":56262},"stnosecretfun",{"type":254,"tag":622,"props":56264,"children":56265},{"style":629},[56266],{"type":259,"value":3234},{"type":254,"tag":622,"props":56268,"children":56269},{"style":1202},[56270],{"type":259,"value":34724},{"type":254,"tag":622,"props":56272,"children":56273},{"style":629},[56274],{"type":259,"value":594},{"type":254,"tag":622,"props":56276,"children":56277},{"style":1202},[56278],{"type":259,"value":34733},{"type":254,"tag":622,"props":56280,"children":56281},{"style":629},[56282],{"type":259,"value":594},{"type":254,"tag":622,"props":56284,"children":56285},{"style":1202},[56286],{"type":259,"value":34775},{"type":254,"tag":622,"props":56288,"children":56289},{"style":629},[56290],{"type":259,"value":2443},{"type":254,"tag":622,"props":56292,"children":56293},{"style":629},[56294],{"type":259,"value":3149},{"type":254,"tag":622,"props":56296,"children":56297},{"style":629},[56298],{"type":259,"value":1624},{"type":254,"tag":622,"props":56300,"children":56301},{"style":714},[56302],{"type":259,"value":56303}," StorageAccountArgs\n",{"type":254,"tag":622,"props":56305,"children":56306},{"class":624,"line":635},[56307],{"type":254,"tag":622,"props":56308,"children":56309},{"style":629},[56310],{"type":259,"value":632},{"type":254,"tag":622,"props":56312,"children":56313},{"class":624,"line":679},[56314,56318,56322,56326,56330,56334],{"type":254,"tag":622,"props":56315,"children":56316},{"style":1202},[56317],{"type":259,"value":34928},{"type":254,"tag":622,"props":56319,"children":56320},{"style":629},[56321],{"type":259,"value":1654},{"type":254,"tag":622,"props":56323,"children":56324},{"style":1202},[56325],{"type":259,"value":34689},{"type":254,"tag":622,"props":56327,"children":56328},{"style":629},[56329],{"type":259,"value":594},{"type":254,"tag":622,"props":56331,"children":56332},{"style":1202},[56333],{"type":259,"value":2254},{"type":254,"tag":622,"props":56335,"children":56336},{"style":629},[56337],{"type":259,"value":676},{"type":254,"tag":622,"props":56339,"children":56340},{"class":624,"line":705},[56341,56345,56349,56353],{"type":254,"tag":622,"props":56342,"children":56343},{"style":1202},[56344],{"type":259,"value":34993},{"type":254,"tag":622,"props":56346,"children":56347},{"style":629},[56348],{"type":259,"value":1654},{"type":254,"tag":622,"props":56350,"children":56351},{"style":629},[56352],{"type":259,"value":1624},{"type":254,"tag":622,"props":56354,"children":56355},{"style":714},[56356],{"type":259,"value":44695},{"type":254,"tag":622,"props":56358,"children":56359},{"class":624,"line":732},[56360],{"type":254,"tag":622,"props":56361,"children":56362},{"style":629},[56363],{"type":259,"value":3574},{"type":254,"tag":622,"props":56365,"children":56366},{"class":624,"line":759},[56367,56371,56375,56380,56384],{"type":254,"tag":622,"props":56368,"children":56369},{"style":1202},[56370],{"type":259,"value":35062},{"type":254,"tag":622,"props":56372,"children":56373},{"style":629},[56374],{"type":259,"value":1654},{"type":254,"tag":622,"props":56376,"children":56377},{"style":1202},[56378],{"type":259,"value":56379}," SkuName",{"type":254,"tag":622,"props":56381,"children":56382},{"style":629},[56383],{"type":259,"value":594},{"type":254,"tag":622,"props":56385,"children":56386},{"style":1202},[56387],{"type":259,"value":56388},"Standard_LRS\n",{"type":254,"tag":622,"props":56390,"children":56391},{"class":624,"line":798},[56392],{"type":254,"tag":622,"props":56393,"children":56394},{"style":629},[56395],{"type":259,"value":20453},{"type":254,"tag":622,"props":56397,"children":56398},{"class":624,"line":836},[56399,56403,56407,56412,56416],{"type":254,"tag":622,"props":56400,"children":56401},{"style":1202},[56402],{"type":259,"value":34960},{"type":254,"tag":622,"props":56404,"children":56405},{"style":629},[56406],{"type":259,"value":1654},{"type":254,"tag":622,"props":56408,"children":56409},{"style":1202},[56410],{"type":259,"value":56411}," Kind",{"type":254,"tag":622,"props":56413,"children":56414},{"style":629},[56415],{"type":259,"value":594},{"type":254,"tag":622,"props":56417,"children":56418},{"style":1202},[56419],{"type":259,"value":56420},"StorageV2\n",{"type":254,"tag":622,"props":56422,"children":56423},{"class":624,"line":862},[56424],{"type":254,"tag":622,"props":56425,"children":56426},{"style":629},[56427],{"type":259,"value":6661},{"type":254,"tag":255,"props":56429,"children":56430},{},[56431],{"type":259,"value":56432},"This is the way of declaring a consumption App Service Plan:",{"type":254,"tag":612,"props":56434,"children":56436},{"className":1423,"code":56435,"language":1425,"meta":205,"style":205},"var appServicePlan = new AppServicePlan($\"plan-{Deployment.Instance.ProjectName}-{Deployment.Instance.StackName}\", new AppServicePlanArgs\n{\n    ResourceGroupName = resourceGroup.Name,\n    Kind = \"Windows\",\n    Sku = new SkuDescriptionArgs\n    {\n        Tier = \"Dynamic\",\n        Name = \"Y1\"\n    }\n});\n",[56437],{"type":254,"tag":389,"props":56438,"children":56439},{"__ignoreMap":205},[56440,56548,56555,56582,56610,56630,56637,56665,56689,56696],{"type":254,"tag":622,"props":56441,"children":56442},{"class":624,"line":625},[56443,56447,56451,56455,56459,56463,56467,56471,56475,56479,56483,56487,56491,56495,56499,56503,56507,56511,56515,56519,56523,56527,56531,56535,56539,56543],{"type":254,"tag":622,"props":56444,"children":56445},{"style":714},[56446],{"type":259,"value":1710},{"type":254,"tag":622,"props":56448,"children":56449},{"style":714},[56450],{"type":259,"value":34806},{"type":254,"tag":622,"props":56452,"children":56453},{"style":629},[56454],{"type":259,"value":1619},{"type":254,"tag":622,"props":56456,"children":56457},{"style":629},[56458],{"type":259,"value":1624},{"type":254,"tag":622,"props":56460,"children":56461},{"style":714},[56462],{"type":259,"value":34819},{"type":254,"tag":622,"props":56464,"children":56465},{"style":629},[56466],{"type":259,"value":1668},{"type":254,"tag":622,"props":56468,"children":56469},{"style":629},[56470],{"type":259,"value":9607},{"type":254,"tag":622,"props":56472,"children":56473},{"style":664},[56474],{"type":259,"value":34832},{"type":254,"tag":622,"props":56476,"children":56477},{"style":629},[56478],{"type":259,"value":3234},{"type":254,"tag":622,"props":56480,"children":56481},{"style":1202},[56482],{"type":259,"value":34724},{"type":254,"tag":622,"props":56484,"children":56485},{"style":629},[56486],{"type":259,"value":594},{"type":254,"tag":622,"props":56488,"children":56489},{"style":1202},[56490],{"type":259,"value":34733},{"type":254,"tag":622,"props":56492,"children":56493},{"style":629},[56494],{"type":259,"value":594},{"type":254,"tag":622,"props":56496,"children":56497},{"style":1202},[56498],{"type":259,"value":34742},{"type":254,"tag":622,"props":56500,"children":56501},{"style":629},[56502],{"type":259,"value":2421},{"type":254,"tag":622,"props":56504,"children":56505},{"style":664},[56506],{"type":259,"value":17065},{"type":254,"tag":622,"props":56508,"children":56509},{"style":629},[56510],{"type":259,"value":3234},{"type":254,"tag":622,"props":56512,"children":56513},{"style":1202},[56514],{"type":259,"value":34724},{"type":254,"tag":622,"props":56516,"children":56517},{"style":629},[56518],{"type":259,"value":594},{"type":254,"tag":622,"props":56520,"children":56521},{"style":1202},[56522],{"type":259,"value":34733},{"type":254,"tag":622,"props":56524,"children":56525},{"style":629},[56526],{"type":259,"value":594},{"type":254,"tag":622,"props":56528,"children":56529},{"style":1202},[56530],{"type":259,"value":34775},{"type":254,"tag":622,"props":56532,"children":56533},{"style":629},[56534],{"type":259,"value":2443},{"type":254,"tag":622,"props":56536,"children":56537},{"style":629},[56538],{"type":259,"value":3149},{"type":254,"tag":622,"props":56540,"children":56541},{"style":629},[56542],{"type":259,"value":1624},{"type":254,"tag":622,"props":56544,"children":56545},{"style":714},[56546],{"type":259,"value":56547}," AppServicePlanArgs\n",{"type":254,"tag":622,"props":56549,"children":56550},{"class":624,"line":635},[56551],{"type":254,"tag":622,"props":56552,"children":56553},{"style":629},[56554],{"type":259,"value":632},{"type":254,"tag":622,"props":56556,"children":56557},{"class":624,"line":679},[56558,56562,56566,56570,56574,56578],{"type":254,"tag":622,"props":56559,"children":56560},{"style":1202},[56561],{"type":259,"value":34928},{"type":254,"tag":622,"props":56563,"children":56564},{"style":629},[56565],{"type":259,"value":1654},{"type":254,"tag":622,"props":56567,"children":56568},{"style":1202},[56569],{"type":259,"value":34689},{"type":254,"tag":622,"props":56571,"children":56572},{"style":629},[56573],{"type":259,"value":594},{"type":254,"tag":622,"props":56575,"children":56576},{"style":1202},[56577],{"type":259,"value":2254},{"type":254,"tag":622,"props":56579,"children":56580},{"style":629},[56581],{"type":259,"value":676},{"type":254,"tag":622,"props":56583,"children":56584},{"class":624,"line":705},[56585,56589,56593,56597,56602,56606],{"type":254,"tag":622,"props":56586,"children":56587},{"style":1202},[56588],{"type":259,"value":34960},{"type":254,"tag":622,"props":56590,"children":56591},{"style":629},[56592],{"type":259,"value":1654},{"type":254,"tag":622,"props":56594,"children":56595},{"style":629},[56596],{"type":259,"value":661},{"type":254,"tag":622,"props":56598,"children":56599},{"style":664},[56600],{"type":259,"value":56601},"Windows",{"type":254,"tag":622,"props":56603,"children":56604},{"style":629},[56605],{"type":259,"value":652},{"type":254,"tag":622,"props":56607,"children":56608},{"style":629},[56609],{"type":259,"value":676},{"type":254,"tag":622,"props":56611,"children":56612},{"class":624,"line":732},[56613,56617,56621,56625],{"type":254,"tag":622,"props":56614,"children":56615},{"style":1202},[56616],{"type":259,"value":34993},{"type":254,"tag":622,"props":56618,"children":56619},{"style":629},[56620],{"type":259,"value":1654},{"type":254,"tag":622,"props":56622,"children":56623},{"style":629},[56624],{"type":259,"value":1624},{"type":254,"tag":622,"props":56626,"children":56627},{"style":714},[56628],{"type":259,"value":56629}," SkuDescriptionArgs\n",{"type":254,"tag":622,"props":56631,"children":56632},{"class":624,"line":759},[56633],{"type":254,"tag":622,"props":56634,"children":56635},{"style":629},[56636],{"type":259,"value":3574},{"type":254,"tag":622,"props":56638,"children":56639},{"class":624,"line":798},[56640,56644,56648,56652,56657,56661],{"type":254,"tag":622,"props":56641,"children":56642},{"style":1202},[56643],{"type":259,"value":35029},{"type":254,"tag":622,"props":56645,"children":56646},{"style":629},[56647],{"type":259,"value":1654},{"type":254,"tag":622,"props":56649,"children":56650},{"style":629},[56651],{"type":259,"value":661},{"type":254,"tag":622,"props":56653,"children":56654},{"style":664},[56655],{"type":259,"value":56656},"Dynamic",{"type":254,"tag":622,"props":56658,"children":56659},{"style":629},[56660],{"type":259,"value":652},{"type":254,"tag":622,"props":56662,"children":56663},{"style":629},[56664],{"type":259,"value":676},{"type":254,"tag":622,"props":56666,"children":56667},{"class":624,"line":836},[56668,56672,56676,56680,56685],{"type":254,"tag":622,"props":56669,"children":56670},{"style":1202},[56671],{"type":259,"value":35062},{"type":254,"tag":622,"props":56673,"children":56674},{"style":629},[56675],{"type":259,"value":1654},{"type":254,"tag":622,"props":56677,"children":56678},{"style":629},[56679],{"type":259,"value":661},{"type":254,"tag":622,"props":56681,"children":56682},{"style":664},[56683],{"type":259,"value":56684},"Y1",{"type":254,"tag":622,"props":56686,"children":56687},{"style":629},[56688],{"type":259,"value":9909},{"type":254,"tag":622,"props":56690,"children":56691},{"class":624,"line":862},[56692],{"type":254,"tag":622,"props":56693,"children":56694},{"style":629},[56695],{"type":259,"value":1093},{"type":254,"tag":622,"props":56697,"children":56698},{"class":624,"line":900},[56699],{"type":254,"tag":622,"props":56700,"children":56701},{"style":629},[56702],{"type":259,"value":6661},{"type":254,"tag":255,"props":56704,"children":56705},{},[56706,56708,56713],{"type":259,"value":56707},"In Azure APIs, a FunctionApp is just a WebApp of a special kind \"FunctionApp\". You can notice that we enabled the System Managed Identity on the Function App by setting the Identity property. And as expected we added an app setting ",{"type":254,"tag":389,"props":56709,"children":56711},{"className":56710},[],[56712],{"type":259,"value":55992},{"type":259,"value":56714}," whose value is the name of the storage account.",{"type":254,"tag":612,"props":56716,"children":56718},{"className":1423,"code":56717,"language":1425,"meta":205,"style":205},"var functionApp = new WebApp($\"func-nosecret-{Deployment.Instance.StackName}\", new WebAppArgs\n{\n    Kind = \"FunctionApp\",\n    ResourceGroupName = resourceGroup.Name,\n    ServerFarmId = appServicePlan.Id,\n    Identity = new ManagedServiceIdentityArgs\n    {\n        Type = Pulumi.AzureNative.Web.ManagedServiceIdentityType.SystemAssigned\n    },\n    SiteConfig = new SiteConfigArgs\n    {\n        AppSettings = new[]\n        {\n            new NameValuePairArgs\n            {\n                Name = \"runtime\",\n                Value = \"dotnet\",\n            },\n            new NameValuePairArgs\n            {\n                Name = \"FUNCTIONS_WORKER_RUNTIME\",\n                Value = \"dotnet\",\n            },\n            new NameValuePairArgs\n            {\n                Name = \"FUNCTIONS_EXTENSION_VERSION\",\n                Value = \"~4\"\n            },\n            new NameValuePairArgs\n            {\n                Name = \"AzureWebJobsStorage__accountName\",\n                Value = storageAccount.Name\n            }\n        },\n    },\n});\n",[56719],{"type":254,"tag":389,"props":56720,"children":56721},{"__ignoreMap":205},[56722,56800,56807,56835,56862,56889,56910,56917,56970,56977,56998,57005,57022,57029,57041,57048,57077,57106,57114,57125,57132,57160,57187,57194,57205,57212,57240,57264,57271,57282,57289,57316,57339,57347,57355,57362],{"type":254,"tag":622,"props":56723,"children":56724},{"class":624,"line":625},[56725,56729,56734,56738,56742,56746,56750,56754,56759,56763,56767,56771,56775,56779,56783,56787,56791,56795],{"type":254,"tag":622,"props":56726,"children":56727},{"style":714},[56728],{"type":259,"value":1710},{"type":254,"tag":622,"props":56730,"children":56731},{"style":714},[56732],{"type":259,"value":56733}," functionApp",{"type":254,"tag":622,"props":56735,"children":56736},{"style":629},[56737],{"type":259,"value":1619},{"type":254,"tag":622,"props":56739,"children":56740},{"style":629},[56741],{"type":259,"value":1624},{"type":254,"tag":622,"props":56743,"children":56744},{"style":714},[56745],{"type":259,"value":35143},{"type":254,"tag":622,"props":56747,"children":56748},{"style":629},[56749],{"type":259,"value":1668},{"type":254,"tag":622,"props":56751,"children":56752},{"style":629},[56753],{"type":259,"value":9607},{"type":254,"tag":622,"props":56755,"children":56756},{"style":664},[56757],{"type":259,"value":56758},"func-nosecret-",{"type":254,"tag":622,"props":56760,"children":56761},{"style":629},[56762],{"type":259,"value":3234},{"type":254,"tag":622,"props":56764,"children":56765},{"style":1202},[56766],{"type":259,"value":34724},{"type":254,"tag":622,"props":56768,"children":56769},{"style":629},[56770],{"type":259,"value":594},{"type":254,"tag":622,"props":56772,"children":56773},{"style":1202},[56774],{"type":259,"value":34733},{"type":254,"tag":622,"props":56776,"children":56777},{"style":629},[56778],{"type":259,"value":594},{"type":254,"tag":622,"props":56780,"children":56781},{"style":1202},[56782],{"type":259,"value":34775},{"type":254,"tag":622,"props":56784,"children":56785},{"style":629},[56786],{"type":259,"value":2443},{"type":254,"tag":622,"props":56788,"children":56789},{"style":629},[56790],{"type":259,"value":3149},{"type":254,"tag":622,"props":56792,"children":56793},{"style":629},[56794],{"type":259,"value":1624},{"type":254,"tag":622,"props":56796,"children":56797},{"style":714},[56798],{"type":259,"value":56799}," WebAppArgs\n",{"type":254,"tag":622,"props":56801,"children":56802},{"class":624,"line":635},[56803],{"type":254,"tag":622,"props":56804,"children":56805},{"style":629},[56806],{"type":259,"value":632},{"type":254,"tag":622,"props":56808,"children":56809},{"class":624,"line":679},[56810,56814,56818,56822,56827,56831],{"type":254,"tag":622,"props":56811,"children":56812},{"style":1202},[56813],{"type":259,"value":34960},{"type":254,"tag":622,"props":56815,"children":56816},{"style":629},[56817],{"type":259,"value":1654},{"type":254,"tag":622,"props":56819,"children":56820},{"style":629},[56821],{"type":259,"value":661},{"type":254,"tag":622,"props":56823,"children":56824},{"style":664},[56825],{"type":259,"value":56826},"FunctionApp",{"type":254,"tag":622,"props":56828,"children":56829},{"style":629},[56830],{"type":259,"value":652},{"type":254,"tag":622,"props":56832,"children":56833},{"style":629},[56834],{"type":259,"value":676},{"type":254,"tag":622,"props":56836,"children":56837},{"class":624,"line":705},[56838,56842,56846,56850,56854,56858],{"type":254,"tag":622,"props":56839,"children":56840},{"style":1202},[56841],{"type":259,"value":34928},{"type":254,"tag":622,"props":56843,"children":56844},{"style":629},[56845],{"type":259,"value":1654},{"type":254,"tag":622,"props":56847,"children":56848},{"style":1202},[56849],{"type":259,"value":34689},{"type":254,"tag":622,"props":56851,"children":56852},{"style":629},[56853],{"type":259,"value":594},{"type":254,"tag":622,"props":56855,"children":56856},{"style":1202},[56857],{"type":259,"value":2254},{"type":254,"tag":622,"props":56859,"children":56860},{"style":629},[56861],{"type":259,"value":676},{"type":254,"tag":622,"props":56863,"children":56864},{"class":624,"line":732},[56865,56869,56873,56877,56881,56885],{"type":254,"tag":622,"props":56866,"children":56867},{"style":1202},[56868],{"type":259,"value":35284},{"type":254,"tag":622,"props":56870,"children":56871},{"style":629},[56872],{"type":259,"value":1654},{"type":254,"tag":622,"props":56874,"children":56875},{"style":1202},[56876],{"type":259,"value":34806},{"type":254,"tag":622,"props":56878,"children":56879},{"style":629},[56880],{"type":259,"value":594},{"type":254,"tag":622,"props":56882,"children":56883},{"style":1202},[56884],{"type":259,"value":20553},{"type":254,"tag":622,"props":56886,"children":56887},{"style":629},[56888],{"type":259,"value":676},{"type":254,"tag":622,"props":56890,"children":56891},{"class":624,"line":759},[56892,56897,56901,56905],{"type":254,"tag":622,"props":56893,"children":56894},{"style":1202},[56895],{"type":259,"value":56896},"    Identity ",{"type":254,"tag":622,"props":56898,"children":56899},{"style":629},[56900],{"type":259,"value":1654},{"type":254,"tag":622,"props":56902,"children":56903},{"style":629},[56904],{"type":259,"value":1624},{"type":254,"tag":622,"props":56906,"children":56907},{"style":714},[56908],{"type":259,"value":56909}," ManagedServiceIdentityArgs\n",{"type":254,"tag":622,"props":56911,"children":56912},{"class":624,"line":798},[56913],{"type":254,"tag":622,"props":56914,"children":56915},{"style":629},[56916],{"type":259,"value":3574},{"type":254,"tag":622,"props":56918,"children":56919},{"class":624,"line":836},[56920,56925,56929,56934,56938,56943,56947,56952,56956,56961,56965],{"type":254,"tag":622,"props":56921,"children":56922},{"style":1202},[56923],{"type":259,"value":56924},"        Type ",{"type":254,"tag":622,"props":56926,"children":56927},{"style":629},[56928],{"type":259,"value":1654},{"type":254,"tag":622,"props":56930,"children":56931},{"style":1202},[56932],{"type":259,"value":56933}," Pulumi",{"type":254,"tag":622,"props":56935,"children":56936},{"style":629},[56937],{"type":259,"value":594},{"type":254,"tag":622,"props":56939,"children":56940},{"style":1202},[56941],{"type":259,"value":56942},"AzureNative",{"type":254,"tag":622,"props":56944,"children":56945},{"style":629},[56946],{"type":259,"value":594},{"type":254,"tag":622,"props":56948,"children":56949},{"style":1202},[56950],{"type":259,"value":56951},"Web",{"type":254,"tag":622,"props":56953,"children":56954},{"style":629},[56955],{"type":259,"value":594},{"type":254,"tag":622,"props":56957,"children":56958},{"style":1202},[56959],{"type":259,"value":56960},"ManagedServiceIdentityType",{"type":254,"tag":622,"props":56962,"children":56963},{"style":629},[56964],{"type":259,"value":594},{"type":254,"tag":622,"props":56966,"children":56967},{"style":1202},[56968],{"type":259,"value":56969},"SystemAssigned\n",{"type":254,"tag":622,"props":56971,"children":56972},{"class":624,"line":862},[56973],{"type":254,"tag":622,"props":56974,"children":56975},{"style":629},[56976],{"type":259,"value":20453},{"type":254,"tag":622,"props":56978,"children":56979},{"class":624,"line":900},[56980,56985,56989,56993],{"type":254,"tag":622,"props":56981,"children":56982},{"style":1202},[56983],{"type":259,"value":56984},"    SiteConfig ",{"type":254,"tag":622,"props":56986,"children":56987},{"style":629},[56988],{"type":259,"value":1654},{"type":254,"tag":622,"props":56990,"children":56991},{"style":629},[56992],{"type":259,"value":1624},{"type":254,"tag":622,"props":56994,"children":56995},{"style":714},[56996],{"type":259,"value":56997}," SiteConfigArgs\n",{"type":254,"tag":622,"props":56999,"children":57000},{"class":624,"line":938},[57001],{"type":254,"tag":622,"props":57002,"children":57003},{"style":629},[57004],{"type":259,"value":3574},{"type":254,"tag":622,"props":57006,"children":57007},{"class":624,"line":976},[57008,57013,57017],{"type":254,"tag":622,"props":57009,"children":57010},{"style":1202},[57011],{"type":259,"value":57012},"        AppSettings ",{"type":254,"tag":622,"props":57014,"children":57015},{"style":629},[57016],{"type":259,"value":1654},{"type":254,"tag":622,"props":57018,"children":57019},{"style":629},[57020],{"type":259,"value":57021}," new[]\n",{"type":254,"tag":622,"props":57023,"children":57024},{"class":624,"line":1014},[57025],{"type":254,"tag":622,"props":57026,"children":57027},{"style":629},[57028],{"type":259,"value":7094},{"type":254,"tag":622,"props":57030,"children":57031},{"class":624,"line":1052},[57032,57036],{"type":254,"tag":622,"props":57033,"children":57034},{"style":629},[57035],{"type":259,"value":36922},{"type":254,"tag":622,"props":57037,"children":57038},{"style":714},[57039],{"type":259,"value":57040}," NameValuePairArgs\n",{"type":254,"tag":622,"props":57042,"children":57043},{"class":624,"line":1078},[57044],{"type":254,"tag":622,"props":57045,"children":57046},{"style":629},[57047],{"type":259,"value":11957},{"type":254,"tag":622,"props":57049,"children":57050},{"class":624,"line":1087},[57051,57056,57060,57064,57069,57073],{"type":254,"tag":622,"props":57052,"children":57053},{"style":1202},[57054],{"type":259,"value":57055},"                Name ",{"type":254,"tag":622,"props":57057,"children":57058},{"style":629},[57059],{"type":259,"value":1654},{"type":254,"tag":622,"props":57061,"children":57062},{"style":629},[57063],{"type":259,"value":661},{"type":254,"tag":622,"props":57065,"children":57066},{"style":664},[57067],{"type":259,"value":57068},"runtime",{"type":254,"tag":622,"props":57070,"children":57071},{"style":629},[57072],{"type":259,"value":652},{"type":254,"tag":622,"props":57074,"children":57075},{"style":629},[57076],{"type":259,"value":676},{"type":254,"tag":622,"props":57078,"children":57079},{"class":624,"line":1096},[57080,57085,57089,57093,57098,57102],{"type":254,"tag":622,"props":57081,"children":57082},{"style":1202},[57083],{"type":259,"value":57084},"                Value ",{"type":254,"tag":622,"props":57086,"children":57087},{"style":629},[57088],{"type":259,"value":1654},{"type":254,"tag":622,"props":57090,"children":57091},{"style":629},[57092],{"type":259,"value":661},{"type":254,"tag":622,"props":57094,"children":57095},{"style":664},[57096],{"type":259,"value":57097},"dotnet",{"type":254,"tag":622,"props":57099,"children":57100},{"style":629},[57101],{"type":259,"value":652},{"type":254,"tag":622,"props":57103,"children":57104},{"style":629},[57105],{"type":259,"value":676},{"type":254,"tag":622,"props":57107,"children":57108},{"class":624,"line":1105},[57109],{"type":254,"tag":622,"props":57110,"children":57111},{"style":629},[57112],{"type":259,"value":57113},"            },\n",{"type":254,"tag":622,"props":57115,"children":57116},{"class":624,"line":4131},[57117,57121],{"type":254,"tag":622,"props":57118,"children":57119},{"style":629},[57120],{"type":259,"value":36922},{"type":254,"tag":622,"props":57122,"children":57123},{"style":714},[57124],{"type":259,"value":57040},{"type":254,"tag":622,"props":57126,"children":57127},{"class":624,"line":4183},[57128],{"type":254,"tag":622,"props":57129,"children":57130},{"style":629},[57131],{"type":259,"value":11957},{"type":254,"tag":622,"props":57133,"children":57134},{"class":624,"line":4208},[57135,57139,57143,57147,57152,57156],{"type":254,"tag":622,"props":57136,"children":57137},{"style":1202},[57138],{"type":259,"value":57055},{"type":254,"tag":622,"props":57140,"children":57141},{"style":629},[57142],{"type":259,"value":1654},{"type":254,"tag":622,"props":57144,"children":57145},{"style":629},[57146],{"type":259,"value":661},{"type":254,"tag":622,"props":57148,"children":57149},{"style":664},[57150],{"type":259,"value":57151},"FUNCTIONS_WORKER_RUNTIME",{"type":254,"tag":622,"props":57153,"children":57154},{"style":629},[57155],{"type":259,"value":652},{"type":254,"tag":622,"props":57157,"children":57158},{"style":629},[57159],{"type":259,"value":676},{"type":254,"tag":622,"props":57161,"children":57162},{"class":624,"line":4216},[57163,57167,57171,57175,57179,57183],{"type":254,"tag":622,"props":57164,"children":57165},{"style":1202},[57166],{"type":259,"value":57084},{"type":254,"tag":622,"props":57168,"children":57169},{"style":629},[57170],{"type":259,"value":1654},{"type":254,"tag":622,"props":57172,"children":57173},{"style":629},[57174],{"type":259,"value":661},{"type":254,"tag":622,"props":57176,"children":57177},{"style":664},[57178],{"type":259,"value":57097},{"type":254,"tag":622,"props":57180,"children":57181},{"style":629},[57182],{"type":259,"value":652},{"type":254,"tag":622,"props":57184,"children":57185},{"style":629},[57186],{"type":259,"value":676},{"type":254,"tag":622,"props":57188,"children":57189},{"class":624,"line":4224},[57190],{"type":254,"tag":622,"props":57191,"children":57192},{"style":629},[57193],{"type":259,"value":57113},{"type":254,"tag":622,"props":57195,"children":57196},{"class":624,"line":4268},[57197,57201],{"type":254,"tag":622,"props":57198,"children":57199},{"style":629},[57200],{"type":259,"value":36922},{"type":254,"tag":622,"props":57202,"children":57203},{"style":714},[57204],{"type":259,"value":57040},{"type":254,"tag":622,"props":57206,"children":57207},{"class":624,"line":4276},[57208],{"type":254,"tag":622,"props":57209,"children":57210},{"style":629},[57211],{"type":259,"value":11957},{"type":254,"tag":622,"props":57213,"children":57214},{"class":624,"line":4332},[57215,57219,57223,57227,57232,57236],{"type":254,"tag":622,"props":57216,"children":57217},{"style":1202},[57218],{"type":259,"value":57055},{"type":254,"tag":622,"props":57220,"children":57221},{"style":629},[57222],{"type":259,"value":1654},{"type":254,"tag":622,"props":57224,"children":57225},{"style":629},[57226],{"type":259,"value":661},{"type":254,"tag":622,"props":57228,"children":57229},{"style":664},[57230],{"type":259,"value":57231},"FUNCTIONS_EXTENSION_VERSION",{"type":254,"tag":622,"props":57233,"children":57234},{"style":629},[57235],{"type":259,"value":652},{"type":254,"tag":622,"props":57237,"children":57238},{"style":629},[57239],{"type":259,"value":676},{"type":254,"tag":622,"props":57241,"children":57242},{"class":624,"line":4376},[57243,57247,57251,57255,57260],{"type":254,"tag":622,"props":57244,"children":57245},{"style":1202},[57246],{"type":259,"value":57084},{"type":254,"tag":622,"props":57248,"children":57249},{"style":629},[57250],{"type":259,"value":1654},{"type":254,"tag":622,"props":57252,"children":57253},{"style":629},[57254],{"type":259,"value":661},{"type":254,"tag":622,"props":57256,"children":57257},{"style":664},[57258],{"type":259,"value":57259},"~4",{"type":254,"tag":622,"props":57261,"children":57262},{"style":629},[57263],{"type":259,"value":9909},{"type":254,"tag":622,"props":57265,"children":57266},{"class":624,"line":4384},[57267],{"type":254,"tag":622,"props":57268,"children":57269},{"style":629},[57270],{"type":259,"value":57113},{"type":254,"tag":622,"props":57272,"children":57273},{"class":624,"line":4416},[57274,57278],{"type":254,"tag":622,"props":57275,"children":57276},{"style":629},[57277],{"type":259,"value":36922},{"type":254,"tag":622,"props":57279,"children":57280},{"style":714},[57281],{"type":259,"value":57040},{"type":254,"tag":622,"props":57283,"children":57284},{"class":624,"line":4456},[57285],{"type":254,"tag":622,"props":57286,"children":57287},{"style":629},[57288],{"type":259,"value":11957},{"type":254,"tag":622,"props":57290,"children":57291},{"class":624,"line":4488},[57292,57296,57300,57304,57308,57312],{"type":254,"tag":622,"props":57293,"children":57294},{"style":1202},[57295],{"type":259,"value":57055},{"type":254,"tag":622,"props":57297,"children":57298},{"style":629},[57299],{"type":259,"value":1654},{"type":254,"tag":622,"props":57301,"children":57302},{"style":629},[57303],{"type":259,"value":661},{"type":254,"tag":622,"props":57305,"children":57306},{"style":664},[57307],{"type":259,"value":55992},{"type":254,"tag":622,"props":57309,"children":57310},{"style":629},[57311],{"type":259,"value":652},{"type":254,"tag":622,"props":57313,"children":57314},{"style":629},[57315],{"type":259,"value":676},{"type":254,"tag":622,"props":57317,"children":57318},{"class":624,"line":4496},[57319,57323,57327,57331,57335],{"type":254,"tag":622,"props":57320,"children":57321},{"style":1202},[57322],{"type":259,"value":57084},{"type":254,"tag":622,"props":57324,"children":57325},{"style":629},[57326],{"type":259,"value":1654},{"type":254,"tag":622,"props":57328,"children":57329},{"style":1202},[57330],{"type":259,"value":56236},{"type":254,"tag":622,"props":57332,"children":57333},{"style":629},[57334],{"type":259,"value":594},{"type":254,"tag":622,"props":57336,"children":57337},{"style":1202},[57338],{"type":259,"value":20582},{"type":254,"tag":622,"props":57340,"children":57341},{"class":624,"line":4564},[57342],{"type":254,"tag":622,"props":57343,"children":57344},{"style":629},[57345],{"type":259,"value":57346},"            }\n",{"type":254,"tag":622,"props":57348,"children":57349},{"class":624,"line":4572},[57350],{"type":254,"tag":622,"props":57351,"children":57352},{"style":629},[57353],{"type":259,"value":57354},"        },\n",{"type":254,"tag":622,"props":57356,"children":57357},{"class":624,"line":4644},[57358],{"type":254,"tag":622,"props":57359,"children":57360},{"style":629},[57361],{"type":259,"value":20453},{"type":254,"tag":622,"props":57363,"children":57364},{"class":624,"line":4652},[57365],{"type":254,"tag":622,"props":57366,"children":57367},{"style":629},[57368],{"type":259,"value":6661},{"type":254,"tag":255,"props":57370,"children":57371},{},[57372,57374,57381,57383,57389],{"type":259,"value":57373},"The last thing to do is to assign the role Storage Blob Data Owner to the Function App. To find the resource id I needed I looked at ",{"type":254,"tag":262,"props":57375,"children":57378},{"href":57376,"rel":57377},"https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles",[266],[57379],{"type":259,"value":57380},"this page of the Microsoft documentation",{"type":259,"value":57382}," but I hope that in the future Pulumi will provide an ",{"type":254,"tag":389,"props":57384,"children":57386},{"className":57385},[],[57387],{"type":259,"value":57388},"enum",{"type":259,"value":57390}," or something like that with the possible values to make that easier to find and assign.",{"type":254,"tag":612,"props":57392,"children":57394},{"className":1423,"code":57393,"language":1425,"meta":205,"style":205},"var storageBlobDataOwnerRole = new RoleAssignment(\"storageBlobDataOwner\", new RoleAssignmentArgs\n{\n    PrincipalId = functionApp.Identity.Apply(i => i.PrincipalId),\n    PrincipalType = PrincipalType.ServicePrincipal,\n    RoleDefinitionId = \"/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b\",\n    Scope = storageAccount.Id\n});\n",[57395],{"type":254,"tag":389,"props":57396,"children":57397},{"__ignoreMap":205},[57398,57452,57459,57522,57550,57578,57601],{"type":254,"tag":622,"props":57399,"children":57400},{"class":624,"line":625},[57401,57405,57410,57414,57418,57422,57426,57430,57435,57439,57443,57447],{"type":254,"tag":622,"props":57402,"children":57403},{"style":714},[57404],{"type":259,"value":1710},{"type":254,"tag":622,"props":57406,"children":57407},{"style":714},[57408],{"type":259,"value":57409}," storageBlobDataOwnerRole",{"type":254,"tag":622,"props":57411,"children":57412},{"style":629},[57413],{"type":259,"value":1619},{"type":254,"tag":622,"props":57415,"children":57416},{"style":629},[57417],{"type":259,"value":1624},{"type":254,"tag":622,"props":57419,"children":57420},{"style":714},[57421],{"type":259,"value":21942},{"type":254,"tag":622,"props":57423,"children":57424},{"style":629},[57425],{"type":259,"value":1668},{"type":254,"tag":622,"props":57427,"children":57428},{"style":629},[57429],{"type":259,"value":652},{"type":254,"tag":622,"props":57431,"children":57432},{"style":664},[57433],{"type":259,"value":57434},"storageBlobDataOwner",{"type":254,"tag":622,"props":57436,"children":57437},{"style":629},[57438],{"type":259,"value":652},{"type":254,"tag":622,"props":57440,"children":57441},{"style":629},[57442],{"type":259,"value":3149},{"type":254,"tag":622,"props":57444,"children":57445},{"style":629},[57446],{"type":259,"value":1624},{"type":254,"tag":622,"props":57448,"children":57449},{"style":714},[57450],{"type":259,"value":57451}," RoleAssignmentArgs\n",{"type":254,"tag":622,"props":57453,"children":57454},{"class":624,"line":635},[57455],{"type":254,"tag":622,"props":57456,"children":57457},{"style":629},[57458],{"type":259,"value":632},{"type":254,"tag":622,"props":57460,"children":57461},{"class":624,"line":679},[57462,57467,57471,57475,57479,57483,57487,57491,57495,57500,57504,57509,57513,57518],{"type":254,"tag":622,"props":57463,"children":57464},{"style":1202},[57465],{"type":259,"value":57466},"    PrincipalId ",{"type":254,"tag":622,"props":57468,"children":57469},{"style":629},[57470],{"type":259,"value":1654},{"type":254,"tag":622,"props":57472,"children":57473},{"style":1202},[57474],{"type":259,"value":56733},{"type":254,"tag":622,"props":57476,"children":57477},{"style":629},[57478],{"type":259,"value":594},{"type":254,"tag":622,"props":57480,"children":57481},{"style":1202},[57482],{"type":259,"value":6388},{"type":254,"tag":622,"props":57484,"children":57485},{"style":629},[57486],{"type":259,"value":594},{"type":254,"tag":622,"props":57488,"children":57489},{"style":1735},[57490],{"type":259,"value":21286},{"type":254,"tag":622,"props":57492,"children":57493},{"style":629},[57494],{"type":259,"value":1668},{"type":254,"tag":622,"props":57496,"children":57497},{"style":714},[57498],{"type":259,"value":57499},"i",{"type":254,"tag":622,"props":57501,"children":57502},{"style":629},[57503],{"type":259,"value":2697},{"type":254,"tag":622,"props":57505,"children":57506},{"style":1202},[57507],{"type":259,"value":57508}," i",{"type":254,"tag":622,"props":57510,"children":57511},{"style":629},[57512],{"type":259,"value":594},{"type":254,"tag":622,"props":57514,"children":57515},{"style":1202},[57516],{"type":259,"value":57517},"PrincipalId",{"type":254,"tag":622,"props":57519,"children":57520},{"style":629},[57521],{"type":259,"value":12556},{"type":254,"tag":622,"props":57523,"children":57524},{"class":624,"line":705},[57525,57530,57534,57538,57542,57546],{"type":254,"tag":622,"props":57526,"children":57527},{"style":1202},[57528],{"type":259,"value":57529},"    PrincipalType ",{"type":254,"tag":622,"props":57531,"children":57532},{"style":629},[57533],{"type":259,"value":1654},{"type":254,"tag":622,"props":57535,"children":57536},{"style":1202},[57537],{"type":259,"value":22019},{"type":254,"tag":622,"props":57539,"children":57540},{"style":629},[57541],{"type":259,"value":594},{"type":254,"tag":622,"props":57543,"children":57544},{"style":1202},[57545],{"type":259,"value":22028},{"type":254,"tag":622,"props":57547,"children":57548},{"style":629},[57549],{"type":259,"value":676},{"type":254,"tag":622,"props":57551,"children":57552},{"class":624,"line":732},[57553,57557,57561,57565,57570,57574],{"type":254,"tag":622,"props":57554,"children":57555},{"style":1202},[57556],{"type":259,"value":22040},{"type":254,"tag":622,"props":57558,"children":57559},{"style":629},[57560],{"type":259,"value":1654},{"type":254,"tag":622,"props":57562,"children":57563},{"style":629},[57564],{"type":259,"value":661},{"type":254,"tag":622,"props":57566,"children":57567},{"style":664},[57568],{"type":259,"value":57569},"/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b",{"type":254,"tag":622,"props":57571,"children":57572},{"style":629},[57573],{"type":259,"value":652},{"type":254,"tag":622,"props":57575,"children":57576},{"style":629},[57577],{"type":259,"value":676},{"type":254,"tag":622,"props":57579,"children":57580},{"class":624,"line":759},[57581,57585,57589,57593,57597],{"type":254,"tag":622,"props":57582,"children":57583},{"style":1202},[57584],{"type":259,"value":22070},{"type":254,"tag":622,"props":57586,"children":57587},{"style":629},[57588],{"type":259,"value":1654},{"type":254,"tag":622,"props":57590,"children":57591},{"style":1202},[57592],{"type":259,"value":56236},{"type":254,"tag":622,"props":57594,"children":57595},{"style":629},[57596],{"type":259,"value":594},{"type":254,"tag":622,"props":57598,"children":57599},{"style":1202},[57600],{"type":259,"value":23447},{"type":254,"tag":622,"props":57602,"children":57603},{"class":624,"line":798},[57604],{"type":254,"tag":622,"props":57605,"children":57606},{"style":629},[57607],{"type":259,"value":6661},{"type":254,"tag":427,"props":57609,"children":57610},{"icon":429},[57611],{"type":254,"tag":255,"props":57612,"children":57613},{},[57614],{"type":259,"value":57615},"Because the Azure provider is auto-generated from the Azure Resource Manager APIs, it is not always obvious to figure out what object and properties we should use to implement the infrastructure we want. Hopefully, because we are using a programming language in an IDE (C# in Visual Studio in my case), IntelliSense can help us.",{"type":254,"tag":255,"props":57617,"children":57618},{},[57619,57621,57626,57628,57633],{"type":259,"value":57620},"And that's just it, with this code the Function App will go well without needing the secret connection string of the storage in the AzureWebJobsStorage setting. Everything works fine thanks to the Managed Identity, the assignment of the correct role, and the setting ",{"type":254,"tag":389,"props":57622,"children":57624},{"className":57623},[],[57625],{"type":259,"value":55992},{"type":259,"value":57627},". One thing we could do is to remove the ",{"type":254,"tag":389,"props":57629,"children":57631},{"className":57630},[],[57632],{"type":259,"value":55992},{"type":259,"value":57634}," setting from the configuration to observe that without it, the Function App will not work properly: for instance, we would not be able to create function keys as a storage is needed to store them.",{"type":254,"tag":255,"props":57636,"children":57637},{},[57638,57640,57646],{"type":259,"value":57639},"You can find all this code on ",{"type":254,"tag":262,"props":57641,"children":57644},{"href":57642,"rel":57643},"https://github.com/TechWatching/FunctionAppWithoutSecretConnectionString",[266],[57645],{"type":259,"value":53777},{"type":259,"value":57647}," if you want to test it by yourself. You will also find an HttpTrigger Azure Function created from the templates in Visual Studio that I only used to have something deployed on my Function App.",{"type":254,"tag":282,"props":57649,"children":57651},{"id":57650},"how-can-we-remove-the-azurewebjobsstorage-secret-setting-using-terraform",[57652],{"type":259,"value":57653},"How can we remove the AzureWebJobsStorage secret setting using Terraform?",{"type":254,"tag":255,"props":57655,"children":57656},{},[57657],{"type":259,"value":57658},"I am a big fan of Pulumi's approach to build and deploy infrastructure but as Terraform is pretty popular for doing Infrastructure as Code, I thought it might be a good idea to explain how to solve the same issue using Terraform instead of Pulumi.",{"type":254,"tag":255,"props":57660,"children":57661},{},[57662,57664,57670,57671,57677,57679,57685,57686,57692],{"type":259,"value":57663},"Well, in fact, you can't. 🤔 Currently there is no way to remove AzureWebJobsStorage secret using Terraform. Indeed as you can see in the ",{"type":254,"tag":262,"props":57665,"children":57667},{"href":43337,"rel":57666},[266],[57668],{"type":259,"value":57669},"Terraform documentation of a Function App resource",{"type":259,"value":28819},{"type":254,"tag":389,"props":57672,"children":57674},{"className":57673},[],[57675],{"type":259,"value":57676},"azurerm_function_app",{"type":259,"value":57678},", the AzureWebJobsStorage setting is automatically filled based on the ",{"type":254,"tag":389,"props":57680,"children":57682},{"className":57681},[],[57683],{"type":259,"value":57684},"storage_account_name",{"type":259,"value":10347},{"type":254,"tag":389,"props":57687,"children":57689},{"className":57688},[],[57690],{"type":259,"value":57691},"storage_account_access_key",{"type":259,"value":57693}," parameters which are required. So not only you can't use the Managed Identity access to storage as we did in Pulumi but also you can't use a key vault reference for the AzureWebJobsStorage setting (see the documentation screenshot below).",{"type":254,"tag":255,"props":57695,"children":57696},{},[57697],{"type":254,"tag":1161,"props":57698,"children":57702},{"alt":57699,"className":57700,"src":57701},"Azure RM provider for Terraform documentation about AzureWebJobsStorage.",[1165,1166],"/posts/images/functionsidentity_terraform_1.png",[],{"type":254,"tag":255,"props":57704,"children":57705},{},[57706,57708,57714],{"type":259,"value":57707},"The only possibility right now is to use an ARM template in your Terraform project thanks to the ",{"type":254,"tag":389,"props":57709,"children":57711},{"className":57710},[],[57712],{"type":259,"value":57713},"azurerm_resource_group_template_deployment",{"type":259,"value":57715}," resource but that defeats the whole point of using Terraform especially for a major resource like a Function App.",{"type":254,"tag":255,"props":57717,"children":57718},{},[57719],{"type":259,"value":57720},"In the GitHub repository of the Terraform provider for Azure RM there are 2 issues relative to this problem (do not hesitate to vote for them):",{"type":254,"tag":332,"props":57722,"children":57723},{},[57724,57737],{"type":254,"tag":336,"props":57725,"children":57726},{},[57727,57728,57735],{"type":259,"value":19899},{"type":254,"tag":262,"props":57729,"children":57732},{"href":57730,"rel":57731},"https://github.com/hashicorp/terraform-provider-azurerm/issues/8977",[266],[57733],{"type":259,"value":57734},"8977 issue",{"type":259,"value":57736}," that aims at supporting a KeyVault reference for the AzureWebJobsStorage setting",{"type":254,"tag":336,"props":57738,"children":57739},{},[57740,57741,57748],{"type":259,"value":19899},{"type":254,"tag":262,"props":57742,"children":57745},{"href":57743,"rel":57744},"https://github.com/hashicorp/terraform-provider-azurerm/issues/13240",[266],[57746],{"type":259,"value":57747},"13240 issue",{"type":259,"value":57749}," that aims at supporting the Managed Identity access to storage",{"type":254,"tag":255,"props":57751,"children":57752},{},[57753],{"type":259,"value":57754},"I guess this will be implemented someday in Terraform provider for Azure RM, so that may not be a big deal but that clearly shows the limitations of Terraform providers.",{"type":254,"tag":427,"props":57756,"children":57757},{"icon":429},[57758],{"type":254,"tag":255,"props":57759,"children":57760},{},[57761],{"type":259,"value":57762},"Because Terraform provider for Azure RM is manually implemented using the Azure SDK, it does not match Azure APIs hence not all new resources and features are available (they have to be implemented in the provider as new features in Azure are released and it can take time). It's something that is not a problem with Pulumi Azure Native provider as the SDKs are generated automatically from the Azure API specifications which makes Pulumi Azure Native provider always up-to-date.",{"type":254,"tag":282,"props":57764,"children":57765},{"id":6186},[57766],{"type":259,"value":37936},{"type":254,"tag":255,"props":57768,"children":57769},{},[57770],{"type":259,"value":57771},"I hope that after reading this article if you are working on a Function App with an AzureWebJobsStorage setting you will take the time to replace it with an access to storage through the Managed Identity of your Function. One question you could ask me is why this is not the default behavior when creating a new Function App instead of using the AzureWebJobsStorage. And that would be an excellent question... for the Azure Functions team 😀.",{"type":254,"tag":255,"props":57773,"children":57774},{},[57775],{"type":259,"value":57776},"If you did not know Pulumi before reading this article, I hope it made you want to give it a try.\nHappy learning.",{"type":254,"tag":1260,"props":57778,"children":57779},{},[57780],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":57782},[57783,57784,57785,57786,57790,57791],{"id":55890,"depth":635,"text":55893},{"id":55930,"depth":635,"text":55933},{"id":55955,"depth":635,"text":55958},{"id":56025,"depth":635,"text":56028,"children":57787},[57788,57789],{"id":56043,"depth":679,"text":56046},{"id":56077,"depth":679,"text":56080},{"id":57650,"depth":635,"text":57653},{"id":6186,"depth":635,"text":37936},"content:1.posts:23.azure-functions-without-azurewebjobsstorage.md","1.posts/23.azure-functions-without-azurewebjobsstorage.md",{"_path":73,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":72,"description":57795,"date":57796,"image":57797,"badge":57799,"tags":57800,"body":57802,"_type":1275,"_id":58414,"_source":231,"_file":58415,"_extension":1278},"I recently played a bit with GitHub Actions and as I have spent some time running, again and again, my workflows to understand what was going wrong I thought it could be interesting to share what I have learned especially concerning environment variables and contexts.","2021-09-06T00:00:00.000Z",{"src":57798},"/images/github-figurine_1.jpg",{"label":9260},[38112,16354,57801],"jq",{"type":251,"children":57803,"toc":58407},[57804,57808,57816,57822,57834,57839,57848,57873,57886,57900,57911,57926,57939,57947,57959,57973,57979,57984,57997,58011,58024,58053,58066,58074,58086,58094,58184,58192,58334,58353,58359,58364,58385,58394,58398,58403],{"type":254,"tag":255,"props":57805,"children":57806},{},[57807],{"type":259,"value":57795},{"type":254,"tag":427,"props":57809,"children":57810},{"icon":429},[57811],{"type":254,"tag":255,"props":57812,"children":57813},{},[57814],{"type":259,"value":57815},"Disclaimer: Although I have some experience with Azure Pipelines, I am still learning GitHub Actions so I do not pretend to know everything about them nor do I never make mistakes when writing about them. Feel free to correct me in the comments if you think I am wrong about something or if something that I show can be done more effectively.",{"type":254,"tag":282,"props":57817,"children":57819},{"id":57818},"tip-n1-environment-variables-syntax-depends-on-the-shell-you-are-using-in-your-job",[57820],{"type":259,"value":57821},"Tip n°1: Environment variables syntax depends on the shell you are using in your job",{"type":254,"tag":255,"props":57823,"children":57824},{},[57825,57827,57832],{"type":259,"value":57826},"As you know a GitHub Actions workflow is composed of different jobs where each job is a set of steps that execute on the same runner. As a runner can be hosted Ubuntu, Windows, macOS, or even another operating system (if you host your own runner) the shell that will execute your commands will not be the same by default depending on the runner you choose. For instance, if you are on a Ubuntu GitHub-hosted runner, by default the shell will be bash whereas on a Windows GitHub-hosted runner it will be PowerShell (I think we don't say PowerShell Core anymore but I am speaking of ",{"type":254,"tag":389,"props":57828,"children":57830},{"className":57829},[],[57831],{"type":259,"value":46544},{"type":259,"value":57833},", not the old Windows PowerShell of course).",{"type":254,"tag":255,"props":57835,"children":57836},{},[57837],{"type":259,"value":57838},"This is really important to know because depending on the shell used, the syntax to use an environment variable in a script is different as you can see on the documentation screenshot below:",{"type":254,"tag":255,"props":57840,"children":57841},{},[57842],{"type":254,"tag":1161,"props":57843,"children":57847},{"alt":57844,"className":57845,"src":57846},"GitHub Actions' documentation of running a script.",[1165,1166],"/posts/images/githubactions_envvar_1.png",[],{"type":254,"tag":427,"props":57849,"children":57850},{"icon":429},[57851],{"type":254,"tag":255,"props":57852,"children":57853},{},[57854,57856,57863,57865,57871],{"type":259,"value":57855},"When I am talking about environment variables here I am referring to GitHub ",{"type":254,"tag":262,"props":57857,"children":57860},{"href":57858,"rel":57859},"https://docs.github.com/en/actions/reference/environment-variables",[266],[57861],{"type":259,"value":57862},"default environment variables",{"type":259,"value":57864}," (for instance ",{"type":254,"tag":389,"props":57866,"children":57868},{"className":57867},[],[57869],{"type":259,"value":57870},"GITHUB_EVENT_NAME",{"type":259,"value":57872}," which is the name of the webhook event that triggered the workflow) and to custom variables you set in a workflow. The syntax for environment variables you just create and use in a shell script does not change of course.",{"type":254,"tag":255,"props":57874,"children":57875},{},[57876,57878,57884],{"type":259,"value":57877},"The documentation briefly explains the syntax to use depending on the shell ",{"type":254,"tag":262,"props":57879,"children":57882},{"href":57880,"rel":57881},"https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#using-a-specific-shell",[266],[57883],{"type":259,"value":1414},{"type":259,"value":57885}," but you can easily miss it as I did. In fact, most of the GitHub Actions examples you can find are in bash so if you use them as-is without paying attention to the shell you are using, you will probably get it wrong. I lost a lot of time trying to figure out why my scripts were not working on a Windows runner so I hope knowing that you will avoid doing the same.",{"type":254,"tag":282,"props":57887,"children":57889},{"id":57888},"tip-n2-do-not-use-your-repository-github_token-in-tasks-that-need-to-trigger-another-workflow",[57890,57892,57898],{"type":259,"value":57891},"Tip n°2: Do not use your repository ",{"type":254,"tag":389,"props":57893,"children":57895},{"className":57894},[],[57896],{"type":259,"value":57897},"GITHUB_TOKEN",{"type":259,"value":57899}," in tasks that need to trigger another workflow.",{"type":254,"tag":255,"props":57901,"children":57902},{},[57903,57904,57909],{"type":259,"value":10970},{"type":254,"tag":389,"props":57905,"children":57907},{"className":57906},[],[57908],{"type":259,"value":57897},{"type":259,"value":57910}," is a secret you can use in your workflow to do some actions on your GitHub repository like pushing a tag, creating a new release, creating an issue... It is very convenient because it allows you to automate in your workflow many things for your GitHub repository using built-in actions or the GitHub REST API.",{"type":254,"tag":427,"props":57912,"children":57913},{"icon":429},[57914],{"type":254,"tag":255,"props":57915,"children":57916},{},[57917,57919,57924],{"type":259,"value":57918},"Be aware that internally GitHub Actions is a GitHub App that is installed on your repository when you start creating GitHub Actions workflows for this repository. That means the token represented by ",{"type":254,"tag":389,"props":57920,"children":57922},{"className":57921},[],[57923],{"type":259,"value":57897},{"type":259,"value":57925}," in your workflow is a GitHub App installation token that will only work on your repository workflow and that will have a limited set of permissions (but you can grant more permissions directly in your workflow if you want).",{"type":254,"tag":255,"props":57927,"children":57928},{},[57929,57931,57937],{"type":259,"value":57930},"If you want to know about authentication in a workflow, there is a dedicated ",{"type":254,"tag":262,"props":57932,"children":57935},{"href":57933,"rel":57934},"https://docs.github.com/en/actions/reference/authentication-in-a-workflow",[266],[57936],{"type":259,"value":51342},{"type":259,"value":57938}," on the topic in the GitHub documentation. However, if you read it quickly you may miss an important piece of information:",{"type":254,"tag":31352,"props":57940,"children":57941},{},[57942],{"type":254,"tag":255,"props":57943,"children":57944},{},[57945],{"type":259,"value":57946},"\"events triggered by the GITHUB_TOKEN will not create a new workflow run\"",{"type":254,"tag":255,"props":57948,"children":57949},{},[57950,57952,57957],{"type":259,"value":57951},"What does this mean? It means that if you have 2 workflows and that the first one uses the ",{"type":254,"tag":389,"props":57953,"children":57955},{"className":57954},[],[57956],{"type":259,"value":57897},{"type":259,"value":57958}," for an action that should trigger the second one, then the second workflow will never be triggered. The aim is to prevent you from making an infinite loop of workflows runs triggering each other.",{"type":254,"tag":255,"props":57960,"children":57961},{},[57962,57964,57971],{"type":259,"value":57963},"So imagine you want to implement a workflow that publishes a release when a new tag is pushed on a repository and another workflow that automatically tweets about your new release once it is published, how can you do that? You just have to ",{"type":254,"tag":262,"props":57965,"children":57968},{"href":57966,"rel":57967},"https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token",[266],[57969],{"type":259,"value":57970},"create a GitHub Personal Access Token",{"type":259,"value":57972},", add it as a secret in your repository, and use it in your first workflow to create your release. This way, the second workflow will run fine when the release is published.",{"type":254,"tag":282,"props":57974,"children":57976},{"id":57975},"tip-n3-assign-information-from-the-event-triggering-a-github-actions-workflow-to-a-powershell-variable",[57977],{"type":259,"value":57978},"Tip n°3: Assign information from the event triggering a GitHub Actions workflow to a PowerShell variable.",{"type":254,"tag":255,"props":57980,"children":57981},{},[57982],{"type":259,"value":57983},"Sometimes, in a GitHub Actions workflow, we want to execute some PowerShell to do specific actions depending on information from the event that triggered the workflow. Hence having a PowerShell variable with this data could be useful. For instance, if we have a workflow triggered by the publication of a release, maybe we need to retrieve the URLs of the binaries of this release which can be done easily if the event data is in a PowerShell variable.",{"type":254,"tag":255,"props":57985,"children":57986},{},[57987,57989,57995],{"type":259,"value":57988},"The webhook payload corresponding to the event that triggered a workflow is part of the properties of the ",{"type":254,"tag":389,"props":57990,"children":57992},{"className":57991},[],[57993],{"type":259,"value":57994},"github",{"type":259,"value":57996}," context.",{"type":254,"tag":427,"props":57998,"children":57999},{"icon":429},[58000],{"type":254,"tag":255,"props":58001,"children":58002},{},[58003,58005],{"type":259,"value":58004},"If you are not familiar with contexts in GitHub Actions, they are objects that contain information about the current workflow, job, runner, or things like that. Contexts are often used in expressions to determine whether or not a step of the workflow should be executed or to set some variables as you can see in the sample below.\n",{"type":254,"tag":1161,"props":58006,"children":58010},{"alt":58007,"className":58008,"src":58009},"GitHub Actions sample to create a draft release.",[1165,1166],"/posts/images/githubactions_context_1.png",[],{"type":254,"tag":255,"props":58012,"children":58013},{},[58014,58016,58022],{"type":259,"value":58015},"As you can read in the ",{"type":254,"tag":262,"props":58017,"children":58020},{"href":58018,"rel":58019},"https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions",[266],[58021],{"type":259,"value":6340},{"type":259,"value":58023},", the event property is of type object which makes it difficult to query it and to assign it to a PowerShell variable. Fortunately, there are ways to do that which are based on the fact that GitHub event data is persisted in a JSON file on the runner file system.",{"type":254,"tag":255,"props":58025,"children":58026},{},[58027,58029,58039,58041,58052],{"type":259,"value":58028},"The first one is to use the tool ",{"type":254,"tag":262,"props":58030,"children":58033},{"href":58031,"rel":58032},"https://stedolan.github.io/jq/",[266],[58034],{"type":254,"tag":389,"props":58035,"children":58037},{"className":58036},[],[58038],{"type":259,"value":57801},{"type":259,"value":58040}," which is already installed on GitHub's runners and allows you to process JSON data. Edward Thomson has a blog post that explains how you can use jq with GitHub Actions to do that: ",{"type":254,"tag":262,"props":58042,"children":58045},{"href":58043,"rel":58044},"https://www.edwardthomson.com/blog/github_actions_12_information_about_your_workflow.html",[266],[58046],{"type":254,"tag":389,"props":58047,"children":58049},{"className":58048},[],[58050],{"type":259,"value":58051},"GitHub Actions Day 12: Information about your Workflow",{"type":259,"value":594},{"type":254,"tag":255,"props":58054,"children":58055},{},[58056,58058,58064],{"type":259,"value":58057},"The second one is to directly use PowerShell to grab the GitHub context like this: ",{"type":254,"tag":389,"props":58059,"children":58061},{"className":58060},[],[58062],{"type":259,"value":58063},"$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json",{"type":259,"value":58065},". Thanks a lot Edward Thomson for helping me with this tip 😀.",{"type":254,"tag":31130,"props":58067,"children":58069},{"className":58068},[31133,31134],[58070],{"type":254,"tag":31137,"props":58071,"children":58073},{"id":58072},"1417509550786322440",[],{"type":254,"tag":255,"props":58075,"children":58076},{},[58077,58079,58084],{"type":259,"value":58078},"To illustrate that, here are a sample of using these two ways to assign the URL of the binary published by a release to a PowerShell variable using the event property of the ",{"type":254,"tag":389,"props":58080,"children":58082},{"className":58081},[],[58083],{"type":259,"value":57994},{"type":259,"value":58085}," context:",{"type":254,"tag":332,"props":58087,"children":58088},{},[58089],{"type":254,"tag":336,"props":58090,"children":58091},{},[58092],{"type":259,"value":58093},"using jq:",{"type":254,"tag":612,"props":58095,"children":58097},{"className":10656,"code":58096,"language":10658,"meta":205,"style":205},"$installerUrl = $(jq --raw-output '.release.assets[].browser_download_url | select(contains(\\\"windows.msi\\\"))' \"${{ github.event_path }}\")\n",[58098],{"type":254,"tag":389,"props":58099,"children":58100},{"__ignoreMap":205},[58101],{"type":254,"tag":622,"props":58102,"children":58103},{"class":624,"line":625},[58104,58108,58113,58117,58122,58127,58131,58136,58140,58145,58149,58154,58158,58163,58168,58172,58176,58180],{"type":254,"tag":622,"props":58105,"children":58106},{"style":629},[58107],{"type":259,"value":16922},{"type":254,"tag":622,"props":58109,"children":58110},{"style":1202},[58111],{"type":259,"value":58112},"installerUrl ",{"type":254,"tag":622,"props":58114,"children":58115},{"style":629},[58116],{"type":259,"value":1654},{"type":254,"tag":622,"props":58118,"children":58119},{"style":629},[58120],{"type":259,"value":58121}," $(",{"type":254,"tag":622,"props":58123,"children":58124},{"style":1202},[58125],{"type":259,"value":58126},"jq ",{"type":254,"tag":622,"props":58128,"children":58129},{"style":629},[58130],{"type":259,"value":10675},{"type":254,"tag":622,"props":58132,"children":58133},{"style":1202},[58134],{"type":259,"value":58135},"raw",{"type":254,"tag":622,"props":58137,"children":58138},{"style":629},[58139],{"type":259,"value":17065},{"type":254,"tag":622,"props":58141,"children":58142},{"style":1202},[58143],{"type":259,"value":58144},"output ",{"type":254,"tag":622,"props":58146,"children":58147},{"style":629},[58148],{"type":259,"value":46169},{"type":254,"tag":622,"props":58150,"children":58151},{"style":664},[58152],{"type":259,"value":58153},".release.assets[].browser_download_url | select(contains(\\\"windows.msi\\\"))",{"type":254,"tag":622,"props":58155,"children":58156},{"style":629},[58157],{"type":259,"value":46169},{"type":254,"tag":622,"props":58159,"children":58160},{"style":629},[58161],{"type":259,"value":58162}," \"${",{"type":254,"tag":622,"props":58164,"children":58165},{"style":1202},[58166],{"type":259,"value":58167},"{ github.event_path ",{"type":254,"tag":622,"props":58169,"children":58170},{"style":629},[58171],{"type":259,"value":2421},{"type":254,"tag":622,"props":58173,"children":58174},{"style":664},[58175],{"type":259,"value":2421},{"type":254,"tag":622,"props":58177,"children":58178},{"style":629},[58179],{"type":259,"value":652},{"type":254,"tag":622,"props":58181,"children":58182},{"style":629},[58183],{"type":259,"value":1685},{"type":254,"tag":332,"props":58185,"children":58186},{},[58187],{"type":254,"tag":336,"props":58188,"children":58189},{},[58190],{"type":259,"value":58191},"using ConvertFrom-Json method in PowerShell:",{"type":254,"tag":612,"props":58193,"children":58195},{"className":10656,"code":58194,"language":10658,"meta":205,"style":205},"$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json\n$installerUrl = $github.release.assets | Where-Object -Property name -match 'windows.msi' | Select -ExpandProperty browser_download_url -First 1\n",[58196],{"type":254,"tag":389,"props":58197,"children":58198},{"__ignoreMap":205},[58199,58242],{"type":254,"tag":622,"props":58200,"children":58201},{"class":624,"line":625},[58202,58206,58211,58215,58220,58224,58229,58233,58237],{"type":254,"tag":622,"props":58203,"children":58204},{"style":629},[58205],{"type":259,"value":16922},{"type":254,"tag":622,"props":58207,"children":58208},{"style":1202},[58209],{"type":259,"value":58210},"github ",{"type":254,"tag":622,"props":58212,"children":58213},{"style":629},[58214],{"type":259,"value":1654},{"type":254,"tag":622,"props":58216,"children":58217},{"style":1735},[58218],{"type":259,"value":58219}," Get-Content",{"type":254,"tag":622,"props":58221,"children":58222},{"style":629},[58223],{"type":259,"value":16556},{"type":254,"tag":622,"props":58225,"children":58226},{"style":664},[58227],{"type":259,"value":58228},"${{ github.event_path }}",{"type":254,"tag":622,"props":58230,"children":58231},{"style":629},[58232],{"type":259,"value":46169},{"type":254,"tag":622,"props":58234,"children":58235},{"style":629},[58236],{"type":259,"value":41040},{"type":254,"tag":622,"props":58238,"children":58239},{"style":1735},[58240],{"type":259,"value":58241}," ConvertFrom-Json\n",{"type":254,"tag":622,"props":58243,"children":58244},{"class":624,"line":635},[58245,58249,58253,58257,58261,58266,58270,58275,58279,58284,58289,58293,58298,58302,58306,58311,58315,58320,58324,58329],{"type":254,"tag":622,"props":58246,"children":58247},{"style":629},[58248],{"type":259,"value":16922},{"type":254,"tag":622,"props":58250,"children":58251},{"style":1202},[58252],{"type":259,"value":58112},{"type":254,"tag":622,"props":58254,"children":58255},{"style":629},[58256],{"type":259,"value":1654},{"type":254,"tag":622,"props":58258,"children":58259},{"style":629},[58260],{"type":259,"value":39174},{"type":254,"tag":622,"props":58262,"children":58263},{"style":1202},[58264],{"type":259,"value":58265},"github.release.assets ",{"type":254,"tag":622,"props":58267,"children":58268},{"style":629},[58269],{"type":259,"value":17875},{"type":254,"tag":622,"props":58271,"children":58272},{"style":1735},[58273],{"type":259,"value":58274}," Where-Object",{"type":254,"tag":622,"props":58276,"children":58277},{"style":629},[58278],{"type":259,"value":10733},{"type":254,"tag":622,"props":58280,"children":58281},{"style":1202},[58282],{"type":259,"value":58283},"Property name ",{"type":254,"tag":622,"props":58285,"children":58286},{"style":629},[58287],{"type":259,"value":58288},"-match",{"type":254,"tag":622,"props":58290,"children":58291},{"style":629},[58292],{"type":259,"value":16556},{"type":254,"tag":622,"props":58294,"children":58295},{"style":664},[58296],{"type":259,"value":58297},"windows.msi",{"type":254,"tag":622,"props":58299,"children":58300},{"style":629},[58301],{"type":259,"value":46169},{"type":254,"tag":622,"props":58303,"children":58304},{"style":629},[58305],{"type":259,"value":41040},{"type":254,"tag":622,"props":58307,"children":58308},{"style":1202},[58309],{"type":259,"value":58310}," Select ",{"type":254,"tag":622,"props":58312,"children":58313},{"style":629},[58314],{"type":259,"value":17065},{"type":254,"tag":622,"props":58316,"children":58317},{"style":1202},[58318],{"type":259,"value":58319},"ExpandProperty browser_download_url ",{"type":254,"tag":622,"props":58321,"children":58322},{"style":629},[58323],{"type":259,"value":17065},{"type":254,"tag":622,"props":58325,"children":58326},{"style":1202},[58327],{"type":259,"value":58328},"First ",{"type":254,"tag":622,"props":58330,"children":58331},{"style":741},[58332],{"type":259,"value":58333},"1\n",{"type":254,"tag":255,"props":58335,"children":58336},{},[58337,58339,58344,58346,58352],{"type":259,"value":58338},"I did not know about jq but I find it is a nice tool although the syntax is not that straightforward. Yet, I prefer the PowerShell way because it allows to directly manipulate an object instead of a JSON string. It is what I did to automate the upgrade of the ",{"type":254,"tag":389,"props":58340,"children":58342},{"className":58341},[],[58343],{"type":259,"value":23654},{"type":259,"value":58345}," winget package using GitHub Actions when a new release is published (you can read more about it in this ",{"type":254,"tag":262,"props":58347,"children":58350},{"href":58348,"rel":58349},"https://www.techwatching.dev/posts/wingetcreate",[266],[58351],{"type":259,"value":36966},{"type":259,"value":2280},{"type":254,"tag":282,"props":58354,"children":58356},{"id":58355},"tip-n4-enable-debugging-logs-to-help-you-understand-what-is-going-wrong-in-your-workflow",[58357],{"type":259,"value":58358},"Tip n°4: Enable debugging logs to help you understand what is going wrong in your workflow",{"type":254,"tag":255,"props":58360,"children":58361},{},[58362],{"type":259,"value":58363},"Like many CI / CD platforms, GitHub Actions has the disadvantage of not being testable locally which makes it hard to debug a workflow when something is not working properly. Moreover, by default, you won't see how some expressions/contexts are evaluated when the workflow run so logs will not help you figure out what is wrong in your workflow definition.",{"type":254,"tag":255,"props":58365,"children":58366},{},[58367,58369,58375,58377,58383],{"type":259,"value":58368},"Hopefully, you can enable debugging logs for all pipelines in your repository (unfortunately you can't specify it for a specific pipeline only) by setting the secret ",{"type":254,"tag":389,"props":58370,"children":58372},{"className":58371},[],[58373],{"type":259,"value":58374},"ACTIONS_STEP_DEBUG",{"type":259,"value":58376}," to true in your repository. You can read more about it in the ",{"type":254,"tag":262,"props":58378,"children":58381},{"href":58379,"rel":58380},"https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging",[266],[58382],{"type":259,"value":6340},{"type":259,"value":58384}," but here is what it look likes in a workflow run:",{"type":254,"tag":255,"props":58386,"children":58387},{},[58388],{"type":254,"tag":1161,"props":58389,"children":58393},{"alt":58390,"className":58391,"src":58392},"Debug logs of a Github workflow run.",[1165,1166],"/posts/images/githubactions_logs_1.png",[],{"type":254,"tag":58395,"props":58396,"children":58397},"hr",{},[],{"type":254,"tag":255,"props":58399,"children":58400},{},[58401],{"type":259,"value":58402},"I hope these 4 tips will help you build awesome GitHub Actions workflows.",{"type":254,"tag":1260,"props":58404,"children":58405},{},[58406],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":58408},[58409,58410,58412,58413],{"id":57818,"depth":635,"text":57821},{"id":57888,"depth":635,"text":58411},"Tip n°2: Do not use your repository GITHUB_TOKEN in tasks that need to trigger another workflow.",{"id":57975,"depth":635,"text":57978},{"id":58355,"depth":635,"text":58358},"content:1.posts:22.github-actions-var-and-context.md","1.posts/22.github-actions-var-and-context.md",{"_path":70,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":69,"description":58417,"lead":58418,"date":58419,"image":58420,"badge":58422,"tags":58423,"body":58425,"_type":1275,"_id":59711,"_source":231,"_file":59712,"_extension":1278},"In my previous articles about winget I talked about installing packages but I did not talk about producing packages for Windows Package Manager. So let's set things right.","Automate the upgrade of a winget package with GitHub Actions and Winget Create.","2021-08-25T00:00:00.000Z",{"src":58421},"/images/surface_2.jpg",{"label":1286},[37214,58424,31105,245,40203,16354],"wingetcreate",{"type":251,"children":58426,"toc":59689},[58427,58441,58447,58461,58470,58481,58486,58492,58498,58526,58554,58566,58579,58590,58603,58614,58619,58623,58637,58642,58648,58654,58663,58668,58673,58679,58689,58703,58712,58725,58734,58747,58753,58773,58791,58796,58932,58937,58950,58955,59021,59040,59134,59146,59201,59206,59334,59342,59361,59370,59375,59384,59390,59403,59624,59629,59638,59644,59649,59654,59685],{"type":254,"tag":255,"props":58428,"children":58429},{},[58430,58432,58439],{"type":259,"value":58431},"In my ",{"type":254,"tag":262,"props":58433,"children":58436},{"href":58434,"rel":58435},"https://www.techwatching.dev/tags/winget",[266],[58437],{"type":259,"value":58438},"previous articles about winget",{"type":259,"value":58440}," I talked about installing packages but I did not talk about producing packages for Windows Package Manager. So let's set things right.",{"type":254,"tag":282,"props":58442,"children":58444},{"id":58443},"about-winget-packages",[58445],{"type":259,"value":58446},"About winget packages",{"type":254,"tag":255,"props":58448,"children":58449},{},[58450,58452,58459],{"type":259,"value":58451},"Windows Package Manager allows you to search and install applications that are referenced by the sources you have configured to be used by the winget tool. Sources are repositories that list applications that can be installed by winget and the data needed for them to be installed (in the form of a manifest file containing information such as the installer location of a package for instance). The default source is the ",{"type":254,"tag":262,"props":58453,"children":58456},{"href":58454,"rel":58455},"https://github.com/microsoft/winget-pkgs",[266],[58457],{"type":259,"value":58458},"Windows Package Manager Community Repository",{"type":259,"value":58460}," which is a public GitHub repository where everyone can submit their application package manifest to make an application available for installation to Windows Package Manager users.",{"type":254,"tag":255,"props":58462,"children":58463},{},[58464],{"type":254,"tag":1161,"props":58465,"children":58469},{"alt":58466,"className":58467,"src":58468},"Windows Package Manager Community repository readme.",[1165,1166],"/posts/images/wingetcreate_package_repository.png",[],{"type":254,"tag":255,"props":58471,"children":58472},{},[58473,58475],{"type":259,"value":58474},"Once you know that, if you are the developer of an application you want to distribute on Windows through the Windows Package Manager you have to create a manifest for your application and publish it through a Pull Request on the Windows Package Manager Community Repository. And each time you release a new version of your application, you have to update your app manifest with the information of your new package version (new version number, new installer location...) and create a PR to the Windows Package Manager Community Repository with this updated version of your manifest. For more details, you can have a look at the official ",{"type":254,"tag":262,"props":58476,"children":58479},{"href":58477,"rel":58478},"https://docs.microsoft.com/en-us/windows/package-manager/package/",[266],[58480],{"type":259,"value":6340},{"type":254,"tag":255,"props":58482,"children":58483},{},[58484],{"type":259,"value":58485},"As a package creator, you probably do not want to create and update this app manifest manually. Luckily for you, there is a tool to do that for you.",{"type":254,"tag":282,"props":58487,"children":58489},{"id":58488},"wingetcreate-to-the-rescue",[58490],{"type":259,"value":58491},"WingetCreate to the rescue",{"type":254,"tag":455,"props":58493,"children":58495},{"id":58494},"introducing-wingetcreate",[58496],{"type":259,"value":58497},"Introducing WingetCreate",{"type":254,"tag":255,"props":58499,"children":58500},{},[58501,58508,58510,58517,58519,58525],{"type":254,"tag":262,"props":58502,"children":58505},{"href":58503,"rel":58504},"https://github.com/microsoft/winget-create",[266],[58506],{"type":259,"value":58507},"Windows Package Manager Manifest Creator",{"type":259,"value":58509}," aka WingetCreate is a tool \"designed to help generate or update manifest files for the Community repo\" (quoting the readme of WingetCreate repository). At the time of writing it is still in preview but you can already use it to help you with your manifest files. You can download the installer from ",{"type":254,"tag":262,"props":58511,"children":58514},{"href":58512,"rel":58513},"https://aka.ms/wingetcreate/latest",[266],[58515],{"type":259,"value":58516},"this link",{"type":259,"value":58518}," but of course, it is available from winget: ",{"type":254,"tag":389,"props":58520,"children":58522},{"className":58521},[],[58523],{"type":259,"value":58524},"winget install wingetcreate",{"type":259,"value":594},{"type":254,"tag":255,"props":58527,"children":58528},{},[58529,58531,58537,58538,58545,58546,58553],{"type":259,"value":58530},"The main commands are ",{"type":254,"tag":262,"props":58532,"children":58535},{"href":58533,"rel":58534},"https://github.com/microsoft/winget-create/blob/main/doc/new.md",[266],[58536],{"type":259,"value":2915},{"type":259,"value":16280},{"type":254,"tag":262,"props":58539,"children":58542},{"href":58540,"rel":58541},"https://github.com/microsoft/winget-create/blob/main/doc/update.md",[266],[58543],{"type":259,"value":58544},"Update",{"type":259,"value":10347},{"type":254,"tag":262,"props":58547,"children":58550},{"href":58548,"rel":58549},"https://github.com/microsoft/winget-create/blob/main/doc/submit.md",[266],[58551],{"type":259,"value":58552},"Submit",{"type":259,"value":594},{"type":254,"tag":455,"props":58555,"children":58557},{"id":58556},"the-new-command",[58558,58559,58564],{"type":259,"value":10970},{"type":254,"tag":389,"props":58560,"children":58562},{"className":58561},[],[58563],{"type":259,"value":2915},{"type":259,"value":58565}," command",{"type":254,"tag":255,"props":58567,"children":58568},{},[58569,58571,58577],{"type":259,"value":58570},"It allows you to create a new manifest from scratch. If you don't know where to start to deal with manifest files it is a nice way of getting started. Yet having a look at existing manifests in the ",{"type":254,"tag":262,"props":58572,"children":58574},{"href":58454,"rel":58573},[266],[58575],{"type":259,"value":58576},"winget community repository",{"type":259,"value":58578}," can be sometimes more efficient.",{"type":254,"tag":455,"props":58580,"children":58582},{"id":58581},"the-update-command",[58583,58584,58589],{"type":259,"value":10970},{"type":254,"tag":389,"props":58585,"children":58587},{"className":58586},[],[58588],{"type":259,"value":58544},{"type":259,"value":58565},{"type":254,"tag":255,"props":58591,"children":58592},{},[58593,58595,58601],{"type":259,"value":58594},"It allows you to update an existing manifest, that is to say, to create an updated version of your manifest when you have released a new version of your application (so new version number and new installer URL). You can use this command to ",{"type":254,"tag":389,"props":58596,"children":58598},{"className":58597},[],[58599],{"type":259,"value":58600},"submit",{"type":259,"value":58602}," your updated package to the Windows Package Manager Community Repository. In my opinion, it is the most useful command from WingetCreate as it can be easily be integrated into a build pipeline to publish your installer.",{"type":254,"tag":455,"props":58604,"children":58606},{"id":58605},"the-submit-command",[58607,58608,58613],{"type":259,"value":10970},{"type":254,"tag":389,"props":58609,"children":58611},{"className":58610},[],[58612],{"type":259,"value":58552},{"type":259,"value":58565},{"type":254,"tag":255,"props":58615,"children":58616},{},[58617],{"type":259,"value":58618},"It allows you to submit an existing manifest (you created earlier on disk with the create or update command) to the Windows Package Manager Community Repository automatically. Basically, what it does is that it uses the GitHub personal access token you give it to create a Pull Request with your manifest in this repository.",{"type":254,"tag":455,"props":58620,"children":58621},{"id":43135},[58622],{"type":259,"value":43138},{"type":254,"tag":255,"props":58624,"children":58625},{},[58626,58628,58635],{"type":259,"value":58627},"If you look at the ",{"type":254,"tag":262,"props":58629,"children":58632},{"href":58630,"rel":58631},"https://github.com/microsoft/winget-create/blob/main/doc/settings.md",[266],[58633],{"type":259,"value":58634},"settings command",{"type":259,"value":58636}," you will see that you can specify the name of the GitHub repository to target for your package submission. This is interesting if you want to host a private source for winget available to your organization only where you will publish applications related to your business needs and that you don't want to make available publicly.",{"type":254,"tag":255,"props":58638,"children":58639},{},[58640],{"type":259,"value":58641},"WingetCreate is a really helpful tool to create, update and validate a manifest for your winget package. Still, you probably don't want to manually run WingetCreate each time you release a new package version. So let's see how to automate that with GitHub Actions.",{"type":254,"tag":282,"props":58643,"children":58645},{"id":58644},"automating-your-app-manifest-upgrade-with-github-actions",[58646],{"type":259,"value":58647},"Automating your app manifest upgrade with GitHub Actions",{"type":254,"tag":455,"props":58649,"children":58651},{"id":58650},"why-using-github-actions-to-demonstrate-the-automation-of-app-manifests-upgrades",[58652],{"type":259,"value":58653},"Why using GitHub Actions to demonstrate the automation of app manifests upgrades?",{"type":254,"tag":255,"props":58655,"children":58656},{},[58657],{"type":254,"tag":1161,"props":58658,"children":58662},{"alt":58659,"className":58660,"src":58661},"GitHub Actions documentation.",[1165,1166],"/posts/images/wingetcreate_githubactions.png",[],{"type":254,"tag":255,"props":58664,"children":58665},{},[58666],{"type":259,"value":58667},"In my daily work, Azure Pipelines are the pipelines I used to do CI/CD and they are great. Currently, they offer more functionalities than GitHub Actions and as the code I develop is hosted in Azure Repos it makes more sense to use the Azure DevOps built-in CI/CD tool than something else (although Azure DevOps does not enforce at all you to choose their tools). However there is already in WingetCreate's readme a section with a link to an example about using WingetCreate with Azure Pipelines, but there is no example with GitHub Actions.",{"type":254,"tag":255,"props":58669,"children":58670},{},[58671],{"type":259,"value":58672},"Moreover, I think many applications that are available or will want to be available as a winget package are open source applications whose code are hosted in a GitHub repository and that are already using GitHub Actions for their CI/CD. So I thought it could be useful to have an example of using WingetCreate with GitHub Actions, especially as GitHub has this concept of \"releases\".",{"type":254,"tag":455,"props":58674,"children":58676},{"id":58675},"an-interesting-use-case-for-with-nushell",[58677],{"type":259,"value":58678},"An interesting use case for with Nushell",{"type":254,"tag":255,"props":58680,"children":58681},{},[58682,58687],{"type":254,"tag":262,"props":58683,"children":58685},{"href":23650,"rel":58684},[266],[58686],{"type":259,"value":23654},{"type":259,"value":58688}," is a cross-platform shell written in Rust. Nushell's developers took the best of existing shells (like the structured data approach from PowerShell) and created a shell that feels modern, easy-to-use, and very useful in my opinion.",{"type":254,"tag":255,"props":58690,"children":58691},{},[58692,58694,58701],{"type":259,"value":58693},"There was a ",{"type":254,"tag":262,"props":58695,"children":58698},{"href":58696,"rel":58697},"https://github.com/nushell/nushell/issues/1859",[266],[58699],{"type":259,"value":58700},"GitHub issue",{"type":259,"value":58702}," to support the new official Windows package manager so I though it was the opportunity to contribute to Nushell. Contributing to this project was something that I had not been able to do yet because I did not know Rust, writing CI/CD pipelines however is something I can do.",{"type":254,"tag":255,"props":58704,"children":58705},{},[58706],{"type":254,"tag":1161,"props":58707,"children":58711},{"alt":58708,"className":58709,"src":58710},"Nushell documentation page.",[1165,1166],"/posts/images/wingetcreate_nushell.png",[],{"type":254,"tag":255,"props":58713,"children":58714},{},[58715,58717,58723],{"type":259,"value":58716},"Nushell already uses GitHub Actions for its continuous integration and to create releases. If you are not familiar with GitHub releases you can read the ",{"type":254,"tag":262,"props":58718,"children":58721},{"href":58719,"rel":58720},"https://docs.github.com/en/github/administering-a-repository/releasing-projects-on-github/about-releases",[266],[58722],{"type":259,"value":47276},{"type":259,"value":58724}," but basically a release is a version of your software (corresponding to a git tag in your repository) that you make available with release notes and binaries files.",{"type":254,"tag":255,"props":58726,"children":58727},{},[58728],{"type":254,"tag":1161,"props":58729,"children":58733},{"alt":58730,"className":58731,"src":58732},"Nushell release page in GitHub.",[1165,1166],"/posts/images/wingetcreate_release.png",[],{"type":254,"tag":255,"props":58735,"children":58736},{},[58737,58739,58745],{"type":259,"value":58738},"Therefore, the idea was to update Nushell manifest with the latest version of Nushell using ",{"type":254,"tag":389,"props":58740,"children":58742},{"className":58741},[],[58743],{"type":259,"value":58744},"WingetCreate",{"type":259,"value":58746}," each time a new release of Nushell is published.",{"type":254,"tag":455,"props":58748,"children":58750},{"id":58749},"triggering-a-new-workflow-from-a-release-event",[58751],{"type":259,"value":58752},"Triggering a new workflow from a release event",{"type":254,"tag":255,"props":58754,"children":58755},{},[58756,58758,58764,58766,58771],{"type":259,"value":58757},"Automating the app manifest upgrade of Nushell just meant creating a ",{"type":254,"tag":389,"props":58759,"children":58761},{"className":58760},[],[58762],{"type":259,"value":58763},"job",{"type":259,"value":58765}," in a GitHub Actions workflow that would call ",{"type":254,"tag":389,"props":58767,"children":58769},{"className":58768},[],[58770],{"type":259,"value":58744},{"type":259,"value":58772}," with the new version number and the new installer URL.",{"type":254,"tag":255,"props":58774,"children":58775},{},[58776,58778,58783,58785,58790],{"type":259,"value":58777},"I first wanted to modify the existing Nushell GitHub Actions workflow that was creating the releases by adding a new ",{"type":254,"tag":389,"props":58779,"children":58781},{"className":58780},[],[58782],{"type":259,"value":58763},{"type":259,"value":58784}," at the end of the workflow just after the release was created. Well this is was a bad idea, I pushed this change and during the next release of Nushell the workflow failed because I did not pay attention that the workflow was creating releases in draft, so the installer URL of the new version did not exist when my job called ",{"type":254,"tag":389,"props":58786,"children":58788},{"className":58787},[],[58789],{"type":259,"value":58744},{"type":259,"value":594},{"type":254,"tag":255,"props":58792,"children":58793},{},[58794],{"type":259,"value":58795},"Because of that, I decided to create a separate workflow that would be triggered each time a Nushell release is published. In Nushell this is done manually (passing from draft to release) but even if it were done automatically by the release workflow I think it is a better idea to have a specific workflow triggered by the publication of a release.",{"type":254,"tag":612,"props":58797,"children":58799},{"className":51570,"code":58798,"language":233,"meta":205,"style":205},"name: Submit Nushell package to Windows Package Manager Community Repository \n\non:\n  release:\n    types: [published]\n\njobs:\n\n  winget:\n    name: Publish winget package\n",[58800],{"type":254,"tag":389,"props":58801,"children":58802},{"__ignoreMap":205},[58803,58823,58830,58841,58853,58878,58885,58896,58903,58915],{"type":254,"tag":622,"props":58804,"children":58805},{"class":624,"line":625},[58806,58810,58814,58819],{"type":254,"tag":622,"props":58807,"children":58808},{"style":768},[58809],{"type":259,"value":16394},{"type":254,"tag":622,"props":58811,"children":58812},{"style":629},[58813],{"type":259,"value":474},{"type":254,"tag":622,"props":58815,"children":58816},{"style":664},[58817],{"type":259,"value":58818}," Submit Nushell package to Windows Package Manager Community Repository",{"type":254,"tag":622,"props":58820,"children":58821},{"style":1202},[58822],{"type":259,"value":2314},{"type":254,"tag":622,"props":58824,"children":58825},{"class":624,"line":635},[58826],{"type":254,"tag":622,"props":58827,"children":58828},{"emptyLinePlaceholder":1699},[58829],{"type":259,"value":1702},{"type":254,"tag":622,"props":58831,"children":58832},{"class":624,"line":679},[58833,58837],{"type":254,"tag":622,"props":58834,"children":58835},{"style":7715},[58836],{"type":259,"value":16411},{"type":254,"tag":622,"props":58838,"children":58839},{"style":629},[58840],{"type":259,"value":16416},{"type":254,"tag":622,"props":58842,"children":58843},{"class":624,"line":705},[58844,58849],{"type":254,"tag":622,"props":58845,"children":58846},{"style":768},[58847],{"type":259,"value":58848},"  release",{"type":254,"tag":622,"props":58850,"children":58851},{"style":629},[58852],{"type":259,"value":16416},{"type":254,"tag":622,"props":58854,"children":58855},{"class":624,"line":732},[58856,58861,58865,58869,58874],{"type":254,"tag":622,"props":58857,"children":58858},{"style":768},[58859],{"type":259,"value":58860},"    types",{"type":254,"tag":622,"props":58862,"children":58863},{"style":629},[58864],{"type":259,"value":474},{"type":254,"tag":622,"props":58866,"children":58867},{"style":629},[58868],{"type":259,"value":13676},{"type":254,"tag":622,"props":58870,"children":58871},{"style":664},[58872],{"type":259,"value":58873},"published",{"type":254,"tag":622,"props":58875,"children":58876},{"style":629},[58877],{"type":259,"value":11003},{"type":254,"tag":622,"props":58879,"children":58880},{"class":624,"line":759},[58881],{"type":254,"tag":622,"props":58882,"children":58883},{"emptyLinePlaceholder":1699},[58884],{"type":259,"value":1702},{"type":254,"tag":622,"props":58886,"children":58887},{"class":624,"line":798},[58888,58892],{"type":254,"tag":622,"props":58889,"children":58890},{"style":768},[58891],{"type":259,"value":16489},{"type":254,"tag":622,"props":58893,"children":58894},{"style":629},[58895],{"type":259,"value":16416},{"type":254,"tag":622,"props":58897,"children":58898},{"class":624,"line":836},[58899],{"type":254,"tag":622,"props":58900,"children":58901},{"emptyLinePlaceholder":1699},[58902],{"type":259,"value":1702},{"type":254,"tag":622,"props":58904,"children":58905},{"class":624,"line":862},[58906,58911],{"type":254,"tag":622,"props":58907,"children":58908},{"style":768},[58909],{"type":259,"value":58910},"  winget",{"type":254,"tag":622,"props":58912,"children":58913},{"style":629},[58914],{"type":259,"value":16416},{"type":254,"tag":622,"props":58916,"children":58917},{"class":624,"line":900},[58918,58923,58927],{"type":254,"tag":622,"props":58919,"children":58920},{"style":768},[58921],{"type":259,"value":58922},"    name",{"type":254,"tag":622,"props":58924,"children":58925},{"style":629},[58926],{"type":259,"value":474},{"type":254,"tag":622,"props":58928,"children":58929},{"style":664},[58930],{"type":259,"value":58931}," Publish winget package\n",{"type":254,"tag":255,"props":58933,"children":58934},{},[58935],{"type":259,"value":58936},"I like how it is possible with GitHub Actions to trigger on many different GitHub events. It is something that seems more limited in Azure Pipelines.",{"type":254,"tag":455,"props":58938,"children":58940},{"id":58939},"calling-wingetcreate-from-a-github-actions-workflow",[58941,58943,58948],{"type":259,"value":58942},"Calling ",{"type":254,"tag":389,"props":58944,"children":58946},{"className":58945},[],[58947],{"type":259,"value":58744},{"type":259,"value":58949}," from a GitHub Actions workflow.",{"type":254,"tag":255,"props":58951,"children":58952},{},[58953],{"type":259,"value":58954},"Windows Package Manager Manifest Creator needs to be run in windows so we need to specify that in the job that will submit a new version of Nushell package to Windows Package Manager Community Repository:",{"type":254,"tag":612,"props":58956,"children":58958},{"className":51570,"code":58957,"language":233,"meta":205,"style":205},"jobs:\n\n  winget:\n    name: Publish winget package\n    runs-on: windows-latest\n",[58959],{"type":254,"tag":389,"props":58960,"children":58961},{"__ignoreMap":205},[58962,58973,58980,58991,59006],{"type":254,"tag":622,"props":58963,"children":58964},{"class":624,"line":625},[58965,58969],{"type":254,"tag":622,"props":58966,"children":58967},{"style":768},[58968],{"type":259,"value":16489},{"type":254,"tag":622,"props":58970,"children":58971},{"style":629},[58972],{"type":259,"value":16416},{"type":254,"tag":622,"props":58974,"children":58975},{"class":624,"line":635},[58976],{"type":254,"tag":622,"props":58977,"children":58978},{"emptyLinePlaceholder":1699},[58979],{"type":259,"value":1702},{"type":254,"tag":622,"props":58981,"children":58982},{"class":624,"line":679},[58983,58987],{"type":254,"tag":622,"props":58984,"children":58985},{"style":768},[58986],{"type":259,"value":58910},{"type":254,"tag":622,"props":58988,"children":58989},{"style":629},[58990],{"type":259,"value":16416},{"type":254,"tag":622,"props":58992,"children":58993},{"class":624,"line":705},[58994,58998,59002],{"type":254,"tag":622,"props":58995,"children":58996},{"style":768},[58997],{"type":259,"value":58922},{"type":254,"tag":622,"props":58999,"children":59000},{"style":629},[59001],{"type":259,"value":474},{"type":254,"tag":622,"props":59003,"children":59004},{"style":664},[59005],{"type":259,"value":58931},{"type":254,"tag":622,"props":59007,"children":59008},{"class":624,"line":732},[59009,59013,59017],{"type":254,"tag":622,"props":59010,"children":59011},{"style":768},[59012],{"type":259,"value":16513},{"type":254,"tag":622,"props":59014,"children":59015},{"style":629},[59016],{"type":259,"value":474},{"type":254,"tag":622,"props":59018,"children":59019},{"style":664},[59020],{"type":259,"value":51735},{"type":254,"tag":255,"props":59022,"children":59023},{},[59024,59026,59031,59033,59038],{"type":259,"value":59025},"This job will only contain one step that is the execution of the commands to call ",{"type":254,"tag":389,"props":59027,"children":59029},{"className":59028},[],[59030],{"type":259,"value":58744},{"type":259,"value":59032},". These commands will be in PowerShell as this is the default runner (",{"type":254,"tag":389,"props":59034,"children":59036},{"className":59035},[],[59037],{"type":259,"value":46544},{"type":259,"value":59039},") in a windows job.",{"type":254,"tag":612,"props":59041,"children":59043},{"className":51570,"code":59042,"language":233,"meta":205,"style":205},"  winget:\n    name: Publish winget package\n    runs-on: windows-latest\n    steps:\n      - name: Submit package to Windows Package Manager Community Repository\n        run: |\n\n",[59044],{"type":254,"tag":389,"props":59045,"children":59046},{"__ignoreMap":205},[59047,59058,59073,59088,59099,59119],{"type":254,"tag":622,"props":59048,"children":59049},{"class":624,"line":625},[59050,59054],{"type":254,"tag":622,"props":59051,"children":59052},{"style":768},[59053],{"type":259,"value":58910},{"type":254,"tag":622,"props":59055,"children":59056},{"style":629},[59057],{"type":259,"value":16416},{"type":254,"tag":622,"props":59059,"children":59060},{"class":624,"line":635},[59061,59065,59069],{"type":254,"tag":622,"props":59062,"children":59063},{"style":768},[59064],{"type":259,"value":58922},{"type":254,"tag":622,"props":59066,"children":59067},{"style":629},[59068],{"type":259,"value":474},{"type":254,"tag":622,"props":59070,"children":59071},{"style":664},[59072],{"type":259,"value":58931},{"type":254,"tag":622,"props":59074,"children":59075},{"class":624,"line":679},[59076,59080,59084],{"type":254,"tag":622,"props":59077,"children":59078},{"style":768},[59079],{"type":259,"value":16513},{"type":254,"tag":622,"props":59081,"children":59082},{"style":629},[59083],{"type":259,"value":474},{"type":254,"tag":622,"props":59085,"children":59086},{"style":664},[59087],{"type":259,"value":51735},{"type":254,"tag":622,"props":59089,"children":59090},{"class":624,"line":705},[59091,59095],{"type":254,"tag":622,"props":59092,"children":59093},{"style":768},[59094],{"type":259,"value":16530},{"type":254,"tag":622,"props":59096,"children":59097},{"style":629},[59098],{"type":259,"value":16416},{"type":254,"tag":622,"props":59100,"children":59101},{"class":624,"line":732},[59102,59106,59110,59114],{"type":254,"tag":622,"props":59103,"children":59104},{"style":629},[59105],{"type":259,"value":16542},{"type":254,"tag":622,"props":59107,"children":59108},{"style":768},[59109],{"type":259,"value":16547},{"type":254,"tag":622,"props":59111,"children":59112},{"style":629},[59113],{"type":259,"value":474},{"type":254,"tag":622,"props":59115,"children":59116},{"style":664},[59117],{"type":259,"value":59118}," Submit package to Windows Package Manager Community Repository\n",{"type":254,"tag":622,"props":59120,"children":59121},{"class":624,"line":759},[59122,59126,59130],{"type":254,"tag":622,"props":59123,"children":59124},{"style":768},[59125],{"type":259,"value":16689},{"type":254,"tag":622,"props":59127,"children":59128},{"style":629},[59129],{"type":259,"value":474},{"type":254,"tag":622,"props":59131,"children":59132},{"style":1196},[59133],{"type":259,"value":16698},{"type":254,"tag":255,"props":59135,"children":59136},{},[59137,59139,59144],{"type":259,"value":59138},"First, we need to download the latest version of ",{"type":254,"tag":389,"props":59140,"children":59142},{"className":59141},[],[59143],{"type":259,"value":58744},{"type":259,"value":59145}," by using the following command :",{"type":254,"tag":612,"props":59147,"children":59149},{"className":10656,"code":59148,"language":10658,"meta":205,"style":205},"iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe\n",[59150],{"type":254,"tag":389,"props":59151,"children":59152},{"__ignoreMap":205},[59153],{"type":254,"tag":622,"props":59154,"children":59155},{"class":624,"line":625},[59156,59161,59165,59170,59174,59178,59182,59187,59191,59196],{"type":254,"tag":622,"props":59157,"children":59158},{"style":1202},[59159],{"type":259,"value":59160},"iwr https:",{"type":254,"tag":622,"props":59162,"children":59163},{"style":629},[59164],{"type":259,"value":38393},{"type":254,"tag":622,"props":59166,"children":59167},{"style":1202},[59168],{"type":259,"value":59169},"aka.ms",{"type":254,"tag":622,"props":59171,"children":59172},{"style":629},[59173],{"type":259,"value":9768},{"type":254,"tag":622,"props":59175,"children":59176},{"style":1202},[59177],{"type":259,"value":58424},{"type":254,"tag":622,"props":59179,"children":59180},{"style":629},[59181],{"type":259,"value":9768},{"type":254,"tag":622,"props":59183,"children":59184},{"style":1202},[59185],{"type":259,"value":59186},"latest ",{"type":254,"tag":622,"props":59188,"children":59189},{"style":629},[59190],{"type":259,"value":17065},{"type":254,"tag":622,"props":59192,"children":59193},{"style":1202},[59194],{"type":259,"value":59195},"OutFile ",{"type":254,"tag":622,"props":59197,"children":59198},{"style":1735},[59199],{"type":259,"value":59200},"wingetcreate.exe\n",{"type":254,"tag":255,"props":59202,"children":59203},{},[59204],{"type":259,"value":59205},"Second, we want to retrieve the version number and the installer URL of the new package. These 2 pieces of information will be needed as parameters to the WingetCreate update command. We can find these in the GitHub context which contains the release event that triggered the workflow. We are using these 2 lines of PowerShell to get assets associated with the release and filter on the msi file which is the Windows installer of Nushell.",{"type":254,"tag":612,"props":59207,"children":59208},{"className":10656,"code":58194,"language":10658,"meta":205,"style":205},[59209],{"type":254,"tag":389,"props":59210,"children":59211},{"__ignoreMap":205},[59212,59251],{"type":254,"tag":622,"props":59213,"children":59214},{"class":624,"line":625},[59215,59219,59223,59227,59231,59235,59239,59243,59247],{"type":254,"tag":622,"props":59216,"children":59217},{"style":629},[59218],{"type":259,"value":16922},{"type":254,"tag":622,"props":59220,"children":59221},{"style":1202},[59222],{"type":259,"value":58210},{"type":254,"tag":622,"props":59224,"children":59225},{"style":629},[59226],{"type":259,"value":1654},{"type":254,"tag":622,"props":59228,"children":59229},{"style":1735},[59230],{"type":259,"value":58219},{"type":254,"tag":622,"props":59232,"children":59233},{"style":629},[59234],{"type":259,"value":16556},{"type":254,"tag":622,"props":59236,"children":59237},{"style":664},[59238],{"type":259,"value":58228},{"type":254,"tag":622,"props":59240,"children":59241},{"style":629},[59242],{"type":259,"value":46169},{"type":254,"tag":622,"props":59244,"children":59245},{"style":629},[59246],{"type":259,"value":41040},{"type":254,"tag":622,"props":59248,"children":59249},{"style":1735},[59250],{"type":259,"value":58241},{"type":254,"tag":622,"props":59252,"children":59253},{"class":624,"line":635},[59254,59258,59262,59266,59270,59274,59278,59282,59286,59290,59294,59298,59302,59306,59310,59314,59318,59322,59326,59330],{"type":254,"tag":622,"props":59255,"children":59256},{"style":629},[59257],{"type":259,"value":16922},{"type":254,"tag":622,"props":59259,"children":59260},{"style":1202},[59261],{"type":259,"value":58112},{"type":254,"tag":622,"props":59263,"children":59264},{"style":629},[59265],{"type":259,"value":1654},{"type":254,"tag":622,"props":59267,"children":59268},{"style":629},[59269],{"type":259,"value":39174},{"type":254,"tag":622,"props":59271,"children":59272},{"style":1202},[59273],{"type":259,"value":58265},{"type":254,"tag":622,"props":59275,"children":59276},{"style":629},[59277],{"type":259,"value":17875},{"type":254,"tag":622,"props":59279,"children":59280},{"style":1735},[59281],{"type":259,"value":58274},{"type":254,"tag":622,"props":59283,"children":59284},{"style":629},[59285],{"type":259,"value":10733},{"type":254,"tag":622,"props":59287,"children":59288},{"style":1202},[59289],{"type":259,"value":58283},{"type":254,"tag":622,"props":59291,"children":59292},{"style":629},[59293],{"type":259,"value":58288},{"type":254,"tag":622,"props":59295,"children":59296},{"style":629},[59297],{"type":259,"value":16556},{"type":254,"tag":622,"props":59299,"children":59300},{"style":664},[59301],{"type":259,"value":58297},{"type":254,"tag":622,"props":59303,"children":59304},{"style":629},[59305],{"type":259,"value":46169},{"type":254,"tag":622,"props":59307,"children":59308},{"style":629},[59309],{"type":259,"value":41040},{"type":254,"tag":622,"props":59311,"children":59312},{"style":1202},[59313],{"type":259,"value":58310},{"type":254,"tag":622,"props":59315,"children":59316},{"style":629},[59317],{"type":259,"value":17065},{"type":254,"tag":622,"props":59319,"children":59320},{"style":1202},[59321],{"type":259,"value":58319},{"type":254,"tag":622,"props":59323,"children":59324},{"style":629},[59325],{"type":259,"value":17065},{"type":254,"tag":622,"props":59327,"children":59328},{"style":1202},[59329],{"type":259,"value":58328},{"type":254,"tag":622,"props":59331,"children":59332},{"style":741},[59333],{"type":259,"value":58333},{"type":254,"tag":31352,"props":59335,"children":59336},{},[59337],{"type":254,"tag":255,"props":59338,"children":59339},{},[59340],{"type":259,"value":59341},"💡 I just thought that instead of doing this in PowerShell we could have done this in Nushell, which would have been fun 'using Nushell to provide a new version of Nushell' but as it is not installed by default on windows agents it would mean a loss of time each time the workflow runs.",{"type":254,"tag":255,"props":59343,"children":59344},{},[59345,59347,59352,59354,59359],{"type":259,"value":59346},"Third, we can call the ",{"type":254,"tag":389,"props":59348,"children":59350},{"className":59349},[],[59351],{"type":259,"value":58744},{"type":259,"value":59353}," update command by specifying the version, the URL of the installer, and a Personal Access Token that will be used by ",{"type":254,"tag":389,"props":59355,"children":59357},{"className":59356},[],[59358],{"type":259,"value":58744},{"type":259,"value":59360}," to make the Pull Request in the Windows Package Manager Community Repository. This PAT needs to be created by a maintainer of the repository with permission and added to the secrets of the project.",{"type":254,"tag":255,"props":59362,"children":59363},{},[59364],{"type":254,"tag":1161,"props":59365,"children":59369},{"alt":59366,"className":59367,"src":59368},"Screen to define Personal Access Tokens.",[1165,1166],"/posts/images/wingetcreate_pat.png",[],{"type":254,"tag":255,"props":59371,"children":59372},{},[59373],{"type":259,"value":59374},"Here you can see a run of the workflow in GitHub:",{"type":254,"tag":255,"props":59376,"children":59377},{},[59378],{"type":254,"tag":1161,"props":59379,"children":59383},{"alt":59380,"className":59381,"src":59382},"GitHub Actions workflow running.",[1165,1166],"/posts/images/wingetcreate_wokflow_1.png",[],{"type":254,"tag":282,"props":59385,"children":59387},{"id":59386},"overview-of-the-created-workflow",[59388],{"type":259,"value":59389},"Overview of the created workflow",{"type":254,"tag":255,"props":59391,"children":59392},{},[59393,59395,59401],{"type":259,"value":59394},"You can find the complete workflow below and ",{"type":254,"tag":262,"props":59396,"children":59399},{"href":59397,"rel":59398},"https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml",[266],[59400],{"type":259,"value":1414},{"type":259,"value":59402}," in the Nushell repository.",{"type":254,"tag":612,"props":59404,"children":59406},{"className":51570,"code":59405,"language":233,"meta":205,"style":205},"name: Submit Nushell package to Windows Package Manager Community Repository \n\non:\n  release:\n    types: [published]\n\njobs:\n\n  winget:\n    name: Publish winget package\n    runs-on: windows-latest\n    steps:\n      - name: Submit package to Windows Package Manager Community Repository\n        run: |\n          iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe\n          $github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json\n          $installerUrl = $github.release.assets | Where-Object -Property name -match 'windows.msi' | Select -ExpandProperty browser_download_url -First 1\n          .\\wingetcreate.exe update Nushell.Nushell -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}\n",[59407],{"type":254,"tag":389,"props":59408,"children":59409},{"__ignoreMap":205},[59410,59429,59436,59447,59458,59481,59488,59499,59506,59517,59532,59547,59558,59577,59592,59600,59608,59616],{"type":254,"tag":622,"props":59411,"children":59412},{"class":624,"line":625},[59413,59417,59421,59425],{"type":254,"tag":622,"props":59414,"children":59415},{"style":768},[59416],{"type":259,"value":16394},{"type":254,"tag":622,"props":59418,"children":59419},{"style":629},[59420],{"type":259,"value":474},{"type":254,"tag":622,"props":59422,"children":59423},{"style":664},[59424],{"type":259,"value":58818},{"type":254,"tag":622,"props":59426,"children":59427},{"style":1202},[59428],{"type":259,"value":2314},{"type":254,"tag":622,"props":59430,"children":59431},{"class":624,"line":635},[59432],{"type":254,"tag":622,"props":59433,"children":59434},{"emptyLinePlaceholder":1699},[59435],{"type":259,"value":1702},{"type":254,"tag":622,"props":59437,"children":59438},{"class":624,"line":679},[59439,59443],{"type":254,"tag":622,"props":59440,"children":59441},{"style":7715},[59442],{"type":259,"value":16411},{"type":254,"tag":622,"props":59444,"children":59445},{"style":629},[59446],{"type":259,"value":16416},{"type":254,"tag":622,"props":59448,"children":59449},{"class":624,"line":705},[59450,59454],{"type":254,"tag":622,"props":59451,"children":59452},{"style":768},[59453],{"type":259,"value":58848},{"type":254,"tag":622,"props":59455,"children":59456},{"style":629},[59457],{"type":259,"value":16416},{"type":254,"tag":622,"props":59459,"children":59460},{"class":624,"line":732},[59461,59465,59469,59473,59477],{"type":254,"tag":622,"props":59462,"children":59463},{"style":768},[59464],{"type":259,"value":58860},{"type":254,"tag":622,"props":59466,"children":59467},{"style":629},[59468],{"type":259,"value":474},{"type":254,"tag":622,"props":59470,"children":59471},{"style":629},[59472],{"type":259,"value":13676},{"type":254,"tag":622,"props":59474,"children":59475},{"style":664},[59476],{"type":259,"value":58873},{"type":254,"tag":622,"props":59478,"children":59479},{"style":629},[59480],{"type":259,"value":11003},{"type":254,"tag":622,"props":59482,"children":59483},{"class":624,"line":759},[59484],{"type":254,"tag":622,"props":59485,"children":59486},{"emptyLinePlaceholder":1699},[59487],{"type":259,"value":1702},{"type":254,"tag":622,"props":59489,"children":59490},{"class":624,"line":798},[59491,59495],{"type":254,"tag":622,"props":59492,"children":59493},{"style":768},[59494],{"type":259,"value":16489},{"type":254,"tag":622,"props":59496,"children":59497},{"style":629},[59498],{"type":259,"value":16416},{"type":254,"tag":622,"props":59500,"children":59501},{"class":624,"line":836},[59502],{"type":254,"tag":622,"props":59503,"children":59504},{"emptyLinePlaceholder":1699},[59505],{"type":259,"value":1702},{"type":254,"tag":622,"props":59507,"children":59508},{"class":624,"line":862},[59509,59513],{"type":254,"tag":622,"props":59510,"children":59511},{"style":768},[59512],{"type":259,"value":58910},{"type":254,"tag":622,"props":59514,"children":59515},{"style":629},[59516],{"type":259,"value":16416},{"type":254,"tag":622,"props":59518,"children":59519},{"class":624,"line":900},[59520,59524,59528],{"type":254,"tag":622,"props":59521,"children":59522},{"style":768},[59523],{"type":259,"value":58922},{"type":254,"tag":622,"props":59525,"children":59526},{"style":629},[59527],{"type":259,"value":474},{"type":254,"tag":622,"props":59529,"children":59530},{"style":664},[59531],{"type":259,"value":58931},{"type":254,"tag":622,"props":59533,"children":59534},{"class":624,"line":938},[59535,59539,59543],{"type":254,"tag":622,"props":59536,"children":59537},{"style":768},[59538],{"type":259,"value":16513},{"type":254,"tag":622,"props":59540,"children":59541},{"style":629},[59542],{"type":259,"value":474},{"type":254,"tag":622,"props":59544,"children":59545},{"style":664},[59546],{"type":259,"value":51735},{"type":254,"tag":622,"props":59548,"children":59549},{"class":624,"line":976},[59550,59554],{"type":254,"tag":622,"props":59551,"children":59552},{"style":768},[59553],{"type":259,"value":16530},{"type":254,"tag":622,"props":59555,"children":59556},{"style":629},[59557],{"type":259,"value":16416},{"type":254,"tag":622,"props":59559,"children":59560},{"class":624,"line":1014},[59561,59565,59569,59573],{"type":254,"tag":622,"props":59562,"children":59563},{"style":629},[59564],{"type":259,"value":16542},{"type":254,"tag":622,"props":59566,"children":59567},{"style":768},[59568],{"type":259,"value":16547},{"type":254,"tag":622,"props":59570,"children":59571},{"style":629},[59572],{"type":259,"value":474},{"type":254,"tag":622,"props":59574,"children":59575},{"style":664},[59576],{"type":259,"value":59118},{"type":254,"tag":622,"props":59578,"children":59579},{"class":624,"line":1052},[59580,59584,59588],{"type":254,"tag":622,"props":59581,"children":59582},{"style":768},[59583],{"type":259,"value":16689},{"type":254,"tag":622,"props":59585,"children":59586},{"style":629},[59587],{"type":259,"value":474},{"type":254,"tag":622,"props":59589,"children":59590},{"style":1196},[59591],{"type":259,"value":16698},{"type":254,"tag":622,"props":59593,"children":59594},{"class":624,"line":1078},[59595],{"type":254,"tag":622,"props":59596,"children":59597},{"style":664},[59598],{"type":259,"value":59599},"          iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe\n",{"type":254,"tag":622,"props":59601,"children":59602},{"class":624,"line":1087},[59603],{"type":254,"tag":622,"props":59604,"children":59605},{"style":664},[59606],{"type":259,"value":59607},"          $github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json\n",{"type":254,"tag":622,"props":59609,"children":59610},{"class":624,"line":1096},[59611],{"type":254,"tag":622,"props":59612,"children":59613},{"style":664},[59614],{"type":259,"value":59615},"          $installerUrl = $github.release.assets | Where-Object -Property name -match 'windows.msi' | Select -ExpandProperty browser_download_url -First 1\n",{"type":254,"tag":622,"props":59617,"children":59618},{"class":624,"line":1105},[59619],{"type":254,"tag":622,"props":59620,"children":59621},{"style":664},[59622],{"type":259,"value":59623},"          .\\wingetcreate.exe update Nushell.Nushell -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}\n",{"type":254,"tag":255,"props":59625,"children":59626},{},[59627],{"type":259,"value":59628},"Here is what a Pull Request generated by the GitHub Actions workflow looks like:",{"type":254,"tag":255,"props":59630,"children":59631},{},[59632],{"type":254,"tag":1161,"props":59633,"children":59637},{"alt":59634,"className":59635,"src":59636},"Pull Request in the winget-pkgs repository.",[1165,1166],"/posts/images/wingetcreate_pr.png",[],{"type":254,"tag":282,"props":59639,"children":59641},{"id":59640},"to-summarize",[59642],{"type":259,"value":59643},"To summarize",{"type":254,"tag":255,"props":59645,"children":59646},{},[59647],{"type":259,"value":59648},"We have introduced the notion of source for winget packages and in particular, the Windows Package Manager Community Repository where we can open PR to submit a new application or new versions of an existing application. We have seen how Windows Package Manager Manifest Creator could help us do that and how it could be automated from a GitHub Actions workflow like it was done for the Nushell project.",{"type":254,"tag":255,"props":59650,"children":59651},{},[59652],{"type":259,"value":59653},"Do not hesitate to copy some of the GitHub Actions workflows I showed you. I hope this will inspire you to do the same to distribute your applications through winget.",{"type":254,"tag":255,"props":59655,"children":59656},{},[59657,59659,59666,59668,59675,59676,59683],{"type":259,"value":59658},"A big thank you to ",{"type":254,"tag":262,"props":59660,"children":59663},{"href":59661,"rel":59662},"https://twitter.com/ethomson",[266],[59664],{"type":259,"value":59665},"Edward Thomson",{"type":259,"value":59667}," who explained to me how to retrieve GitHub Actions contexts in PowerShell. Thanks also to ",{"type":254,"tag":262,"props":59669,"children":59672},{"href":59670,"rel":59671},"https://twitter.com/fdncred",[266],[59673],{"type":259,"value":59674},"Darren Schroeder",{"type":259,"value":10347},{"type":254,"tag":262,"props":59677,"children":59680},{"href":59678,"rel":59679},"https://twitter.com/jntrnr",[266],[59681],{"type":259,"value":59682},"Jonathan Turner",{"type":259,"value":59684}," who supported me to set up a workflow that publishes new releases of Nushell in winget.",{"type":254,"tag":1260,"props":59686,"children":59687},{},[59688],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":59690},[59691,59692,59702,59709,59710],{"id":58443,"depth":635,"text":58446},{"id":58488,"depth":635,"text":58491,"children":59693},[59694,59695,59697,59699,59701],{"id":58494,"depth":679,"text":58497},{"id":58556,"depth":679,"text":59696},"The New command",{"id":58581,"depth":679,"text":59698},"The Update command",{"id":58605,"depth":679,"text":59700},"The Submit command",{"id":43135,"depth":679,"text":43138},{"id":58644,"depth":635,"text":58647,"children":59703},[59704,59705,59706,59707],{"id":58650,"depth":679,"text":58653},{"id":58675,"depth":679,"text":58678},{"id":58749,"depth":679,"text":58752},{"id":58939,"depth":679,"text":59708},"Calling WingetCreate from a GitHub Actions workflow.",{"id":59386,"depth":635,"text":59389},{"id":59640,"depth":635,"text":59643},"content:1.posts:21.wingetcreate.md","1.posts/21.wingetcreate.md",{"_path":67,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":66,"description":59714,"lead":59715,"date":59716,"image":59717,"badge":59719,"tags":59720,"body":59721,"_type":1275,"_id":62069,"_source":231,"_file":62070,"_extension":1278},"In Microsoft.Data.SqlClient v3.0.0, a new authentication mode Active Directory Default has been released. Let's see what this means when querying an Azure SQL Database from some C# code.","Talking about Active Directory Default authentication mode for SqlClient.","2021-06-22T00:00:00.000Z",{"src":59718},"/images/cloud-azure_1.jpg",{"label":1286},[23935,43854,6261,6260,16351,10627],{"type":251,"children":59722,"toc":62058},[59723,59742,59765,59770,59776,59796,59918,59929,59934,59957,59962,59968,59973,59978,59990,60002,60199,60237,60242,60255,60289,60367,60382,60388,60393,60399,60413,61378,61383,61418,61423,61437,61443,61457,61469,61949,61954,62012,62021,62037,62041,62054],{"type":254,"tag":255,"props":59724,"children":59725},{},[59726,59727,59732,59734,59740],{"type":259,"value":43871},{"type":254,"tag":389,"props":59728,"children":59730},{"className":59729},[],[59731],{"type":259,"value":43886},{"type":259,"value":59733}," v3.0.0, a new authentication mode ",{"type":254,"tag":389,"props":59735,"children":59737},{"className":59736},[],[59738],{"type":259,"value":59739},"Active Directory Default",{"type":259,"value":59741}," has been released. Let's see what this means when querying an Azure SQL Database from some C# code.",{"type":254,"tag":427,"props":59743,"children":59744},{"icon":429},[59745],{"type":254,"tag":255,"props":59746,"children":59747},{},[59748,59750,59755,59757,59763],{"type":259,"value":59749},"If you do not have heard about ",{"type":254,"tag":389,"props":59751,"children":59753},{"className":59752},[],[59754],{"type":259,"value":43886},{"type":259,"value":59756},", it is the new data provider for Microsoft SQL Server and Azure SQL Database which supports both .NET Framework and .NET Core and replace the old ",{"type":254,"tag":389,"props":59758,"children":59760},{"className":59759},[],[59761],{"type":259,"value":59762},"System.Data.SqlClient",{"type":259,"value":59764}," components.",{"type":254,"tag":255,"props":59766,"children":59767},{},[59768],{"type":259,"value":59769},"But first, let's talk about how we used to do that before.",{"type":254,"tag":282,"props":59771,"children":59773},{"id":59772},"the-traditional-way-using-a-secret-connection-string",[59774],{"type":259,"value":59775},"The traditional way: using a secret connection string",{"type":254,"tag":255,"props":59777,"children":59778},{},[59779,59780,59786,59788,59794],{"type":259,"value":10970},{"type":254,"tag":389,"props":59781,"children":59783},{"className":59782},[],[59784],{"type":259,"value":59785},"traditional way",{"type":259,"value":59787}," to connect to an Azure SQL database from an application in C# is to provide to the ",{"type":254,"tag":389,"props":59789,"children":59791},{"className":59790},[],[59792],{"type":259,"value":59793},"SqlConnection",{"type":259,"value":59795}," constructor a connection string that contains a username and a password. The corresponding C# code is quite simple:",{"type":254,"tag":612,"props":59797,"children":59799},{"className":1423,"code":59798,"language":1425,"meta":205,"style":205},"var connectionString = \"Server=server-testingmsi6499.database.windows.net; Database=database-testingmsi6499;User ID=globalSqlAdmin;Password=MySecretPassword;\");\n\nusing (var sqlConnection = new SqlConnection(connectionString));\nawait connection.OpenAsync();\n",[59800],{"type":254,"tag":389,"props":59801,"children":59802},{"__ignoreMap":205},[59803,59840,59847,59893],{"type":254,"tag":622,"props":59804,"children":59805},{"class":624,"line":625},[59806,59810,59815,59819,59823,59828,59832,59836],{"type":254,"tag":622,"props":59807,"children":59808},{"style":714},[59809],{"type":259,"value":1710},{"type":254,"tag":622,"props":59811,"children":59812},{"style":714},[59813],{"type":259,"value":59814}," connectionString",{"type":254,"tag":622,"props":59816,"children":59817},{"style":629},[59818],{"type":259,"value":1619},{"type":254,"tag":622,"props":59820,"children":59821},{"style":629},[59822],{"type":259,"value":661},{"type":254,"tag":622,"props":59824,"children":59825},{"style":664},[59826],{"type":259,"value":59827},"Server=server-testingmsi6499.database.windows.net; Database=database-testingmsi6499;User ID=globalSqlAdmin;Password=MySecretPassword;",{"type":254,"tag":622,"props":59829,"children":59830},{"style":629},[59831],{"type":259,"value":652},{"type":254,"tag":622,"props":59833,"children":59834},{"style":1202},[59835],{"type":259,"value":2309},{"type":254,"tag":622,"props":59837,"children":59838},{"style":629},[59839],{"type":259,"value":2350},{"type":254,"tag":622,"props":59841,"children":59842},{"class":624,"line":635},[59843],{"type":254,"tag":622,"props":59844,"children":59845},{"emptyLinePlaceholder":1699},[59846],{"type":259,"value":1702},{"type":254,"tag":622,"props":59848,"children":59849},{"class":624,"line":679},[59850,59854,59858,59862,59867,59871,59875,59880,59884,59889],{"type":254,"tag":622,"props":59851,"children":59852},{"style":1196},[59853],{"type":259,"value":1604},{"type":254,"tag":622,"props":59855,"children":59856},{"style":629},[59857],{"type":259,"value":3547},{"type":254,"tag":622,"props":59859,"children":59860},{"style":714},[59861],{"type":259,"value":1710},{"type":254,"tag":622,"props":59863,"children":59864},{"style":714},[59865],{"type":259,"value":59866}," sqlConnection",{"type":254,"tag":622,"props":59868,"children":59869},{"style":629},[59870],{"type":259,"value":1619},{"type":254,"tag":622,"props":59872,"children":59873},{"style":629},[59874],{"type":259,"value":1624},{"type":254,"tag":622,"props":59876,"children":59877},{"style":714},[59878],{"type":259,"value":59879}," SqlConnection",{"type":254,"tag":622,"props":59881,"children":59882},{"style":629},[59883],{"type":259,"value":1668},{"type":254,"tag":622,"props":59885,"children":59886},{"style":1202},[59887],{"type":259,"value":59888},"connectionString",{"type":254,"tag":622,"props":59890,"children":59891},{"style":629},[59892],{"type":259,"value":6624},{"type":254,"tag":622,"props":59894,"children":59895},{"class":624,"line":705},[59896,59901,59906,59910,59914],{"type":254,"tag":622,"props":59897,"children":59898},{"style":629},[59899],{"type":259,"value":59900},"await",{"type":254,"tag":622,"props":59902,"children":59903},{"style":1202},[59904],{"type":259,"value":59905}," connection",{"type":254,"tag":622,"props":59907,"children":59908},{"style":629},[59909],{"type":259,"value":594},{"type":254,"tag":622,"props":59911,"children":59912},{"style":1735},[59913],{"type":259,"value":2979},{"type":254,"tag":622,"props":59915,"children":59916},{"style":629},[59917],{"type":259,"value":2798},{"type":254,"tag":255,"props":59919,"children":59920},{},[59921,59923,59928],{"type":259,"value":59922},"In that case, the connection string is a secret we must secure and not just by putting it in some configuration location everyone can have access to, but by storing it in a secured place like ",{"type":254,"tag":389,"props":59924,"children":59926},{"className":59925},[],[59927],{"type":259,"value":40204},{"type":259,"value":594},{"type":254,"tag":255,"props":59930,"children":59931},{},[59932],{"type":259,"value":59933},"However, even if you secure it appropriately, using a connection string with a username/password in it has some disadvantages:",{"type":254,"tag":332,"props":59935,"children":59936},{},[59937,59942,59947,59952],{"type":254,"tag":336,"props":59938,"children":59939},{},[59940],{"type":259,"value":59941},"you need to handle who has access to it (so who has access to the key vault)",{"type":254,"tag":336,"props":59943,"children":59944},{},[59945],{"type":259,"value":59946},"every application or every developer could potentially use the same connection string so auditing is not very convenient (for instance identifying in the database logs which user has run a specific transaction)",{"type":254,"tag":336,"props":59948,"children":59949},{},[59950],{"type":259,"value":59951},"you only control who has access to the connection string in the key vault, not what people do with it (share it by email, store it on their local computer, ...) so not who can access the database",{"type":254,"tag":336,"props":59953,"children":59954},{},[59955],{"type":259,"value":59956},"you need to handle the rotation of the secret, in other words, change the username/password regularly (because you can revoke the access to someone to the database, if he had access to the connection string at some point in time it is not a secret for him anymore)",{"type":254,"tag":255,"props":59958,"children":59959},{},[59960],{"type":259,"value":59961},"For all these reasons, using a secret connection string to connect to an Azure SQL Database is not the right approach.",{"type":254,"tag":282,"props":59963,"children":59965},{"id":59964},"the-new-way-using-azure-active-directory-authentication",[59966],{"type":259,"value":59967},"The new way: using Azure Active Directory Authentication",{"type":254,"tag":255,"props":59969,"children":59970},{},[59971],{"type":259,"value":59972},"Instead of using a secret connection string to connect to a database, the idea is to use the Azure Active Directory authentication mechanism. Azure Active Directory is the location that contains all the identities of your users and your applications in your company. So you can manage directly which identity (user or application) have access to a database.",{"type":254,"tag":255,"props":59974,"children":59975},{},[59976],{"type":259,"value":59977},"Applications or users that want to query a database will authenticate against Azure AD to retrieve an access token that will allow them to access the database using a connection string without any username nor password.",{"type":254,"tag":255,"props":59979,"children":59980},{},[59981,59983,59989],{"type":259,"value":59982},"If you want to know more about the advantages of using Azure AD authentication for connecting to an Azure SQL Database you can have a look in the ",{"type":254,"tag":262,"props":59984,"children":59987},{"href":59985,"rel":59986},"https://docs.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-overview",[266],[59988],{"type":259,"value":47276},{"type":259,"value":594},{"type":254,"tag":255,"props":59991,"children":59992},{},[59993,59995,60000],{"type":259,"value":59994},"In the code we can remove the user id and password from the connection string but we have to retrieve an Azure AD access token and pass it to the ",{"type":254,"tag":389,"props":59996,"children":59998},{"className":59997},[],[59999],{"type":259,"value":59793},{"type":259,"value":60001}," instance:",{"type":254,"tag":612,"props":60003,"children":60005},{"className":1423,"code":60004,"language":1425,"meta":205,"style":205},"var accessToken = await new DefaultAzureCredential().GetTokenAsync(new TokenRequestContext(new string[] { \"https://database.windows.net//.default\" }));\nusing var connection = new SqlConnection(\"Server=server-testingmsi6499.database.windows.net; Database=database-testingmsi6499;\")\n{\n    AccessToken = accessToken.Token\n};\nawait connection.OpenAsync();\n",[60006],{"type":254,"tag":389,"props":60007,"children":60008},{"__ignoreMap":205},[60009,60089,60137,60144,60169,60176],{"type":254,"tag":622,"props":60010,"children":60011},{"class":624,"line":625},[60012,60016,60021,60025,60029,60033,60037,60041,60046,60050,60055,60059,60063,60067,60071,60075,60080,60084],{"type":254,"tag":622,"props":60013,"children":60014},{"style":714},[60015],{"type":259,"value":1710},{"type":254,"tag":622,"props":60017,"children":60018},{"style":714},[60019],{"type":259,"value":60020}," accessToken",{"type":254,"tag":622,"props":60022,"children":60023},{"style":629},[60024],{"type":259,"value":1619},{"type":254,"tag":622,"props":60026,"children":60027},{"style":629},[60028],{"type":259,"value":1724},{"type":254,"tag":622,"props":60030,"children":60031},{"style":629},[60032],{"type":259,"value":1624},{"type":254,"tag":622,"props":60034,"children":60035},{"style":714},[60036],{"type":259,"value":6649},{"type":254,"tag":622,"props":60038,"children":60039},{"style":629},[60040],{"type":259,"value":45460},{"type":254,"tag":622,"props":60042,"children":60043},{"style":1735},[60044],{"type":259,"value":60045},"GetTokenAsync",{"type":254,"tag":622,"props":60047,"children":60048},{"style":629},[60049],{"type":259,"value":6598},{"type":254,"tag":622,"props":60051,"children":60052},{"style":714},[60053],{"type":259,"value":60054}," TokenRequestContext",{"type":254,"tag":622,"props":60056,"children":60057},{"style":629},[60058],{"type":259,"value":6598},{"type":254,"tag":622,"props":60060,"children":60061},{"style":629},[60062],{"type":259,"value":1975},{"type":254,"tag":622,"props":60064,"children":60065},{"style":629},[60066],{"type":259,"value":13436},{"type":254,"tag":622,"props":60068,"children":60069},{"style":629},[60070],{"type":259,"value":1990},{"type":254,"tag":622,"props":60072,"children":60073},{"style":629},[60074],{"type":259,"value":661},{"type":254,"tag":622,"props":60076,"children":60077},{"style":664},[60078],{"type":259,"value":60079},"https://database.windows.net//.default",{"type":254,"tag":622,"props":60081,"children":60082},{"style":629},[60083],{"type":259,"value":652},{"type":254,"tag":622,"props":60085,"children":60086},{"style":629},[60087],{"type":259,"value":60088}," }));\n",{"type":254,"tag":622,"props":60090,"children":60091},{"class":624,"line":635},[60092,60096,60100,60104,60108,60112,60116,60120,60124,60129,60133],{"type":254,"tag":622,"props":60093,"children":60094},{"style":1196},[60095],{"type":259,"value":1604},{"type":254,"tag":622,"props":60097,"children":60098},{"style":714},[60099],{"type":259,"value":1609},{"type":254,"tag":622,"props":60101,"children":60102},{"style":714},[60103],{"type":259,"value":59905},{"type":254,"tag":622,"props":60105,"children":60106},{"style":629},[60107],{"type":259,"value":1619},{"type":254,"tag":622,"props":60109,"children":60110},{"style":629},[60111],{"type":259,"value":1624},{"type":254,"tag":622,"props":60113,"children":60114},{"style":714},[60115],{"type":259,"value":59879},{"type":254,"tag":622,"props":60117,"children":60118},{"style":629},[60119],{"type":259,"value":1668},{"type":254,"tag":622,"props":60121,"children":60122},{"style":629},[60123],{"type":259,"value":652},{"type":254,"tag":622,"props":60125,"children":60126},{"style":664},[60127],{"type":259,"value":60128},"Server=server-testingmsi6499.database.windows.net; Database=database-testingmsi6499;",{"type":254,"tag":622,"props":60130,"children":60131},{"style":629},[60132],{"type":259,"value":652},{"type":254,"tag":622,"props":60134,"children":60135},{"style":629},[60136],{"type":259,"value":1685},{"type":254,"tag":622,"props":60138,"children":60139},{"class":624,"line":679},[60140],{"type":254,"tag":622,"props":60141,"children":60142},{"style":629},[60143],{"type":259,"value":632},{"type":254,"tag":622,"props":60145,"children":60146},{"class":624,"line":705},[60147,60152,60156,60160,60164],{"type":254,"tag":622,"props":60148,"children":60149},{"style":1202},[60150],{"type":259,"value":60151},"    AccessToken ",{"type":254,"tag":622,"props":60153,"children":60154},{"style":629},[60155],{"type":259,"value":1654},{"type":254,"tag":622,"props":60157,"children":60158},{"style":1202},[60159],{"type":259,"value":60020},{"type":254,"tag":622,"props":60161,"children":60162},{"style":629},[60163],{"type":259,"value":594},{"type":254,"tag":622,"props":60165,"children":60166},{"style":1202},[60167],{"type":259,"value":60168},"Token\n",{"type":254,"tag":622,"props":60170,"children":60171},{"class":624,"line":732},[60172],{"type":254,"tag":622,"props":60173,"children":60174},{"style":629},[60175],{"type":259,"value":1693},{"type":254,"tag":622,"props":60177,"children":60178},{"class":624,"line":759},[60179,60183,60187,60191,60195],{"type":254,"tag":622,"props":60180,"children":60181},{"style":629},[60182],{"type":259,"value":59900},{"type":254,"tag":622,"props":60184,"children":60185},{"style":1202},[60186],{"type":259,"value":59905},{"type":254,"tag":622,"props":60188,"children":60189},{"style":629},[60190],{"type":259,"value":594},{"type":254,"tag":622,"props":60192,"children":60193},{"style":1735},[60194],{"type":259,"value":2979},{"type":254,"tag":622,"props":60196,"children":60197},{"style":629},[60198],{"type":259,"value":2798},{"type":254,"tag":255,"props":60200,"children":60201},{},[60202,60204,60211,60213,60218,60220,60227,60229,60235],{"type":259,"value":60203},"The code is using the ",{"type":254,"tag":262,"props":60205,"children":60208},{"href":60206,"rel":60207},"https://docs.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme",[266],[60209],{"type":259,"value":60210},"Azure Identity library",{"type":259,"value":60212}," which as the documentation says \"",{"type":254,"tag":19837,"props":60214,"children":60215},{},[60216],{"type":259,"value":60217},"provides Azure Active Directory token authentication support across the Azure SDK",{"type":259,"value":60219},"\". It is the recommended way to get an Azure token although you may have seen code that uses another library ",{"type":254,"tag":262,"props":60221,"children":60224},{"href":60222,"rel":60223},"https://www.nuget.org/packages/Microsoft.Azure.Services.AppAuthentication",[266],[60225],{"type":259,"value":60226},"Microsoft.Azure.Services.AppAuthentication",{"type":259,"value":60228}," to do the same thing. The class ",{"type":254,"tag":389,"props":60230,"children":60232},{"className":60231},[],[60233],{"type":259,"value":60234},"DefaultAzureCredential",{"type":259,"value":60236}," from Azure Identity library combines multiple authentication mechanisms (like Managed Identities, Visual Studio, Azure CLI ...) that will be tried in order to retrieve a token so it is a practical class that can handle most of the scenarios.",{"type":254,"tag":255,"props":60238,"children":60239},{},[60240],{"type":259,"value":60241},"Therefore, provided that you have granted access to your database to the user you are using locally (in Visual Studio, in vs code, or in Azure CLI) and to the managed identity of your application in Azure (App Service or Azure Function for instance) the same code will work both locally and in Azure.",{"type":254,"tag":282,"props":60243,"children":60245},{"id":60244},"here-comes-active-directory-default-authentication-mode",[60246,60248,60253],{"type":259,"value":60247},"Here comes ",{"type":254,"tag":389,"props":60249,"children":60251},{"className":60250},[],[60252],{"type":259,"value":59739},{"type":259,"value":60254}," authentication mode",{"type":254,"tag":255,"props":60256,"children":60257},{},[60258,60260,60265,60267,60272,60274,60280,60282,60287],{"type":259,"value":60259},"We have seen that using Azure Active Directory Authentication was a better solution than using a connection string with secrets in it to connect to a database. However, it involves manually retrieving an Azure AD token which makes the code a bit more complex to read. That is exactly why ",{"type":254,"tag":389,"props":60261,"children":60263},{"className":60262},[],[60264],{"type":259,"value":59739},{"type":259,"value":60266}," new authentication mode was introduced in ",{"type":254,"tag":389,"props":60268,"children":60270},{"className":60269},[],[60271],{"type":259,"value":43886},{"type":259,"value":60273}," v3.0.0. Under the hood, ",{"type":254,"tag":389,"props":60275,"children":60277},{"className":60276},[],[60278],{"type":259,"value":60279},"SqlClient",{"type":259,"value":60281}," does the same thing that we were showing previously so we don't have to do it ourselves: just specifying the authentication mode to ",{"type":254,"tag":389,"props":60283,"children":60285},{"className":60284},[],[60286],{"type":259,"value":59739},{"type":259,"value":60288}," in the connection string is enough to make it work.",{"type":254,"tag":612,"props":60290,"children":60292},{"className":1423,"code":60291,"language":1425,"meta":205,"style":205},"using var connection = new SqlConnection(\"Server=server-testingmsi6499.database.windows.net; Authentication=Active Directory Default; Database=database-testingmsi6499;\");\nawait connection.OpenAsync();\n",[60293],{"type":254,"tag":389,"props":60294,"children":60295},{"__ignoreMap":205},[60296,60344],{"type":254,"tag":622,"props":60297,"children":60298},{"class":624,"line":625},[60299,60303,60307,60311,60315,60319,60323,60327,60331,60336,60340],{"type":254,"tag":622,"props":60300,"children":60301},{"style":1196},[60302],{"type":259,"value":1604},{"type":254,"tag":622,"props":60304,"children":60305},{"style":714},[60306],{"type":259,"value":1609},{"type":254,"tag":622,"props":60308,"children":60309},{"style":714},[60310],{"type":259,"value":59905},{"type":254,"tag":622,"props":60312,"children":60313},{"style":629},[60314],{"type":259,"value":1619},{"type":254,"tag":622,"props":60316,"children":60317},{"style":629},[60318],{"type":259,"value":1624},{"type":254,"tag":622,"props":60320,"children":60321},{"style":714},[60322],{"type":259,"value":59879},{"type":254,"tag":622,"props":60324,"children":60325},{"style":629},[60326],{"type":259,"value":1668},{"type":254,"tag":622,"props":60328,"children":60329},{"style":629},[60330],{"type":259,"value":652},{"type":254,"tag":622,"props":60332,"children":60333},{"style":664},[60334],{"type":259,"value":60335},"Server=server-testingmsi6499.database.windows.net; Authentication=Active Directory Default; Database=database-testingmsi6499;",{"type":254,"tag":622,"props":60337,"children":60338},{"style":629},[60339],{"type":259,"value":652},{"type":254,"tag":622,"props":60341,"children":60342},{"style":629},[60343],{"type":259,"value":1554},{"type":254,"tag":622,"props":60345,"children":60346},{"class":624,"line":635},[60347,60351,60355,60359,60363],{"type":254,"tag":622,"props":60348,"children":60349},{"style":629},[60350],{"type":259,"value":59900},{"type":254,"tag":622,"props":60352,"children":60353},{"style":1202},[60354],{"type":259,"value":59905},{"type":254,"tag":622,"props":60356,"children":60357},{"style":629},[60358],{"type":259,"value":594},{"type":254,"tag":622,"props":60360,"children":60361},{"style":1735},[60362],{"type":259,"value":2979},{"type":254,"tag":622,"props":60364,"children":60365},{"style":629},[60366],{"type":259,"value":2798},{"type":254,"tag":427,"props":60368,"children":60369},{"icon":429},[60370],{"type":254,"tag":255,"props":60371,"children":60372},{},[60373,60375,60381],{"type":259,"value":60374},"There are other Azure Active Directory authentication methods available, you can find them in the documentation ",{"type":254,"tag":262,"props":60376,"children":60379},{"href":60377,"rel":60378},"https://docs.microsoft.com/en-us/sql/connect/ado-net/sql/azure-active-directory-authentication?view=sql-server-ver15#using-active-directory-password-authentication",[266],[60380],{"type":259,"value":1414},{"type":259,"value":594},{"type":254,"tag":282,"props":60383,"children":60385},{"id":60384},"a-complete-example",[60386],{"type":259,"value":60387},"A complete example",{"type":254,"tag":255,"props":60389,"children":60390},{},[60391],{"type":259,"value":60392},"Enough theory, what if you want to quickly test this by yourself?",{"type":254,"tag":455,"props":60394,"children":60396},{"id":60395},"a-bit-of-azure-cli-to-initialize-the-database",[60397],{"type":259,"value":60398},"A bit of Azure CLI to initialize the database",{"type":254,"tag":255,"props":60400,"children":60401},{},[60402,60404,60411],{"type":259,"value":60403},"I took an ",{"type":254,"tag":262,"props":60405,"children":60408},{"href":60406,"rel":60407},"https://docs.microsoft.com/en-us/azure/azure-sql/database/scripts/create-and-configure-database-cli",[266],[60409],{"type":259,"value":60410},"Azure CLI sample script",{"type":259,"value":60412}," from Microsoft and modify it a little to configure a database with all that is necessary to use Azure Active Directory to connect my user to it.",{"type":254,"tag":612,"props":60414,"children":60416},{"className":16836,"code":60415,"language":16838,"meta":205,"style":205},"#!/bin/bash\nlocation=\"West Europe\" # to change with your preferred location\nrandomIdentifier=testingmsi${RANDOM:0:5}\n\nresourceGroup=\"resource-$randomIdentifier\"\nserver=\"server-$randomIdentifier\"\ndatabase=\"database-$randomIdentifier\"\n\nlogin=\"globalSqlAdmin\"\npassword=\"P@ssw0rdToChange!\" # to change to have a more secured password\n\n# Retrieve your public IP.\n# Replace by your local machine IP if you are executing this script from cloud shell.\nstartIP=$(dig +short myip.opendns.com @resolver1.opendns.com)\nendIP=$startIP\n\n# Retrieve your current logged-in user to be used as SQL server admin. \n# Change with another user id if you want another user to be an admin.\nazureaduser=$(az ad signed-in-user show --query \"objectId\" -o tsv)\n\necho \"Creating $resourceGroup...\"\naz group create --name $resourceGroup --location \"$location\"\n\necho \"Creating $server in $location...\"\naz sql server create --name $server --resource-group $resourceGroup --location \"$location\" --admin-user $login --admin-password $password\n\necho \"Configuring firewall...\"\naz sql server firewall-rule create --resource-group $resourceGroup --server $server -n AllowYourIp --start-ip-address $startIP --end-ip-address $endIP\n\necho \"Creating $database on $server...\"\naz sql db create --resource-group $resourceGroup --server $server --name $database --sample-name AdventureWorksLT --service-objective Basic --zone-redundant false\n\necho \"Creating AD admin in sql server...\"\naz sql server ad-admin create --resource-group $resourceGroup --server-name $server --display-name ADMIN --object-id $azureaduser\n\necho \"Database connection string to use: \\\"Server=$server.database.windows.net; Authentication=Active Directory Default; Database=$database;\\\"\"\n",[60417],{"type":254,"tag":389,"props":60418,"children":60419},{"__ignoreMap":205},[60420,60428,60456,60503,60510,60539,60568,60596,60603,60628,60658,60665,60673,60681,60717,60734,60741,60749,60757,60816,60823,60854,60897,60904,60941,61016,61023,61043,61114,61121,61157,61233,61240,61260,61321,61328],{"type":254,"tag":622,"props":60421,"children":60422},{"class":624,"line":625},[60423],{"type":254,"tag":622,"props":60424,"children":60425},{"style":3195},[60426],{"type":259,"value":60427},"#!/bin/bash\n",{"type":254,"tag":622,"props":60429,"children":60430},{"class":624,"line":635},[60431,60435,60439,60443,60447,60451],{"type":254,"tag":622,"props":60432,"children":60433},{"style":1202},[60434],{"type":259,"value":48283},{"type":254,"tag":622,"props":60436,"children":60437},{"style":629},[60438],{"type":259,"value":1654},{"type":254,"tag":622,"props":60440,"children":60441},{"style":629},[60442],{"type":259,"value":652},{"type":254,"tag":622,"props":60444,"children":60445},{"style":664},[60446],{"type":259,"value":48296},{"type":254,"tag":622,"props":60448,"children":60449},{"style":629},[60450],{"type":259,"value":652},{"type":254,"tag":622,"props":60452,"children":60453},{"style":3195},[60454],{"type":259,"value":60455}," # to change with your preferred location\n",{"type":254,"tag":622,"props":60457,"children":60458},{"class":624,"line":679},[60459,60464,60468,60473,60477,60482,60486,60490,60494,60499],{"type":254,"tag":622,"props":60460,"children":60461},{"style":1202},[60462],{"type":259,"value":60463},"randomIdentifier",{"type":254,"tag":622,"props":60465,"children":60466},{"style":629},[60467],{"type":259,"value":1654},{"type":254,"tag":622,"props":60469,"children":60470},{"style":664},[60471],{"type":259,"value":60472},"testingmsi",{"type":254,"tag":622,"props":60474,"children":60475},{"style":629},[60476],{"type":259,"value":17340},{"type":254,"tag":622,"props":60478,"children":60479},{"style":1202},[60480],{"type":259,"value":60481},"RANDOM",{"type":254,"tag":622,"props":60483,"children":60484},{"style":629},[60485],{"type":259,"value":474},{"type":254,"tag":622,"props":60487,"children":60488},{"style":1202},[60489],{"type":259,"value":53613},{"type":254,"tag":622,"props":60491,"children":60492},{"style":629},[60493],{"type":259,"value":474},{"type":254,"tag":622,"props":60495,"children":60496},{"style":1202},[60497],{"type":259,"value":60498},"5",{"type":254,"tag":622,"props":60500,"children":60501},{"style":629},[60502],{"type":259,"value":1111},{"type":254,"tag":622,"props":60504,"children":60505},{"class":624,"line":705},[60506],{"type":254,"tag":622,"props":60507,"children":60508},{"emptyLinePlaceholder":1699},[60509],{"type":259,"value":1702},{"type":254,"tag":622,"props":60511,"children":60512},{"class":624,"line":732},[60513,60517,60521,60525,60530,60535],{"type":254,"tag":622,"props":60514,"children":60515},{"style":1202},[60516],{"type":259,"value":56215},{"type":254,"tag":622,"props":60518,"children":60519},{"style":629},[60520],{"type":259,"value":1654},{"type":254,"tag":622,"props":60522,"children":60523},{"style":629},[60524],{"type":259,"value":652},{"type":254,"tag":622,"props":60526,"children":60527},{"style":664},[60528],{"type":259,"value":60529},"resource-",{"type":254,"tag":622,"props":60531,"children":60532},{"style":1202},[60533],{"type":259,"value":60534},"$randomIdentifier",{"type":254,"tag":622,"props":60536,"children":60537},{"style":629},[60538],{"type":259,"value":9909},{"type":254,"tag":622,"props":60540,"children":60541},{"class":624,"line":759},[60542,60547,60551,60555,60560,60564],{"type":254,"tag":622,"props":60543,"children":60544},{"style":1202},[60545],{"type":259,"value":60546},"server",{"type":254,"tag":622,"props":60548,"children":60549},{"style":629},[60550],{"type":259,"value":1654},{"type":254,"tag":622,"props":60552,"children":60553},{"style":629},[60554],{"type":259,"value":652},{"type":254,"tag":622,"props":60556,"children":60557},{"style":664},[60558],{"type":259,"value":60559},"server-",{"type":254,"tag":622,"props":60561,"children":60562},{"style":1202},[60563],{"type":259,"value":60534},{"type":254,"tag":622,"props":60565,"children":60566},{"style":629},[60567],{"type":259,"value":9909},{"type":254,"tag":622,"props":60569,"children":60570},{"class":624,"line":798},[60571,60575,60579,60583,60588,60592],{"type":254,"tag":622,"props":60572,"children":60573},{"style":1202},[60574],{"type":259,"value":45085},{"type":254,"tag":622,"props":60576,"children":60577},{"style":629},[60578],{"type":259,"value":1654},{"type":254,"tag":622,"props":60580,"children":60581},{"style":629},[60582],{"type":259,"value":652},{"type":254,"tag":622,"props":60584,"children":60585},{"style":664},[60586],{"type":259,"value":60587},"database-",{"type":254,"tag":622,"props":60589,"children":60590},{"style":1202},[60591],{"type":259,"value":60534},{"type":254,"tag":622,"props":60593,"children":60594},{"style":629},[60595],{"type":259,"value":9909},{"type":254,"tag":622,"props":60597,"children":60598},{"class":624,"line":836},[60599],{"type":254,"tag":622,"props":60600,"children":60601},{"emptyLinePlaceholder":1699},[60602],{"type":259,"value":1702},{"type":254,"tag":622,"props":60604,"children":60605},{"class":624,"line":862},[60606,60611,60615,60619,60624],{"type":254,"tag":622,"props":60607,"children":60608},{"style":1202},[60609],{"type":259,"value":60610},"login",{"type":254,"tag":622,"props":60612,"children":60613},{"style":629},[60614],{"type":259,"value":1654},{"type":254,"tag":622,"props":60616,"children":60617},{"style":629},[60618],{"type":259,"value":652},{"type":254,"tag":622,"props":60620,"children":60621},{"style":664},[60622],{"type":259,"value":60623},"globalSqlAdmin",{"type":254,"tag":622,"props":60625,"children":60626},{"style":629},[60627],{"type":259,"value":9909},{"type":254,"tag":622,"props":60629,"children":60630},{"class":624,"line":900},[60631,60636,60640,60644,60649,60653],{"type":254,"tag":622,"props":60632,"children":60633},{"style":1202},[60634],{"type":259,"value":60635},"password",{"type":254,"tag":622,"props":60637,"children":60638},{"style":629},[60639],{"type":259,"value":1654},{"type":254,"tag":622,"props":60641,"children":60642},{"style":629},[60643],{"type":259,"value":652},{"type":254,"tag":622,"props":60645,"children":60646},{"style":664},[60647],{"type":259,"value":60648},"P@ssw0rdToChange!",{"type":254,"tag":622,"props":60650,"children":60651},{"style":629},[60652],{"type":259,"value":652},{"type":254,"tag":622,"props":60654,"children":60655},{"style":3195},[60656],{"type":259,"value":60657}," # to change to have a more secured password\n",{"type":254,"tag":622,"props":60659,"children":60660},{"class":624,"line":938},[60661],{"type":254,"tag":622,"props":60662,"children":60663},{"emptyLinePlaceholder":1699},[60664],{"type":259,"value":1702},{"type":254,"tag":622,"props":60666,"children":60667},{"class":624,"line":976},[60668],{"type":254,"tag":622,"props":60669,"children":60670},{"style":3195},[60671],{"type":259,"value":60672},"# Retrieve your public IP.\n",{"type":254,"tag":622,"props":60674,"children":60675},{"class":624,"line":1014},[60676],{"type":254,"tag":622,"props":60677,"children":60678},{"style":3195},[60679],{"type":259,"value":60680},"# Replace by your local machine IP if you are executing this script from cloud shell.\n",{"type":254,"tag":622,"props":60682,"children":60683},{"class":624,"line":1052},[60684,60689,60693,60698,60703,60708,60713],{"type":254,"tag":622,"props":60685,"children":60686},{"style":1202},[60687],{"type":259,"value":60688},"startIP",{"type":254,"tag":622,"props":60690,"children":60691},{"style":629},[60692],{"type":259,"value":17046},{"type":254,"tag":622,"props":60694,"children":60695},{"style":714},[60696],{"type":259,"value":60697},"dig",{"type":254,"tag":622,"props":60699,"children":60700},{"style":664},[60701],{"type":259,"value":60702}," +short",{"type":254,"tag":622,"props":60704,"children":60705},{"style":664},[60706],{"type":259,"value":60707}," myip.opendns.com",{"type":254,"tag":622,"props":60709,"children":60710},{"style":664},[60711],{"type":259,"value":60712}," @resolver1.opendns.com",{"type":254,"tag":622,"props":60714,"children":60715},{"style":629},[60716],{"type":259,"value":1685},{"type":254,"tag":622,"props":60718,"children":60719},{"class":624,"line":1078},[60720,60725,60729],{"type":254,"tag":622,"props":60721,"children":60722},{"style":1202},[60723],{"type":259,"value":60724},"endIP",{"type":254,"tag":622,"props":60726,"children":60727},{"style":629},[60728],{"type":259,"value":1654},{"type":254,"tag":622,"props":60730,"children":60731},{"style":1202},[60732],{"type":259,"value":60733},"$startIP\n",{"type":254,"tag":622,"props":60735,"children":60736},{"class":624,"line":1087},[60737],{"type":254,"tag":622,"props":60738,"children":60739},{"emptyLinePlaceholder":1699},[60740],{"type":259,"value":1702},{"type":254,"tag":622,"props":60742,"children":60743},{"class":624,"line":1096},[60744],{"type":254,"tag":622,"props":60745,"children":60746},{"style":3195},[60747],{"type":259,"value":60748},"# Retrieve your current logged-in user to be used as SQL server admin. \n",{"type":254,"tag":622,"props":60750,"children":60751},{"class":624,"line":1105},[60752],{"type":254,"tag":622,"props":60753,"children":60754},{"style":3195},[60755],{"type":259,"value":60756},"# Change with another user id if you want another user to be an admin.\n",{"type":254,"tag":622,"props":60758,"children":60759},{"class":624,"line":4131},[60760,60765,60769,60773,60778,60783,60787,60791,60795,60799,60803,60808,60812],{"type":254,"tag":622,"props":60761,"children":60762},{"style":1202},[60763],{"type":259,"value":60764},"azureaduser",{"type":254,"tag":622,"props":60766,"children":60767},{"style":629},[60768],{"type":259,"value":17046},{"type":254,"tag":622,"props":60770,"children":60771},{"style":714},[60772],{"type":259,"value":41000},{"type":254,"tag":622,"props":60774,"children":60775},{"style":664},[60776],{"type":259,"value":60777}," ad",{"type":254,"tag":622,"props":60779,"children":60780},{"style":664},[60781],{"type":259,"value":60782}," signed-in-user",{"type":254,"tag":622,"props":60784,"children":60785},{"style":664},[60786],{"type":259,"value":41013},{"type":254,"tag":622,"props":60788,"children":60789},{"style":664},[60790],{"type":259,"value":48547},{"type":254,"tag":622,"props":60792,"children":60793},{"style":629},[60794],{"type":259,"value":661},{"type":254,"tag":622,"props":60796,"children":60797},{"style":664},[60798],{"type":259,"value":25120},{"type":254,"tag":622,"props":60800,"children":60801},{"style":629},[60802],{"type":259,"value":652},{"type":254,"tag":622,"props":60804,"children":60805},{"style":664},[60806],{"type":259,"value":60807}," -o",{"type":254,"tag":622,"props":60809,"children":60810},{"style":664},[60811],{"type":259,"value":48542},{"type":254,"tag":622,"props":60813,"children":60814},{"style":629},[60815],{"type":259,"value":1685},{"type":254,"tag":622,"props":60817,"children":60818},{"class":624,"line":4183},[60819],{"type":254,"tag":622,"props":60820,"children":60821},{"emptyLinePlaceholder":1699},[60822],{"type":259,"value":1702},{"type":254,"tag":622,"props":60824,"children":60825},{"class":624,"line":4208},[60826,60831,60835,60840,60845,60850],{"type":254,"tag":622,"props":60827,"children":60828},{"style":1735},[60829],{"type":259,"value":60830},"echo",{"type":254,"tag":622,"props":60832,"children":60833},{"style":629},[60834],{"type":259,"value":661},{"type":254,"tag":622,"props":60836,"children":60837},{"style":664},[60838],{"type":259,"value":60839},"Creating ",{"type":254,"tag":622,"props":60841,"children":60842},{"style":1202},[60843],{"type":259,"value":60844},"$resourceGroup",{"type":254,"tag":622,"props":60846,"children":60847},{"style":664},[60848],{"type":259,"value":60849},"...",{"type":254,"tag":622,"props":60851,"children":60852},{"style":629},[60853],{"type":259,"value":9909},{"type":254,"tag":622,"props":60855,"children":60856},{"class":624,"line":4216},[60857,60861,60865,60869,60874,60879,60884,60888,60893],{"type":254,"tag":622,"props":60858,"children":60859},{"style":714},[60860],{"type":259,"value":41000},{"type":254,"tag":622,"props":60862,"children":60863},{"style":664},[60864],{"type":259,"value":48398},{"type":254,"tag":622,"props":60866,"children":60867},{"style":664},[60868],{"type":259,"value":48403},{"type":254,"tag":622,"props":60870,"children":60871},{"style":664},[60872],{"type":259,"value":60873}," --name",{"type":254,"tag":622,"props":60875,"children":60876},{"style":1202},[60877],{"type":259,"value":60878}," $resourceGroup ",{"type":254,"tag":622,"props":60880,"children":60881},{"style":664},[60882],{"type":259,"value":60883},"--location",{"type":254,"tag":622,"props":60885,"children":60886},{"style":629},[60887],{"type":259,"value":661},{"type":254,"tag":622,"props":60889,"children":60890},{"style":1202},[60891],{"type":259,"value":60892},"$location",{"type":254,"tag":622,"props":60894,"children":60895},{"style":629},[60896],{"type":259,"value":9909},{"type":254,"tag":622,"props":60898,"children":60899},{"class":624,"line":4224},[60900],{"type":254,"tag":622,"props":60901,"children":60902},{"emptyLinePlaceholder":1699},[60903],{"type":259,"value":1702},{"type":254,"tag":622,"props":60905,"children":60906},{"class":624,"line":4268},[60907,60911,60915,60919,60924,60929,60933,60937],{"type":254,"tag":622,"props":60908,"children":60909},{"style":1735},[60910],{"type":259,"value":60830},{"type":254,"tag":622,"props":60912,"children":60913},{"style":629},[60914],{"type":259,"value":661},{"type":254,"tag":622,"props":60916,"children":60917},{"style":664},[60918],{"type":259,"value":60839},{"type":254,"tag":622,"props":60920,"children":60921},{"style":1202},[60922],{"type":259,"value":60923},"$server",{"type":254,"tag":622,"props":60925,"children":60926},{"style":664},[60927],{"type":259,"value":60928}," in ",{"type":254,"tag":622,"props":60930,"children":60931},{"style":1202},[60932],{"type":259,"value":60892},{"type":254,"tag":622,"props":60934,"children":60935},{"style":664},[60936],{"type":259,"value":60849},{"type":254,"tag":622,"props":60938,"children":60939},{"style":629},[60940],{"type":259,"value":9909},{"type":254,"tag":622,"props":60942,"children":60943},{"class":624,"line":4276},[60944,60948,60953,60958,60962,60966,60971,60976,60980,60984,60988,60992,60996,61001,61006,61011],{"type":254,"tag":622,"props":60945,"children":60946},{"style":714},[60947],{"type":259,"value":41000},{"type":254,"tag":622,"props":60949,"children":60950},{"style":664},[60951],{"type":259,"value":60952}," sql",{"type":254,"tag":622,"props":60954,"children":60955},{"style":664},[60956],{"type":259,"value":60957}," server",{"type":254,"tag":622,"props":60959,"children":60960},{"style":664},[60961],{"type":259,"value":48403},{"type":254,"tag":622,"props":60963,"children":60964},{"style":664},[60965],{"type":259,"value":60873},{"type":254,"tag":622,"props":60967,"children":60968},{"style":1202},[60969],{"type":259,"value":60970}," $server ",{"type":254,"tag":622,"props":60972,"children":60973},{"style":664},[60974],{"type":259,"value":60975},"--resource-group",{"type":254,"tag":622,"props":60977,"children":60978},{"style":1202},[60979],{"type":259,"value":60878},{"type":254,"tag":622,"props":60981,"children":60982},{"style":664},[60983],{"type":259,"value":60883},{"type":254,"tag":622,"props":60985,"children":60986},{"style":629},[60987],{"type":259,"value":661},{"type":254,"tag":622,"props":60989,"children":60990},{"style":1202},[60991],{"type":259,"value":60892},{"type":254,"tag":622,"props":60993,"children":60994},{"style":629},[60995],{"type":259,"value":652},{"type":254,"tag":622,"props":60997,"children":60998},{"style":664},[60999],{"type":259,"value":61000}," --admin-user",{"type":254,"tag":622,"props":61002,"children":61003},{"style":1202},[61004],{"type":259,"value":61005}," $login ",{"type":254,"tag":622,"props":61007,"children":61008},{"style":664},[61009],{"type":259,"value":61010},"--admin-password",{"type":254,"tag":622,"props":61012,"children":61013},{"style":1202},[61014],{"type":259,"value":61015}," $password\n",{"type":254,"tag":622,"props":61017,"children":61018},{"class":624,"line":4332},[61019],{"type":254,"tag":622,"props":61020,"children":61021},{"emptyLinePlaceholder":1699},[61022],{"type":259,"value":1702},{"type":254,"tag":622,"props":61024,"children":61025},{"class":624,"line":4376},[61026,61030,61034,61039],{"type":254,"tag":622,"props":61027,"children":61028},{"style":1735},[61029],{"type":259,"value":60830},{"type":254,"tag":622,"props":61031,"children":61032},{"style":629},[61033],{"type":259,"value":661},{"type":254,"tag":622,"props":61035,"children":61036},{"style":664},[61037],{"type":259,"value":61038},"Configuring firewall...",{"type":254,"tag":622,"props":61040,"children":61041},{"style":629},[61042],{"type":259,"value":9909},{"type":254,"tag":622,"props":61044,"children":61045},{"class":624,"line":4384},[61046,61050,61054,61058,61063,61067,61072,61076,61081,61085,61089,61094,61099,61104,61109],{"type":254,"tag":622,"props":61047,"children":61048},{"style":714},[61049],{"type":259,"value":41000},{"type":254,"tag":622,"props":61051,"children":61052},{"style":664},[61053],{"type":259,"value":60952},{"type":254,"tag":622,"props":61055,"children":61056},{"style":664},[61057],{"type":259,"value":60957},{"type":254,"tag":622,"props":61059,"children":61060},{"style":664},[61061],{"type":259,"value":61062}," firewall-rule",{"type":254,"tag":622,"props":61064,"children":61065},{"style":664},[61066],{"type":259,"value":48403},{"type":254,"tag":622,"props":61068,"children":61069},{"style":664},[61070],{"type":259,"value":61071}," --resource-group",{"type":254,"tag":622,"props":61073,"children":61074},{"style":1202},[61075],{"type":259,"value":60878},{"type":254,"tag":622,"props":61077,"children":61078},{"style":664},[61079],{"type":259,"value":61080},"--server",{"type":254,"tag":622,"props":61082,"children":61083},{"style":1202},[61084],{"type":259,"value":60970},{"type":254,"tag":622,"props":61086,"children":61087},{"style":664},[61088],{"type":259,"value":19849},{"type":254,"tag":622,"props":61090,"children":61091},{"style":664},[61092],{"type":259,"value":61093}," AllowYourIp",{"type":254,"tag":622,"props":61095,"children":61096},{"style":664},[61097],{"type":259,"value":61098}," --start-ip-address",{"type":254,"tag":622,"props":61100,"children":61101},{"style":1202},[61102],{"type":259,"value":61103}," $startIP ",{"type":254,"tag":622,"props":61105,"children":61106},{"style":664},[61107],{"type":259,"value":61108},"--end-ip-address",{"type":254,"tag":622,"props":61110,"children":61111},{"style":1202},[61112],{"type":259,"value":61113}," $endIP\n",{"type":254,"tag":622,"props":61115,"children":61116},{"class":624,"line":4416},[61117],{"type":254,"tag":622,"props":61118,"children":61119},{"emptyLinePlaceholder":1699},[61120],{"type":259,"value":1702},{"type":254,"tag":622,"props":61122,"children":61123},{"class":624,"line":4456},[61124,61128,61132,61136,61141,61145,61149,61153],{"type":254,"tag":622,"props":61125,"children":61126},{"style":1735},[61127],{"type":259,"value":60830},{"type":254,"tag":622,"props":61129,"children":61130},{"style":629},[61131],{"type":259,"value":661},{"type":254,"tag":622,"props":61133,"children":61134},{"style":664},[61135],{"type":259,"value":60839},{"type":254,"tag":622,"props":61137,"children":61138},{"style":1202},[61139],{"type":259,"value":61140},"$database",{"type":254,"tag":622,"props":61142,"children":61143},{"style":664},[61144],{"type":259,"value":10532},{"type":254,"tag":622,"props":61146,"children":61147},{"style":1202},[61148],{"type":259,"value":60923},{"type":254,"tag":622,"props":61150,"children":61151},{"style":664},[61152],{"type":259,"value":60849},{"type":254,"tag":622,"props":61154,"children":61155},{"style":629},[61156],{"type":259,"value":9909},{"type":254,"tag":622,"props":61158,"children":61159},{"class":624,"line":4488},[61160,61164,61168,61173,61177,61181,61185,61189,61193,61198,61203,61208,61213,61218,61223,61228],{"type":254,"tag":622,"props":61161,"children":61162},{"style":714},[61163],{"type":259,"value":41000},{"type":254,"tag":622,"props":61165,"children":61166},{"style":664},[61167],{"type":259,"value":60952},{"type":254,"tag":622,"props":61169,"children":61170},{"style":664},[61171],{"type":259,"value":61172}," db",{"type":254,"tag":622,"props":61174,"children":61175},{"style":664},[61176],{"type":259,"value":48403},{"type":254,"tag":622,"props":61178,"children":61179},{"style":664},[61180],{"type":259,"value":61071},{"type":254,"tag":622,"props":61182,"children":61183},{"style":1202},[61184],{"type":259,"value":60878},{"type":254,"tag":622,"props":61186,"children":61187},{"style":664},[61188],{"type":259,"value":61080},{"type":254,"tag":622,"props":61190,"children":61191},{"style":1202},[61192],{"type":259,"value":60970},{"type":254,"tag":622,"props":61194,"children":61195},{"style":664},[61196],{"type":259,"value":61197},"--name",{"type":254,"tag":622,"props":61199,"children":61200},{"style":1202},[61201],{"type":259,"value":61202}," $database ",{"type":254,"tag":622,"props":61204,"children":61205},{"style":664},[61206],{"type":259,"value":61207},"--sample-name",{"type":254,"tag":622,"props":61209,"children":61210},{"style":664},[61211],{"type":259,"value":61212}," AdventureWorksLT",{"type":254,"tag":622,"props":61214,"children":61215},{"style":664},[61216],{"type":259,"value":61217}," --service-objective",{"type":254,"tag":622,"props":61219,"children":61220},{"style":664},[61221],{"type":259,"value":61222}," Basic",{"type":254,"tag":622,"props":61224,"children":61225},{"style":664},[61226],{"type":259,"value":61227}," --zone-redundant",{"type":254,"tag":622,"props":61229,"children":61230},{"style":629},[61231],{"type":259,"value":61232}," false\n",{"type":254,"tag":622,"props":61234,"children":61235},{"class":624,"line":4496},[61236],{"type":254,"tag":622,"props":61237,"children":61238},{"emptyLinePlaceholder":1699},[61239],{"type":259,"value":1702},{"type":254,"tag":622,"props":61241,"children":61242},{"class":624,"line":4564},[61243,61247,61251,61256],{"type":254,"tag":622,"props":61244,"children":61245},{"style":1735},[61246],{"type":259,"value":60830},{"type":254,"tag":622,"props":61248,"children":61249},{"style":629},[61250],{"type":259,"value":661},{"type":254,"tag":622,"props":61252,"children":61253},{"style":664},[61254],{"type":259,"value":61255},"Creating AD admin in sql server...",{"type":254,"tag":622,"props":61257,"children":61258},{"style":629},[61259],{"type":259,"value":9909},{"type":254,"tag":622,"props":61261,"children":61262},{"class":624,"line":4572},[61263,61267,61271,61275,61280,61284,61288,61292,61297,61301,61306,61311,61316],{"type":254,"tag":622,"props":61264,"children":61265},{"style":714},[61266],{"type":259,"value":41000},{"type":254,"tag":622,"props":61268,"children":61269},{"style":664},[61270],{"type":259,"value":60952},{"type":254,"tag":622,"props":61272,"children":61273},{"style":664},[61274],{"type":259,"value":60957},{"type":254,"tag":622,"props":61276,"children":61277},{"style":664},[61278],{"type":259,"value":61279}," ad-admin",{"type":254,"tag":622,"props":61281,"children":61282},{"style":664},[61283],{"type":259,"value":48403},{"type":254,"tag":622,"props":61285,"children":61286},{"style":664},[61287],{"type":259,"value":61071},{"type":254,"tag":622,"props":61289,"children":61290},{"style":1202},[61291],{"type":259,"value":60878},{"type":254,"tag":622,"props":61293,"children":61294},{"style":664},[61295],{"type":259,"value":61296},"--server-name",{"type":254,"tag":622,"props":61298,"children":61299},{"style":1202},[61300],{"type":259,"value":60970},{"type":254,"tag":622,"props":61302,"children":61303},{"style":664},[61304],{"type":259,"value":61305},"--display-name",{"type":254,"tag":622,"props":61307,"children":61308},{"style":664},[61309],{"type":259,"value":61310}," ADMIN",{"type":254,"tag":622,"props":61312,"children":61313},{"style":664},[61314],{"type":259,"value":61315}," --object-id",{"type":254,"tag":622,"props":61317,"children":61318},{"style":1202},[61319],{"type":259,"value":61320}," $azureaduser\n",{"type":254,"tag":622,"props":61322,"children":61323},{"class":624,"line":4644},[61324],{"type":254,"tag":622,"props":61325,"children":61326},{"emptyLinePlaceholder":1699},[61327],{"type":259,"value":1702},{"type":254,"tag":622,"props":61329,"children":61330},{"class":624,"line":4652},[61331,61335,61339,61344,61348,61353,61357,61362,61366,61370,61374],{"type":254,"tag":622,"props":61332,"children":61333},{"style":1735},[61334],{"type":259,"value":60830},{"type":254,"tag":622,"props":61336,"children":61337},{"style":629},[61338],{"type":259,"value":661},{"type":254,"tag":622,"props":61340,"children":61341},{"style":664},[61342],{"type":259,"value":61343},"Database connection string to use: ",{"type":254,"tag":622,"props":61345,"children":61346},{"style":1202},[61347],{"type":259,"value":9852},{"type":254,"tag":622,"props":61349,"children":61350},{"style":664},[61351],{"type":259,"value":61352},"Server=",{"type":254,"tag":622,"props":61354,"children":61355},{"style":1202},[61356],{"type":259,"value":60923},{"type":254,"tag":622,"props":61358,"children":61359},{"style":664},[61360],{"type":259,"value":61361},".database.windows.net; Authentication=Active Directory Default; Database=",{"type":254,"tag":622,"props":61363,"children":61364},{"style":1202},[61365],{"type":259,"value":61140},{"type":254,"tag":622,"props":61367,"children":61368},{"style":664},[61369],{"type":259,"value":2000},{"type":254,"tag":622,"props":61371,"children":61372},{"style":1202},[61373],{"type":259,"value":9852},{"type":254,"tag":622,"props":61375,"children":61376},{"style":629},[61377],{"type":259,"value":9909},{"type":254,"tag":255,"props":61379,"children":61380},{},[61381],{"type":259,"value":61382},"This script should be self-explanatory if you have already played a little with Azure CLI. Basically, what it does is:",{"type":254,"tag":332,"props":61384,"children":61385},{},[61386,61391,61403,61408,61413],{"type":254,"tag":336,"props":61387,"children":61388},{},[61389],{"type":259,"value":61390},"create an azure SQL server",{"type":254,"tag":336,"props":61392,"children":61393},{},[61394,61396,61401],{"type":259,"value":61395},"configure the server firewall to allow you to query it from your local IP address (if you are executing the script from cloud shell, replace ",{"type":254,"tag":389,"props":61397,"children":61399},{"className":61398},[],[61400],{"type":259,"value":60688},{"type":259,"value":61402}," variable with your local machine IP)",{"type":254,"tag":336,"props":61404,"children":61405},{},[61406],{"type":259,"value":61407},"create an azure SQL database with already tables and data in it from the sample AdventureWorksLT",{"type":254,"tag":336,"props":61409,"children":61410},{},[61411],{"type":259,"value":61412},"set you logged in azure ad user as the AD administrator of the database",{"type":254,"tag":336,"props":61414,"children":61415},{},[61416],{"type":259,"value":61417},"write in the console the connection string to use in your C# code to access the database",{"type":254,"tag":255,"props":61419,"children":61420},{},[61421],{"type":259,"value":61422},"If you want to customize something do not hesitate to modify the scripts and especially variables like the resources location, the SQL server user/password, or the name of the resources. This is a bash script but if you want to execute it in PowerShell, all the Azure CLI commands should work fine, you just have to change the variables declarations as the syntax is different in PowerShell. If you don't have Azure CLI installed on your laptop you can use Azure Cloud Shell to execute this script.",{"type":254,"tag":427,"props":61424,"children":61425},{"icon":429},[61426],{"type":254,"tag":255,"props":61427,"children":61428},{},[61429,61431,61436],{"type":259,"value":61430},"If you are new to Azure CLI, you can read my article ",{"type":254,"tag":262,"props":61432,"children":61434},{"href":17271,"rel":61433},[266],[61435],{"type":259,"value":15},{"type":259,"value":594},{"type":254,"tag":455,"props":61438,"children":61440},{"id":61439},"querying-the-database-from-a-minima-api-in-c",[61441],{"type":259,"value":61442},"Querying the database from a minima API in C#",{"type":254,"tag":255,"props":61444,"children":61445},{},[61446,61448,61455],{"type":259,"value":61447},"Usually, I like to create a console application (with the ",{"type":254,"tag":262,"props":61449,"children":61452},{"href":61450,"rel":61451},"https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-5.0&tabs=visual-studio#worker-service-template",[266],[61453],{"type":259,"value":61454},"worker service template",{"type":259,"value":61456}," for instance) for my samples, yet this time I decided to try the new minimal APIs from .NET 6 (currently in preview).",{"type":254,"tag":255,"props":61458,"children":61459},{},[61460,61462,61467],{"type":259,"value":61461},"Minimal APIs would probably deserve an entire blog post, but let's just say a minimal API in .NET 6 allow you to build a small HTTP API with less ceremony than a classic controller-based API. As all the code can be written in a ",{"type":254,"tag":389,"props":61463,"children":61465},{"className":61464},[],[61466],{"type":259,"value":3667},{"type":259,"value":61468}," file, so it's very convenient when you want to quickly build a web application without too much complexity (especially if you are new to ASP.NET Core) or if you are developing a small microservice.",{"type":254,"tag":612,"props":61470,"children":61472},{"className":1423,"code":61471,"language":1425,"meta":205,"style":205},"using Dapper;\nusing Microsoft.Data.SqlClient;\n\nvar builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\nif (app.Environment.IsDevelopment())\n{\n    app.UseDeveloperExceptionPage();\n}\n\napp.MapGet(\"/\", async () =>\n{\n    using var connection = new SqlConnection(\"Server=server-testingmsi28497.database.windows.net; Authentication=Active Directory Default; Database=database-testingmsi28497;\");\n    var products = await connection.QueryAsync\u003CProduct>(\"SELECT TOP 10 ProductID, Name from [SalesLT].[Product]\");\n    return products;\n});\n\napp.Run();\n\npublic record Product(int ProductID, string Name);\n",[61473],{"type":254,"tag":389,"props":61474,"children":61475},{"__ignoreMap":205},[61476,61492,61523,61530,61571,61603,61610,61646,61653,61674,61681,61688,61736,61743,61792,61854,61869,61876,61883,61902,61909],{"type":254,"tag":622,"props":61477,"children":61478},{"class":624,"line":625},[61479,61483,61488],{"type":254,"tag":622,"props":61480,"children":61481},{"style":741},[61482],{"type":259,"value":1604},{"type":254,"tag":622,"props":61484,"children":61485},{"style":1202},[61486],{"type":259,"value":61487}," Dapper",{"type":254,"tag":622,"props":61489,"children":61490},{"style":629},[61491],{"type":259,"value":2350},{"type":254,"tag":622,"props":61493,"children":61494},{"class":624,"line":635},[61495,61499,61503,61507,61511,61515,61519],{"type":254,"tag":622,"props":61496,"children":61497},{"style":741},[61498],{"type":259,"value":1604},{"type":254,"tag":622,"props":61500,"children":61501},{"style":1202},[61502],{"type":259,"value":6420},{"type":254,"tag":622,"props":61504,"children":61505},{"style":629},[61506],{"type":259,"value":594},{"type":254,"tag":622,"props":61508,"children":61509},{"style":1202},[61510],{"type":259,"value":21594},{"type":254,"tag":622,"props":61512,"children":61513},{"style":629},[61514],{"type":259,"value":594},{"type":254,"tag":622,"props":61516,"children":61517},{"style":1202},[61518],{"type":259,"value":60279},{"type":254,"tag":622,"props":61520,"children":61521},{"style":629},[61522],{"type":259,"value":2350},{"type":254,"tag":622,"props":61524,"children":61525},{"class":624,"line":679},[61526],{"type":254,"tag":622,"props":61527,"children":61528},{"emptyLinePlaceholder":1699},[61529],{"type":259,"value":1702},{"type":254,"tag":622,"props":61531,"children":61532},{"class":624,"line":705},[61533,61537,61541,61545,61550,61554,61559,61563,61567],{"type":254,"tag":622,"props":61534,"children":61535},{"style":714},[61536],{"type":259,"value":1710},{"type":254,"tag":622,"props":61538,"children":61539},{"style":714},[61540],{"type":259,"value":6460},{"type":254,"tag":622,"props":61542,"children":61543},{"style":629},[61544],{"type":259,"value":1619},{"type":254,"tag":622,"props":61546,"children":61547},{"style":1202},[61548],{"type":259,"value":61549}," WebApplication",{"type":254,"tag":622,"props":61551,"children":61552},{"style":629},[61553],{"type":259,"value":594},{"type":254,"tag":622,"props":61555,"children":61556},{"style":1735},[61557],{"type":259,"value":61558},"CreateBuilder",{"type":254,"tag":622,"props":61560,"children":61561},{"style":629},[61562],{"type":259,"value":1668},{"type":254,"tag":622,"props":61564,"children":61565},{"style":1202},[61566],{"type":259,"value":6487},{"type":254,"tag":622,"props":61568,"children":61569},{"style":629},[61570],{"type":259,"value":1554},{"type":254,"tag":622,"props":61572,"children":61573},{"class":624,"line":732},[61574,61578,61583,61587,61591,61595,61599],{"type":254,"tag":622,"props":61575,"children":61576},{"style":714},[61577],{"type":259,"value":1710},{"type":254,"tag":622,"props":61579,"children":61580},{"style":714},[61581],{"type":259,"value":61582}," app",{"type":254,"tag":622,"props":61584,"children":61585},{"style":629},[61586],{"type":259,"value":1619},{"type":254,"tag":622,"props":61588,"children":61589},{"style":1202},[61590],{"type":259,"value":6460},{"type":254,"tag":622,"props":61592,"children":61593},{"style":629},[61594],{"type":259,"value":594},{"type":254,"tag":622,"props":61596,"children":61597},{"style":1735},[61598],{"type":259,"value":6697},{"type":254,"tag":622,"props":61600,"children":61601},{"style":629},[61602],{"type":259,"value":2798},{"type":254,"tag":622,"props":61604,"children":61605},{"class":624,"line":759},[61606],{"type":254,"tag":622,"props":61607,"children":61608},{"emptyLinePlaceholder":1699},[61609],{"type":259,"value":1702},{"type":254,"tag":622,"props":61611,"children":61612},{"class":624,"line":798},[61613,61617,61621,61626,61630,61634,61638,61642],{"type":254,"tag":622,"props":61614,"children":61615},{"style":1196},[61616],{"type":259,"value":52837},{"type":254,"tag":622,"props":61618,"children":61619},{"style":629},[61620],{"type":259,"value":3547},{"type":254,"tag":622,"props":61622,"children":61623},{"style":1202},[61624],{"type":259,"value":61625},"app",{"type":254,"tag":622,"props":61627,"children":61628},{"style":629},[61629],{"type":259,"value":594},{"type":254,"tag":622,"props":61631,"children":61632},{"style":1202},[61633],{"type":259,"value":55085},{"type":254,"tag":622,"props":61635,"children":61636},{"style":629},[61637],{"type":259,"value":594},{"type":254,"tag":622,"props":61639,"children":61640},{"style":1735},[61641],{"type":259,"value":54271},{"type":254,"tag":622,"props":61643,"children":61644},{"style":629},[61645],{"type":259,"value":2756},{"type":254,"tag":622,"props":61647,"children":61648},{"class":624,"line":836},[61649],{"type":254,"tag":622,"props":61650,"children":61651},{"style":629},[61652],{"type":259,"value":632},{"type":254,"tag":622,"props":61654,"children":61655},{"class":624,"line":862},[61656,61661,61665,61670],{"type":254,"tag":622,"props":61657,"children":61658},{"style":1202},[61659],{"type":259,"value":61660},"    app",{"type":254,"tag":622,"props":61662,"children":61663},{"style":629},[61664],{"type":259,"value":594},{"type":254,"tag":622,"props":61666,"children":61667},{"style":1735},[61668],{"type":259,"value":61669},"UseDeveloperExceptionPage",{"type":254,"tag":622,"props":61671,"children":61672},{"style":629},[61673],{"type":259,"value":2798},{"type":254,"tag":622,"props":61675,"children":61676},{"class":624,"line":900},[61677],{"type":254,"tag":622,"props":61678,"children":61679},{"style":629},[61680],{"type":259,"value":1111},{"type":254,"tag":622,"props":61682,"children":61683},{"class":624,"line":938},[61684],{"type":254,"tag":622,"props":61685,"children":61686},{"emptyLinePlaceholder":1699},[61687],{"type":259,"value":1702},{"type":254,"tag":622,"props":61689,"children":61690},{"class":624,"line":976},[61691,61695,61699,61704,61708,61712,61716,61720,61724,61728,61732],{"type":254,"tag":622,"props":61692,"children":61693},{"style":1202},[61694],{"type":259,"value":61625},{"type":254,"tag":622,"props":61696,"children":61697},{"style":629},[61698],{"type":259,"value":594},{"type":254,"tag":622,"props":61700,"children":61701},{"style":1735},[61702],{"type":259,"value":61703},"MapGet",{"type":254,"tag":622,"props":61705,"children":61706},{"style":629},[61707],{"type":259,"value":1668},{"type":254,"tag":622,"props":61709,"children":61710},{"style":629},[61711],{"type":259,"value":652},{"type":254,"tag":622,"props":61713,"children":61714},{"style":664},[61715],{"type":259,"value":9768},{"type":254,"tag":622,"props":61717,"children":61718},{"style":629},[61719],{"type":259,"value":652},{"type":254,"tag":622,"props":61721,"children":61722},{"style":629},[61723],{"type":259,"value":3149},{"type":254,"tag":622,"props":61725,"children":61726},{"style":644},[61727],{"type":259,"value":6987},{"type":254,"tag":622,"props":61729,"children":61730},{"style":629},[61731],{"type":259,"value":9116},{"type":254,"tag":622,"props":61733,"children":61734},{"style":629},[61735],{"type":259,"value":6569},{"type":254,"tag":622,"props":61737,"children":61738},{"class":624,"line":1014},[61739],{"type":254,"tag":622,"props":61740,"children":61741},{"style":629},[61742],{"type":259,"value":632},{"type":254,"tag":622,"props":61744,"children":61745},{"class":624,"line":1052},[61746,61751,61755,61759,61763,61767,61771,61775,61779,61784,61788],{"type":254,"tag":622,"props":61747,"children":61748},{"style":1196},[61749],{"type":259,"value":61750},"    using",{"type":254,"tag":622,"props":61752,"children":61753},{"style":714},[61754],{"type":259,"value":1609},{"type":254,"tag":622,"props":61756,"children":61757},{"style":714},[61758],{"type":259,"value":59905},{"type":254,"tag":622,"props":61760,"children":61761},{"style":629},[61762],{"type":259,"value":1619},{"type":254,"tag":622,"props":61764,"children":61765},{"style":629},[61766],{"type":259,"value":1624},{"type":254,"tag":622,"props":61768,"children":61769},{"style":714},[61770],{"type":259,"value":59879},{"type":254,"tag":622,"props":61772,"children":61773},{"style":629},[61774],{"type":259,"value":1668},{"type":254,"tag":622,"props":61776,"children":61777},{"style":629},[61778],{"type":259,"value":652},{"type":254,"tag":622,"props":61780,"children":61781},{"style":664},[61782],{"type":259,"value":61783},"Server=server-testingmsi28497.database.windows.net; Authentication=Active Directory Default; Database=database-testingmsi28497;",{"type":254,"tag":622,"props":61785,"children":61786},{"style":629},[61787],{"type":259,"value":652},{"type":254,"tag":622,"props":61789,"children":61790},{"style":629},[61791],{"type":259,"value":1554},{"type":254,"tag":622,"props":61793,"children":61794},{"class":624,"line":1078},[61795,61799,61804,61808,61812,61816,61820,61825,61829,61833,61837,61841,61846,61850],{"type":254,"tag":622,"props":61796,"children":61797},{"style":714},[61798],{"type":259,"value":2099},{"type":254,"tag":622,"props":61800,"children":61801},{"style":714},[61802],{"type":259,"value":61803}," products",{"type":254,"tag":622,"props":61805,"children":61806},{"style":629},[61807],{"type":259,"value":1619},{"type":254,"tag":622,"props":61809,"children":61810},{"style":629},[61811],{"type":259,"value":1724},{"type":254,"tag":622,"props":61813,"children":61814},{"style":1202},[61815],{"type":259,"value":59905},{"type":254,"tag":622,"props":61817,"children":61818},{"style":629},[61819],{"type":259,"value":594},{"type":254,"tag":622,"props":61821,"children":61822},{"style":1735},[61823],{"type":259,"value":61824},"QueryAsync",{"type":254,"tag":622,"props":61826,"children":61827},{"style":629},[61828],{"type":259,"value":1743},{"type":254,"tag":622,"props":61830,"children":61831},{"style":714},[61832],{"type":259,"value":9651},{"type":254,"tag":622,"props":61834,"children":61835},{"style":629},[61836],{"type":259,"value":3517},{"type":254,"tag":622,"props":61838,"children":61839},{"style":629},[61840],{"type":259,"value":652},{"type":254,"tag":622,"props":61842,"children":61843},{"style":664},[61844],{"type":259,"value":61845},"SELECT TOP 10 ProductID, Name from [SalesLT].[Product]",{"type":254,"tag":622,"props":61847,"children":61848},{"style":629},[61849],{"type":259,"value":652},{"type":254,"tag":622,"props":61851,"children":61852},{"style":629},[61853],{"type":259,"value":1554},{"type":254,"tag":622,"props":61855,"children":61856},{"class":624,"line":1087},[61857,61861,61865],{"type":254,"tag":622,"props":61858,"children":61859},{"style":1196},[61860],{"type":259,"value":2358},{"type":254,"tag":622,"props":61862,"children":61863},{"style":1202},[61864],{"type":259,"value":61803},{"type":254,"tag":622,"props":61866,"children":61867},{"style":629},[61868],{"type":259,"value":2350},{"type":254,"tag":622,"props":61870,"children":61871},{"class":624,"line":1096},[61872],{"type":254,"tag":622,"props":61873,"children":61874},{"style":629},[61875],{"type":259,"value":6661},{"type":254,"tag":622,"props":61877,"children":61878},{"class":624,"line":1105},[61879],{"type":254,"tag":622,"props":61880,"children":61881},{"emptyLinePlaceholder":1699},[61882],{"type":259,"value":1702},{"type":254,"tag":622,"props":61884,"children":61885},{"class":624,"line":4131},[61886,61890,61894,61898],{"type":254,"tag":622,"props":61887,"children":61888},{"style":1202},[61889],{"type":259,"value":61625},{"type":254,"tag":622,"props":61891,"children":61892},{"style":629},[61893],{"type":259,"value":594},{"type":254,"tag":622,"props":61895,"children":61896},{"style":1735},[61897],{"type":259,"value":6718},{"type":254,"tag":622,"props":61899,"children":61900},{"style":629},[61901],{"type":259,"value":2798},{"type":254,"tag":622,"props":61903,"children":61904},{"class":624,"line":4183},[61905],{"type":254,"tag":622,"props":61906,"children":61907},{"emptyLinePlaceholder":1699},[61908],{"type":259,"value":1702},{"type":254,"tag":622,"props":61910,"children":61911},{"class":624,"line":4208},[61912,61916,61920,61924,61928,61933,61937,61941,61945],{"type":254,"tag":622,"props":61913,"children":61914},{"style":644},[61915],{"type":259,"value":1437},{"type":254,"tag":622,"props":61917,"children":61918},{"style":714},[61919],{"type":259,"value":1442},{"type":254,"tag":622,"props":61921,"children":61922},{"style":714},[61923],{"type":259,"value":9319},{"type":254,"tag":622,"props":61925,"children":61926},{"style":629},[61927],{"type":259,"value":15271},{"type":254,"tag":622,"props":61929,"children":61930},{"style":714},[61931],{"type":259,"value":61932}," ProductID",{"type":254,"tag":622,"props":61934,"children":61935},{"style":629},[61936],{"type":259,"value":3149},{"type":254,"tag":622,"props":61938,"children":61939},{"style":629},[61940],{"type":259,"value":1975},{"type":254,"tag":622,"props":61942,"children":61943},{"style":714},[61944],{"type":259,"value":1465},{"type":254,"tag":622,"props":61946,"children":61947},{"style":629},[61948],{"type":259,"value":1554},{"type":254,"tag":255,"props":61950,"children":61951},{},[61952],{"type":259,"value":61953},"As you can see this code is only 26 lines long:",{"type":254,"tag":332,"props":61955,"children":61956},{},[61957,61970,61982,61994],{"type":254,"tag":336,"props":61958,"children":61959},{},[61960,61962,61968],{"type":259,"value":61961},"there is only one route, that returns the Product identifiers and names from the table ",{"type":254,"tag":389,"props":61963,"children":61965},{"className":61964},[],[61966],{"type":259,"value":61967},"[SalesLT].[Product]",{"type":259,"value":61969}," of the database created with the previous Azure CLI script",{"type":254,"tag":336,"props":61971,"children":61972},{},[61973,61975,61980],{"type":259,"value":61974},"the SQL query is done by using the micro ORM ",{"type":254,"tag":262,"props":61976,"children":61978},{"href":45047,"rel":61977},[266],[61979],{"type":259,"value":45051},{"type":259,"value":61981}," which simplifies the boilerplate code to query an SQL database while keeping performance",{"type":254,"tag":336,"props":61983,"children":61984},{},[61985,61987,61992],{"type":259,"value":61986},"the result of the SQL query is mapped to a record class ",{"type":254,"tag":389,"props":61988,"children":61990},{"className":61989},[],[61991],{"type":259,"value":9651},{"type":259,"value":61993}," which is declared in one line",{"type":254,"tag":336,"props":61995,"children":61996},{},[61997,61999,62004,62006,62011],{"type":259,"value":61998},"the code uses ",{"type":254,"tag":389,"props":62000,"children":62002},{"className":62001},[],[62003],{"type":259,"value":43886},{"type":259,"value":62005}," v3.0.0 with the ",{"type":254,"tag":389,"props":62007,"children":62009},{"className":62008},[],[62010],{"type":259,"value":59739},{"type":259,"value":60254},{"type":254,"tag":255,"props":62013,"children":62014},{},[62015],{"type":254,"tag":1161,"props":62016,"children":62020},{"alt":62017,"className":62018,"src":62019},"C# code querying an SQL Database using Active Directory Default authentication mode.",[1165,1166],"/posts/images/sqlclient_minapi_1.png",[],{"type":254,"tag":427,"props":62022,"children":62023},{"icon":429},[62024],{"type":254,"tag":255,"props":62025,"children":62026},{},[62027,62029,62035],{"type":259,"value":62028},"To keep things simple, I am connecting to the database with the Azure AD account which is an admin of the SQL server. But I could also have assigned a role with lower permissions to my account, see ",{"type":254,"tag":262,"props":62030,"children":62033},{"href":62031,"rel":62032},"https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi#grant-permissions-to-managed-identity",[266],[62034],{"type":259,"value":1414},{"type":259,"value":62036}," for more information on how to do that.",{"type":254,"tag":282,"props":62038,"children":62039},{"id":10572},[62040],{"type":259,"value":10575},{"type":254,"tag":255,"props":62042,"children":62043},{},[62044,62046,62052],{"type":259,"value":62045},"While building an application interacting with Azure we often neglect to use mechanisms like Azure AD authentication that remove the need for secrets. But as we have seen in this article some libraries like ",{"type":254,"tag":389,"props":62047,"children":62049},{"className":62048},[],[62050],{"type":259,"value":62051},"Microsoft.Data.Sql.Client",{"type":259,"value":62053}," or the Azure SDKs allow us to do that quite easily. I love how connecting to an Azure SQL Database in C# is becoming more simple and more secure at the same time.",{"type":254,"tag":1260,"props":62055,"children":62056},{},[62057],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":62059},[62060,62061,62062,62064,62068],{"id":59772,"depth":635,"text":59775},{"id":59964,"depth":635,"text":59967},{"id":60244,"depth":635,"text":62063},"Here comes Active Directory Default authentication mode",{"id":60384,"depth":635,"text":60387,"children":62065},[62066,62067],{"id":60395,"depth":679,"text":60398},{"id":61439,"depth":679,"text":61442},{"id":10572,"depth":635,"text":10575},"content:1.posts:20.sqlclient-active-directory-authent.md","1.posts/20.sqlclient-active-directory-authent.md",{"_path":64,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":63,"description":62072,"lead":62073,"date":62074,"image":62075,"badge":62076,"tags":62077,"body":62078,"_type":1275,"_id":62422,"_source":231,"_file":62423,"_extension":1278},"This week I learned some Azure CLI commands, how to have JSON IntelliSense in vscode and that Azure Storage Explorer was usable again.","IntelliSense in vscode, Azure CLI command to get assigned roles, and Azure Storage Explorer new version.","2021-06-07T00:00:00.000Z",{"src":9258},{"label":9260},[9262,37215,16351,6260],{"type":251,"children":62079,"toc":62417},[62080,62084,62090,62095,62108,62117,62122,62135,62141,62159,62168,62194,62214,62256,62261,62372,62378,62390,62395,62404,62409,62413],{"type":254,"tag":255,"props":62081,"children":62082},{},[62083],{"type":259,"value":62072},{"type":254,"tag":282,"props":62085,"children":62087},{"id":62086},"autocompletion-and-schema-validation-when-editing-a-json-file-in-vscode",[62088],{"type":259,"value":62089},"Autocompletion and schema validation when editing a JSON file in vscode.",{"type":254,"tag":255,"props":62091,"children":62092},{},[62093],{"type":259,"value":62094},"When you are editing a JSON file in vscode, chances are that there is a JSON schema somewhere that describes the shape of the JSON you are modifying. Wouldn't it be great if vscode could provide you suggestions and validate the JSON according to such a schema while you are typing ⌨?",{"type":254,"tag":255,"props":62096,"children":62097},{},[62098,62100,62106],{"type":259,"value":62099},"Well in fact it is possible, you just have to add at the beginning of your file a ",{"type":254,"tag":389,"props":62101,"children":62103},{"className":62102},[],[62104],{"type":259,"value":62105},"$schema",{"type":259,"value":62107}," key (with the URI of the JSON schema as the value) and you will have IntelliSense to edit your JSON in vscode.",{"type":254,"tag":255,"props":62109,"children":62110},{},[62111],{"type":254,"tag":1161,"props":62112,"children":62116},{"alt":62113,"className":62114,"src":62115},"Json file in vscode.",[1165,1166],"/posts/images/w222021tips_vscode_1.png",[],{"type":254,"tag":255,"props":62118,"children":62119},{},[62120],{"type":259,"value":62121},"In the screenshot above, I am editing a JSON file where the linked JSON schema is a Microsoft Teams app manifest schema, and as you can see vscode gives me suggestions based on this schema.",{"type":254,"tag":255,"props":62123,"children":62124},{},[62125,62127,62134],{"type":259,"value":62126},"This is a tip that you probably already know but as far as I am concerned I only discovered it recently and it is really useful. If you want to do more advanced things like mapping some files types to specific JSON schemas you can have a look in ",{"type":254,"tag":262,"props":62128,"children":62131},{"href":62129,"rel":62130},"https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings",[266],[62132],{"type":259,"value":62133},"vscode documentation",{"type":259,"value":594},{"type":254,"tag":282,"props":62136,"children":62138},{"id":62137},"listing-assigned-roles-for-a-user-on-a-subscription-with-azure-cli",[62139],{"type":259,"value":62140},"Listing assigned roles for a user on a subscription with Azure CLI",{"type":254,"tag":255,"props":62142,"children":62143},{},[62144,62146,62158],{"type":259,"value":62145},"I recently had to list all the roles assigned to my user on a subscription. 👮‍♀️\nFor that I used the ",{"type":254,"tag":262,"props":62147,"children":62150},{"href":62148,"rel":62149},"https://docs.microsoft.com/en-us/cli/azure/role/assignment?view=azure-cli-latest#az_role_assignment_list",[266],[62151,62157],{"type":254,"tag":389,"props":62152,"children":62154},{"className":62153},[],[62155],{"type":259,"value":62156},"az role assignment list",{"type":259,"value":58565},{"type":259,"value":9687},{"type":254,"tag":255,"props":62160,"children":62161},{},[62162],{"type":254,"tag":1161,"props":62163,"children":62167},{"alt":62164,"className":62165,"src":62166},"Terminal showing az role assigment command.",[1165,1166],"/posts/images/w222021tips_azcli_1.png",[],{"type":254,"tag":255,"props":62169,"children":62170},{},[62171,62173,62178,62180,62186,62187,62193],{"type":259,"value":62172},"I am filtering the command result with the ",{"type":254,"tag":389,"props":62174,"children":62176},{"className":62175},[],[62177],{"type":259,"value":17258},{"type":259,"value":62179}," argument to only have the information I am interested in which are ",{"type":254,"tag":389,"props":62181,"children":62183},{"className":62182},[],[62184],{"type":259,"value":62185},"roleDefinitionName",{"type":259,"value":10347},{"type":254,"tag":389,"props":62188,"children":62190},{"className":62189},[],[62191],{"type":259,"value":62192},"scope",{"type":259,"value":594},{"type":254,"tag":255,"props":62195,"children":62196},{},[62197,62199,62205,62207,62213],{"type":259,"value":62198},"You can specify a subscription by using the optional parameter ",{"type":254,"tag":389,"props":62200,"children":62202},{"className":62201},[],[62203],{"type":259,"value":62204},"--subscription",{"type":259,"value":62206}," but by default, the subscription selected is the current subscription which you can see by doing an ",{"type":254,"tag":389,"props":62208,"children":62210},{"className":62209},[],[62211],{"type":259,"value":62212},"az account show",{"type":259,"value":594},{"type":254,"tag":255,"props":62215,"children":62216},{},[62217,62219,62225,62227,62233,62235,62241,62243,62254],{"type":259,"value":62218},"The variable ",{"type":254,"tag":389,"props":62220,"children":62222},{"className":62221},[],[62223],{"type":259,"value":62224},"UserId",{"type":259,"value":62226}," which is passed to the option ",{"type":254,"tag":389,"props":62228,"children":62230},{"className":62229},[],[62231],{"type":259,"value":62232},"--assignee",{"type":259,"value":62234}," contains my user id but I could have also passed my user principal name. As I was looking to list my assigned roles on a subscription in a tenant id where I am a guest, my user principal name is a bit strange with an ",{"type":254,"tag":389,"props":62236,"children":62238},{"className":62237},[],[62239],{"type":259,"value":62240},"#EXT#",{"type":259,"value":62242}," in it so I can never remember what it is. And as I don't know by heart my user id either, I use the ",{"type":254,"tag":262,"props":62244,"children":62247},{"href":62245,"rel":62246},"https://docs.microsoft.com/en-us/cli/azure/ad/user?view=azure-cli-latest#az_ad_user_show",[266],[62248],{"type":254,"tag":389,"props":62249,"children":62251},{"className":62250},[],[62252],{"type":259,"value":62253},"az ad signed-in-user show",{"type":259,"value":62255}," command to get my user id.",{"type":254,"tag":255,"props":62257,"children":62258},{},[62259],{"type":259,"value":62260},"So at the end my command to list the assigned roles to my user on the current subscription is the following:",{"type":254,"tag":612,"props":62262,"children":62264},{"className":10656,"code":62263,"language":10658,"meta":205,"style":205},"az role assignment list --assignee $(az ad signed-in-user show --query \"objectId\") --query \"[*].{role:roleDefinitionName,scope:scope}\" --include-inherited\n",[62265],{"type":254,"tag":389,"props":62266,"children":62267},{"__ignoreMap":205},[62268],{"type":254,"tag":622,"props":62269,"children":62270},{"class":624,"line":625},[62271,62276,62280,62284,62289,62293,62297,62301,62305,62309,62313,62317,62321,62325,62329,62333,62337,62341,62345,62350,62354,62358,62363,62367],{"type":254,"tag":622,"props":62272,"children":62273},{"style":1202},[62274],{"type":259,"value":62275},"az role assignment list ",{"type":254,"tag":622,"props":62277,"children":62278},{"style":629},[62279],{"type":259,"value":10675},{"type":254,"tag":622,"props":62281,"children":62282},{"style":1202},[62283],{"type":259,"value":49285},{"type":254,"tag":622,"props":62285,"children":62286},{"style":629},[62287],{"type":259,"value":62288},"$(",{"type":254,"tag":622,"props":62290,"children":62291},{"style":1202},[62292],{"type":259,"value":49164},{"type":254,"tag":622,"props":62294,"children":62295},{"style":629},[62296],{"type":259,"value":17065},{"type":254,"tag":622,"props":62298,"children":62299},{"style":1202},[62300],{"type":259,"value":49173},{"type":254,"tag":622,"props":62302,"children":62303},{"style":629},[62304],{"type":259,"value":17065},{"type":254,"tag":622,"props":62306,"children":62307},{"style":1202},[62308],{"type":259,"value":49182},{"type":254,"tag":622,"props":62310,"children":62311},{"style":629},[62312],{"type":259,"value":10675},{"type":254,"tag":622,"props":62314,"children":62315},{"style":1202},[62316],{"type":259,"value":17166},{"type":254,"tag":622,"props":62318,"children":62319},{"style":629},[62320],{"type":259,"value":652},{"type":254,"tag":622,"props":62322,"children":62323},{"style":664},[62324],{"type":259,"value":25120},{"type":254,"tag":622,"props":62326,"children":62327},{"style":629},[62328],{"type":259,"value":652},{"type":254,"tag":622,"props":62330,"children":62331},{"style":629},[62332],{"type":259,"value":2309},{"type":254,"tag":622,"props":62334,"children":62335},{"style":629},[62336],{"type":259,"value":16988},{"type":254,"tag":622,"props":62338,"children":62339},{"style":1202},[62340],{"type":259,"value":17166},{"type":254,"tag":622,"props":62342,"children":62343},{"style":629},[62344],{"type":259,"value":652},{"type":254,"tag":622,"props":62346,"children":62347},{"style":664},[62348],{"type":259,"value":62349},"[*].{role:roleDefinitionName,scope:scope}",{"type":254,"tag":622,"props":62351,"children":62352},{"style":629},[62353],{"type":259,"value":652},{"type":254,"tag":622,"props":62355,"children":62356},{"style":629},[62357],{"type":259,"value":16988},{"type":254,"tag":622,"props":62359,"children":62360},{"style":1202},[62361],{"type":259,"value":62362},"include",{"type":254,"tag":622,"props":62364,"children":62365},{"style":629},[62366],{"type":259,"value":17065},{"type":254,"tag":622,"props":62368,"children":62369},{"style":1202},[62370],{"type":259,"value":62371},"inherited\n",{"type":254,"tag":282,"props":62373,"children":62375},{"id":62374},"azure-storage-explorer-just-got-better",[62376],{"type":259,"value":62377},"Azure Storage Explorer just got better!",{"type":254,"tag":255,"props":62379,"children":62380},{},[62381,62388],{"type":254,"tag":262,"props":62382,"children":62385},{"href":62383,"rel":62384},"https://azure.microsoft.com/en-us/features/storage-explorer/",[266],[62386],{"type":259,"value":62387},"Azure Storage Explorer",{"type":259,"value":62389}," is a tool to manage your Azure cloud storage resources from your desktop. Instead of going to the Azure portal and navigating between all the panes and resources, you can do everything from this tool like viewing the tables and blobs in the storage accounts you have access to.",{"type":254,"tag":255,"props":62391,"children":62392},{},[62393],{"type":259,"value":62394},"Unfortunately, if your account had access to multiple subscriptions on multiple tenants with MFA enabled, using Azure Storage Explorer was a nightmare where you had to log in to every tenant multiple times to access any storage account. If you had the correct permissions you ended up grabbing a secret connection string to access your storage account from Azure Storage Explorer but it was not a good solution (using secrets instead of your Azure AD account to access resources is never a good idea ⛔).",{"type":254,"tag":255,"props":62396,"children":62397},{},[62398],{"type":254,"tag":1161,"props":62399,"children":62403},{"alt":62400,"className":62401,"src":62402},"Azure Storage Explorer account management section.",[1165,1166],"/posts/images/w222021tips_storageexplorer_1.png",[],{"type":254,"tag":255,"props":62405,"children":62406},{},[62407],{"type":259,"value":62408},"As you can see on the screenshot above, in v1.19.x Azure Storage Explorer account management section has been completely refreshed to allow us to control exactly the tenants and the subscriptions that we want to load. This way we only have to enter the credentials we need and Azure Storage Explorer becomes usable again! 🎉",{"type":254,"tag":255,"props":62410,"children":62411},{},[62412],{"type":259,"value":10040},{"type":254,"tag":1260,"props":62414,"children":62415},{},[62416],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":62418},[62419,62420,62421],{"id":62086,"depth":635,"text":62089},{"id":62137,"depth":635,"text":62140},{"id":62374,"depth":635,"text":62377},"content:1.posts:19.w22-2021-tips-learned-this-week.md","1.posts/19.w22-2021-tips-learned-this-week.md",{"_path":61,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":60,"description":62425,"lead":62426,"date":62427,"image":62428,"badge":62429,"tags":62430,"body":62431,"_type":1275,"_id":62646,"_source":231,"_file":62647,"_extension":1278},"In my last article about Windows Package Manager, I said that with winget I was missing \"being able to specify some parameters for a package installation (like the workload and components to install for Visual Studio 2019)\". Well, that was before I went through a few GitHub issues of the winget-cli repository that mentioned the override option.","The override option of winget install command","2021-05-31T00:00:00.000Z",{"src":58421},{"label":243},[38111,37214,31105,245],{"type":251,"children":62432,"toc":62639},[62433,62452,62458,62491,62502,62508,62513,62566,62578,62590,62617,62623,62635],{"type":254,"tag":255,"props":62434,"children":62435},{},[62436,62438,62443,62445,62451],{"type":259,"value":62437},"In my last article about Windows Package Manager, I said that with winget I was missing \"",{"type":254,"tag":19837,"props":62439,"children":62440},{},[62441],{"type":259,"value":62442},"being able to specify some parameters for a package installation (like the workload and components to install for Visual Studio 2019)",{"type":259,"value":62444},"\". Well, that was before I went through a few GitHub issues of the winget-cli repository that mentioned the ",{"type":254,"tag":389,"props":62446,"children":62448},{"className":62447},[],[62449],{"type":259,"value":62450},"override",{"type":259,"value":47825},{"type":254,"tag":282,"props":62453,"children":62455},{"id":62454},"about-the-override-option",[62456],{"type":259,"value":62457},"About the override option",{"type":254,"tag":255,"props":62459,"children":62460},{},[62461,62467,62469,62474,62476,62482,62484,62489],{"type":254,"tag":389,"props":62462,"children":62464},{"className":62463},[],[62465],{"type":259,"value":62466},"winget install",{"type":259,"value":62468}," official documentation says the ",{"type":254,"tag":389,"props":62470,"children":62472},{"className":62471},[],[62473],{"type":259,"value":62450},{"type":259,"value":62475}," option to the ",{"type":254,"tag":389,"props":62477,"children":62479},{"className":62478},[],[62480],{"type":259,"value":62481},"install",{"type":259,"value":62483}," command can be used to provide a \"",{"type":254,"tag":19837,"props":62485,"children":62486},{},[62487],{"type":259,"value":62488},"string that will be passed directly to the installer",{"type":259,"value":62490},"\". What does that mean exactly?",{"type":254,"tag":255,"props":62492,"children":62493},{},[62494,62496,62501],{"type":259,"value":62495},"When you install a program on your laptop, the installer GUI often prompts you to configure some parameters for the program. For instance, Git installer lets you choose how you want to handle CRLF. Each installer has its own parameters that you can specify on the command line if you run the installer exe in your terminal. When using winget CLI, you can also specify these parameters by passing them as the value of the option ",{"type":254,"tag":389,"props":62497,"children":62499},{"className":62498},[],[62500],{"type":259,"value":62450},{"type":259,"value":594},{"type":254,"tag":282,"props":62503,"children":62505},{"id":62504},"an-example-with-visual-studio-2019-installation",[62506],{"type":259,"value":62507},"An example with Visual Studio 2019 installation",{"type":254,"tag":255,"props":62509,"children":62510},{},[62511],{"type":259,"value":62512},"If we take the example of Visual Studio 2019, let's say we want to install it with the English language pack and the Azure workload. To do that we can run the following command:",{"type":254,"tag":612,"props":62514,"children":62516},{"className":10656,"code":62515,"language":10658,"meta":205,"style":205},"winget install Microsoft.VisualStudio.2019.Enterprise --silent --override \"--wait --quiet --addProductLang En-us --add Microsoft.VisualStudio.Workload.Azure\"\n",[62517],{"type":254,"tag":389,"props":62518,"children":62519},{"__ignoreMap":205},[62520],{"type":254,"tag":622,"props":62521,"children":62522},{"class":624,"line":625},[62523,62528,62533,62537,62541,62545,62549,62553,62557,62562],{"type":254,"tag":622,"props":62524,"children":62525},{"style":1202},[62526],{"type":259,"value":62527},"winget install Microsoft.VisualStudio.",{"type":254,"tag":622,"props":62529,"children":62530},{"style":741},[62531],{"type":259,"value":62532},"2019.",{"type":254,"tag":622,"props":62534,"children":62535},{"style":1202},[62536],{"type":259,"value":38580},{"type":254,"tag":622,"props":62538,"children":62539},{"style":629},[62540],{"type":259,"value":10675},{"type":254,"tag":622,"props":62542,"children":62543},{"style":1202},[62544],{"type":259,"value":38589},{"type":254,"tag":622,"props":62546,"children":62547},{"style":629},[62548],{"type":259,"value":10675},{"type":254,"tag":622,"props":62550,"children":62551},{"style":1202},[62552],{"type":259,"value":38598},{"type":254,"tag":622,"props":62554,"children":62555},{"style":629},[62556],{"type":259,"value":652},{"type":254,"tag":622,"props":62558,"children":62559},{"style":664},[62560],{"type":259,"value":62561},"--wait --quiet --addProductLang En-us --add Microsoft.VisualStudio.Workload.Azure",{"type":254,"tag":622,"props":62563,"children":62564},{"style":629},[62565],{"type":259,"value":9909},{"type":254,"tag":255,"props":62567,"children":62568},{},[62569,62571,62577],{"type":259,"value":62570},"You can find the documentation of the visual studio installer with the parameters I used ",{"type":254,"tag":262,"props":62572,"children":62575},{"href":62573,"rel":62574},"https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio?view=vs-2019#bootstrapper-commands-and-command-line-parameters",[266],[62576],{"type":259,"value":1414},{"type":259,"value":594},{"type":254,"tag":282,"props":62579,"children":62581},{"id":62580},"why-override",[62582,62584,62589],{"type":259,"value":62583},"Why ",{"type":254,"tag":389,"props":62585,"children":62587},{"className":62586},[],[62588],{"type":259,"value":62450},{"type":259,"value":1980},{"type":254,"tag":255,"props":62591,"children":62592},{},[62593,62595,62600,62602,62607,62609,62615],{"type":259,"value":62594},"So ",{"type":254,"tag":389,"props":62596,"children":62598},{"className":62597},[],[62599],{"type":259,"value":62450},{"type":259,"value":62601}," is a really useful option because it allows us to customize our program installations. The name ",{"type":254,"tag":389,"props":62603,"children":62605},{"className":62604},[],[62606],{"type":259,"value":62450},{"type":259,"value":62608}," could seem a bit strange but it is to indicate that we override the default parameters that could have been specified in the manifest of the package you are installing. By the way, the packages and their manifests can be found in ",{"type":254,"tag":262,"props":62610,"children":62613},{"href":62611,"rel":62612},"https://github.com/microsoft/winget-pkgs/",[266],[62614],{"type":259,"value":53777},{"type":259,"value":62616},". If you look at some of them you will see what parameters they use by default and be able to know if you need to override them to fit your needs.",{"type":254,"tag":282,"props":62618,"children":62620},{"id":62619},"pros-and-cons",[62621],{"type":259,"value":62622},"Pros and cons",{"type":254,"tag":255,"props":62624,"children":62625},{},[62626,62628,62633],{"type":259,"value":62627},"As we said, each installer has its own parameters so if we want to customize our installations we will have to look at the documentation of each installer which can be quite tiresome. If we compare to Chocolatey it is more or less the same but on Chocolatey site, each package has a page with most of the time the parameters documentation or at least a link to the documentation of the installer. Winget clearly lacks that. Yet, even if it requires a bit of effort to find the parameters of a package, thanks to the ",{"type":254,"tag":389,"props":62629,"children":62631},{"className":62630},[],[62632],{"type":259,"value":62450},{"type":259,"value":62634}," option we can fully customize its installation and that's great.",{"type":254,"tag":1260,"props":62636,"children":62637},{},[62638],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":62640},[62641,62642,62643,62645],{"id":62454,"depth":635,"text":62457},{"id":62504,"depth":635,"text":62507},{"id":62580,"depth":635,"text":62644},"Why override?",{"id":62619,"depth":635,"text":62622},"content:1.posts:18.winget-override.md","1.posts/18.winget-override.md",{"_path":58,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":57,"description":62649,"lead":62650,"date":62651,"image":62652,"badge":62653,"tags":62654,"body":62655,"_type":1275,"_id":63695,"_source":231,"_file":63696,"_extension":1278},"Using Windows Package Manager import to install multiple applications.","Winget import a promising feature","2021-05-26T00:00:00.000Z",{"src":58421},{"label":243},[38111,37214,31105,245],{"type":251,"children":62656,"toc":63689},[62657,62663,62682,62687,62701,62720,62726,62753,62770,62779,62785,62810,62815,63636,63648,63664,63669,63673,63685],{"type":254,"tag":282,"props":62658,"children":62660},{"id":62659},"about-windows-package-manager",[62661],{"type":259,"value":62662},"About Windows Package Manager",{"type":254,"tag":255,"props":62664,"children":62665},{},[62666,62668,62673,62675,62680],{"type":259,"value":62667},"You probably have already heard of the new ",{"type":254,"tag":262,"props":62669,"children":62671},{"href":38467,"rel":62670},[266],[62672],{"type":259,"value":37235},{"type":259,"value":62674}," and its command-line tool ",{"type":254,"tag":389,"props":62676,"children":62678},{"className":62677},[],[62679],{"type":259,"value":37214},{"type":259,"value":62681}," that allows you to automate installing and upgrading software on your Windows 10 computer.",{"type":254,"tag":255,"props":62683,"children":62684},{},[62685],{"type":259,"value":62686},"With winget you can install an application very easily simply by executing in your terminal a command like this one which installs PowerToys:",{"type":254,"tag":612,"props":62688,"children":62690},{"className":10656,"code":62689,"language":10658,"meta":205,"style":205},"winget install powertoys\n",[62691],{"type":254,"tag":389,"props":62692,"children":62693},{"__ignoreMap":205},[62694],{"type":254,"tag":622,"props":62695,"children":62696},{"class":624,"line":625},[62697],{"type":254,"tag":622,"props":62698,"children":62699},{"style":1202},[62700],{"type":259,"value":62689},{"type":254,"tag":255,"props":62702,"children":62703},{},[62704,62706,62711,62713,62718],{"type":259,"value":62705},"Currently, Windows Package Manager only offers basic features and has far fewer packages compared to other package managers like ",{"type":254,"tag":262,"props":62707,"children":62709},{"href":38474,"rel":62708},[266],[62710],{"type":259,"value":38478},{"type":259,"value":62712},". However, even if ",{"type":254,"tag":389,"props":62714,"children":62716},{"className":62715},[],[62717],{"type":259,"value":37214},{"type":259,"value":62719}," is still in its early days, there are some promising features that make it a tool to consider when setting up a Windows 10 machine.",{"type":254,"tag":282,"props":62721,"children":62723},{"id":62722},"installing-microsoft-store-applications",[62724],{"type":259,"value":62725},"Installing Microsoft Store applications",{"type":254,"tag":255,"props":62727,"children":62728},{},[62729,62731,62736,62738,62743,62745,62751],{"type":259,"value":62730},"First, winget can install Microsoft Store applications. Most of the software you use as a developer probably does not come from Microsoft Store, but there are still some applications that it is handy to get from it. For instance, the new Windows Terminal is available as a Microsoft Store application. As far as I know, apart from ",{"type":254,"tag":389,"props":62732,"children":62734},{"className":62733},[],[62735],{"type":259,"value":37214},{"type":259,"value":62737},", there is no other easy way to install a Microsoft Store application from the command line. With ",{"type":254,"tag":389,"props":62739,"children":62741},{"className":62740},[],[62742],{"type":259,"value":37214},{"type":259,"value":62744}," you can just do: ",{"type":254,"tag":389,"props":62746,"children":62748},{"className":62747},[],[62749],{"type":259,"value":62750},"winget install Microsoft.WindowsTerminal -s msstore",{"type":259,"value":62752}," to install the new WindowsTerminal application from Microsoft Store.",{"type":254,"tag":427,"props":62754,"children":62756},{"icon":62755},"i-fluent-emoji-flat-pushpin",[62757],{"type":254,"tag":255,"props":62758,"children":62759},{},[62760,62762,62768],{"type":259,"value":62761},"Please note that at the time of writing, installing store applications from winget is an experimental feature that you have to enable in ",{"type":254,"tag":389,"props":62763,"children":62765},{"className":62764},[],[62766],{"type":259,"value":62767},"winget settings",{"type":259,"value":62769}," and that only a subset of Microsoft Store applications can be installed.",{"type":254,"tag":255,"props":62771,"children":62772},{},[62773],{"type":254,"tag":1161,"props":62774,"children":62778},{"alt":62775,"className":62776,"src":62777},"Winget configuration file.",[1165,1166],"/posts/images/winget_import_1.png",[],{"type":254,"tag":282,"props":62780,"children":62782},{"id":62781},"installing-several-packages-with-the-import-command",[62783],{"type":259,"value":62784},"Installing several packages with the import command",{"type":254,"tag":255,"props":62786,"children":62787},{},[62788,62790,62795,62797,62802,62804],{"type":259,"value":62789},"Second, winget has an ",{"type":254,"tag":389,"props":62791,"children":62793},{"className":62792},[],[62794],{"type":259,"value":24446},{"type":259,"value":62796}," command that allows you installing all the package specified in a JSON file you pass in parameter. It means that instead of writing a script with many install commands for each of the packages you want to install, you can write a ",{"type":254,"tag":389,"props":62798,"children":62800},{"className":62799},[],[62801],{"type":259,"value":24338},{"type":259,"value":62803}," file that will contain all the packages you want to install, their version, the source of the package (place to find them, msstore for Microsoft Store applications), ... and you will be able to install the software with one command: ",{"type":254,"tag":389,"props":62805,"children":62807},{"className":62806},[],[62808],{"type":259,"value":62809},"winget import packages.json",{"type":254,"tag":255,"props":62811,"children":62812},{},[62813],{"type":259,"value":62814},"Here is an example of such a file:",{"type":254,"tag":612,"props":62816,"children":62818},{"className":614,"code":62817,"language":616,"meta":205,"style":205},"{\n    \"$schema\" : \"https://aka.ms/winget-packages.schema.2.0.json\",\n    \"CreationDate\" : \"2021-05-23T14:41:38.200-00:00\",\n    \"Sources\" : \n    [\n        {\n            \"Packages\" : \n            [\n                {\n                    \"PackageIdentifier\" : \"Microsoft.Whiteboard\"\n                },\n                {\n                    \"PackageIdentifier\" : \"Microsoft.WindowsTerminal\"   \n                }\n            ],\n            \"SourceDetails\" : \n            {\n                \"Argument\" : \"https://winget.azureedge.net/msstore\",\n                \"Identifier\" : \"Microsoft.Winget.MSStore.Source_8wekyb3d8bbwe\",\n                \"Name\" : \"msstore\",\n                \"Type\" : \"Microsoft.PreIndexed.Package\"\n            }\n        },\n        {\n            \"Packages\" : \n            [\n                {\n                    \"PackageIdentifier\": \"Microsoft.AzureCLI\"\n                },\n                {\n                    \"PackageIdentifier\" : \"Microsoft.PowerToys\"\n                }\n            ],\n            \"SourceDetails\" : \n            {\n                \"Argument\" : \"https://winget.azureedge.net/cache\",\n                \"Identifier\" : \"Microsoft.Winget.Source_8wekyb3d8bbwe\",\n                \"Name\" : \"winget\",\n                \"Type\" : \"Microsoft.PreIndexed.Package\"\n            }\n        }\n    ],\n    \"WinGetVersion\" : \"0.4.11391\"\n}\n",[62819],{"type":254,"tag":389,"props":62820,"children":62821},{"__ignoreMap":205},[62822,62829,62865,62902,62926,62934,62941,62966,62974,62981,63015,63023,63030,63066,63074,63082,63106,63113,63151,63188,63224,63256,63263,63270,63277,63300,63307,63314,63346,63353,63360,63392,63399,63406,63429,63436,63472,63508,63543,63574,63581,63588,63596,63629],{"type":254,"tag":622,"props":62823,"children":62824},{"class":624,"line":625},[62825],{"type":254,"tag":622,"props":62826,"children":62827},{"style":629},[62828],{"type":259,"value":632},{"type":254,"tag":622,"props":62830,"children":62831},{"class":624,"line":635},[62832,62836,62840,62844,62848,62852,62857,62861],{"type":254,"tag":622,"props":62833,"children":62834},{"style":629},[62835],{"type":259,"value":711},{"type":254,"tag":622,"props":62837,"children":62838},{"style":644},[62839],{"type":259,"value":62105},{"type":254,"tag":622,"props":62841,"children":62842},{"style":629},[62843],{"type":259,"value":652},{"type":254,"tag":622,"props":62845,"children":62846},{"style":629},[62847],{"type":259,"value":3274},{"type":254,"tag":622,"props":62849,"children":62850},{"style":629},[62851],{"type":259,"value":661},{"type":254,"tag":622,"props":62853,"children":62854},{"style":664},[62855],{"type":259,"value":62856},"https://aka.ms/winget-packages.schema.2.0.json",{"type":254,"tag":622,"props":62858,"children":62859},{"style":629},[62860],{"type":259,"value":652},{"type":254,"tag":622,"props":62862,"children":62863},{"style":629},[62864],{"type":259,"value":676},{"type":254,"tag":622,"props":62866,"children":62867},{"class":624,"line":679},[62868,62872,62877,62881,62885,62889,62894,62898],{"type":254,"tag":622,"props":62869,"children":62870},{"style":629},[62871],{"type":259,"value":711},{"type":254,"tag":622,"props":62873,"children":62874},{"style":644},[62875],{"type":259,"value":62876},"CreationDate",{"type":254,"tag":622,"props":62878,"children":62879},{"style":629},[62880],{"type":259,"value":652},{"type":254,"tag":622,"props":62882,"children":62883},{"style":629},[62884],{"type":259,"value":3274},{"type":254,"tag":622,"props":62886,"children":62887},{"style":629},[62888],{"type":259,"value":661},{"type":254,"tag":622,"props":62890,"children":62891},{"style":664},[62892],{"type":259,"value":62893},"2021-05-23T14:41:38.200-00:00",{"type":254,"tag":622,"props":62895,"children":62896},{"style":629},[62897],{"type":259,"value":652},{"type":254,"tag":622,"props":62899,"children":62900},{"style":629},[62901],{"type":259,"value":676},{"type":254,"tag":622,"props":62903,"children":62904},{"class":624,"line":705},[62905,62909,62914,62918,62922],{"type":254,"tag":622,"props":62906,"children":62907},{"style":629},[62908],{"type":259,"value":711},{"type":254,"tag":622,"props":62910,"children":62911},{"style":644},[62912],{"type":259,"value":62913},"Sources",{"type":254,"tag":622,"props":62915,"children":62916},{"style":629},[62917],{"type":259,"value":652},{"type":254,"tag":622,"props":62919,"children":62920},{"style":629},[62921],{"type":259,"value":3274},{"type":254,"tag":622,"props":62923,"children":62924},{"style":1202},[62925],{"type":259,"value":2314},{"type":254,"tag":622,"props":62927,"children":62928},{"class":624,"line":732},[62929],{"type":254,"tag":622,"props":62930,"children":62931},{"style":629},[62932],{"type":259,"value":62933},"    [\n",{"type":254,"tag":622,"props":62935,"children":62936},{"class":624,"line":759},[62937],{"type":254,"tag":622,"props":62938,"children":62939},{"style":629},[62940],{"type":259,"value":7094},{"type":254,"tag":622,"props":62942,"children":62943},{"class":624,"line":798},[62944,62949,62954,62958,62962],{"type":254,"tag":622,"props":62945,"children":62946},{"style":629},[62947],{"type":259,"value":62948},"            \"",{"type":254,"tag":622,"props":62950,"children":62951},{"style":714},[62952],{"type":259,"value":62953},"Packages",{"type":254,"tag":622,"props":62955,"children":62956},{"style":629},[62957],{"type":259,"value":652},{"type":254,"tag":622,"props":62959,"children":62960},{"style":629},[62961],{"type":259,"value":3274},{"type":254,"tag":622,"props":62963,"children":62964},{"style":1202},[62965],{"type":259,"value":2314},{"type":254,"tag":622,"props":62967,"children":62968},{"class":624,"line":836},[62969],{"type":254,"tag":622,"props":62970,"children":62971},{"style":629},[62972],{"type":259,"value":62973},"            [\n",{"type":254,"tag":622,"props":62975,"children":62976},{"class":624,"line":862},[62977],{"type":254,"tag":622,"props":62978,"children":62979},{"style":629},[62980],{"type":259,"value":12482},{"type":254,"tag":622,"props":62982,"children":62983},{"class":624,"line":900},[62984,62989,62994,62998,63002,63006,63011],{"type":254,"tag":622,"props":62985,"children":62986},{"style":629},[62987],{"type":259,"value":62988},"                    \"",{"type":254,"tag":622,"props":62990,"children":62991},{"style":741},[62992],{"type":259,"value":62993},"PackageIdentifier",{"type":254,"tag":622,"props":62995,"children":62996},{"style":629},[62997],{"type":259,"value":652},{"type":254,"tag":622,"props":62999,"children":63000},{"style":629},[63001],{"type":259,"value":3274},{"type":254,"tag":622,"props":63003,"children":63004},{"style":629},[63005],{"type":259,"value":661},{"type":254,"tag":622,"props":63007,"children":63008},{"style":664},[63009],{"type":259,"value":63010},"Microsoft.Whiteboard",{"type":254,"tag":622,"props":63012,"children":63013},{"style":629},[63014],{"type":259,"value":9909},{"type":254,"tag":622,"props":63016,"children":63017},{"class":624,"line":938},[63018],{"type":254,"tag":622,"props":63019,"children":63020},{"style":629},[63021],{"type":259,"value":63022},"                },\n",{"type":254,"tag":622,"props":63024,"children":63025},{"class":624,"line":976},[63026],{"type":254,"tag":622,"props":63027,"children":63028},{"style":629},[63029],{"type":259,"value":12482},{"type":254,"tag":622,"props":63031,"children":63032},{"class":624,"line":1014},[63033,63037,63041,63045,63049,63053,63058,63062],{"type":254,"tag":622,"props":63034,"children":63035},{"style":629},[63036],{"type":259,"value":62988},{"type":254,"tag":622,"props":63038,"children":63039},{"style":741},[63040],{"type":259,"value":62993},{"type":254,"tag":622,"props":63042,"children":63043},{"style":629},[63044],{"type":259,"value":652},{"type":254,"tag":622,"props":63046,"children":63047},{"style":629},[63048],{"type":259,"value":3274},{"type":254,"tag":622,"props":63050,"children":63051},{"style":629},[63052],{"type":259,"value":661},{"type":254,"tag":622,"props":63054,"children":63055},{"style":664},[63056],{"type":259,"value":63057},"Microsoft.WindowsTerminal",{"type":254,"tag":622,"props":63059,"children":63060},{"style":629},[63061],{"type":259,"value":652},{"type":254,"tag":622,"props":63063,"children":63064},{"style":1202},[63065],{"type":259,"value":35234},{"type":254,"tag":622,"props":63067,"children":63068},{"class":624,"line":1052},[63069],{"type":254,"tag":622,"props":63070,"children":63071},{"style":629},[63072],{"type":259,"value":63073},"                }\n",{"type":254,"tag":622,"props":63075,"children":63076},{"class":624,"line":1078},[63077],{"type":254,"tag":622,"props":63078,"children":63079},{"style":629},[63080],{"type":259,"value":63081},"            ],\n",{"type":254,"tag":622,"props":63083,"children":63084},{"class":624,"line":1087},[63085,63089,63094,63098,63102],{"type":254,"tag":622,"props":63086,"children":63087},{"style":629},[63088],{"type":259,"value":62948},{"type":254,"tag":622,"props":63090,"children":63091},{"style":714},[63092],{"type":259,"value":63093},"SourceDetails",{"type":254,"tag":622,"props":63095,"children":63096},{"style":629},[63097],{"type":259,"value":652},{"type":254,"tag":622,"props":63099,"children":63100},{"style":629},[63101],{"type":259,"value":3274},{"type":254,"tag":622,"props":63103,"children":63104},{"style":1202},[63105],{"type":259,"value":2314},{"type":254,"tag":622,"props":63107,"children":63108},{"class":624,"line":1096},[63109],{"type":254,"tag":622,"props":63110,"children":63111},{"style":629},[63112],{"type":259,"value":11957},{"type":254,"tag":622,"props":63114,"children":63115},{"class":624,"line":1105},[63116,63121,63126,63130,63134,63138,63143,63147],{"type":254,"tag":622,"props":63117,"children":63118},{"style":629},[63119],{"type":259,"value":63120},"                \"",{"type":254,"tag":622,"props":63122,"children":63123},{"style":741},[63124],{"type":259,"value":63125},"Argument",{"type":254,"tag":622,"props":63127,"children":63128},{"style":629},[63129],{"type":259,"value":652},{"type":254,"tag":622,"props":63131,"children":63132},{"style":629},[63133],{"type":259,"value":3274},{"type":254,"tag":622,"props":63135,"children":63136},{"style":629},[63137],{"type":259,"value":661},{"type":254,"tag":622,"props":63139,"children":63140},{"style":664},[63141],{"type":259,"value":63142},"https://winget.azureedge.net/msstore",{"type":254,"tag":622,"props":63144,"children":63145},{"style":629},[63146],{"type":259,"value":652},{"type":254,"tag":622,"props":63148,"children":63149},{"style":629},[63150],{"type":259,"value":676},{"type":254,"tag":622,"props":63152,"children":63153},{"class":624,"line":4131},[63154,63158,63163,63167,63171,63175,63180,63184],{"type":254,"tag":622,"props":63155,"children":63156},{"style":629},[63157],{"type":259,"value":63120},{"type":254,"tag":622,"props":63159,"children":63160},{"style":741},[63161],{"type":259,"value":63162},"Identifier",{"type":254,"tag":622,"props":63164,"children":63165},{"style":629},[63166],{"type":259,"value":652},{"type":254,"tag":622,"props":63168,"children":63169},{"style":629},[63170],{"type":259,"value":3274},{"type":254,"tag":622,"props":63172,"children":63173},{"style":629},[63174],{"type":259,"value":661},{"type":254,"tag":622,"props":63176,"children":63177},{"style":664},[63178],{"type":259,"value":63179},"Microsoft.Winget.MSStore.Source_8wekyb3d8bbwe",{"type":254,"tag":622,"props":63181,"children":63182},{"style":629},[63183],{"type":259,"value":652},{"type":254,"tag":622,"props":63185,"children":63186},{"style":629},[63187],{"type":259,"value":676},{"type":254,"tag":622,"props":63189,"children":63190},{"class":624,"line":4183},[63191,63195,63199,63203,63207,63211,63216,63220],{"type":254,"tag":622,"props":63192,"children":63193},{"style":629},[63194],{"type":259,"value":63120},{"type":254,"tag":622,"props":63196,"children":63197},{"style":741},[63198],{"type":259,"value":2254},{"type":254,"tag":622,"props":63200,"children":63201},{"style":629},[63202],{"type":259,"value":652},{"type":254,"tag":622,"props":63204,"children":63205},{"style":629},[63206],{"type":259,"value":3274},{"type":254,"tag":622,"props":63208,"children":63209},{"style":629},[63210],{"type":259,"value":661},{"type":254,"tag":622,"props":63212,"children":63213},{"style":664},[63214],{"type":259,"value":63215},"msstore",{"type":254,"tag":622,"props":63217,"children":63218},{"style":629},[63219],{"type":259,"value":652},{"type":254,"tag":622,"props":63221,"children":63222},{"style":629},[63223],{"type":259,"value":676},{"type":254,"tag":622,"props":63225,"children":63226},{"class":624,"line":4208},[63227,63231,63235,63239,63243,63247,63252],{"type":254,"tag":622,"props":63228,"children":63229},{"style":629},[63230],{"type":259,"value":63120},{"type":254,"tag":622,"props":63232,"children":63233},{"style":741},[63234],{"type":259,"value":771},{"type":254,"tag":622,"props":63236,"children":63237},{"style":629},[63238],{"type":259,"value":652},{"type":254,"tag":622,"props":63240,"children":63241},{"style":629},[63242],{"type":259,"value":3274},{"type":254,"tag":622,"props":63244,"children":63245},{"style":629},[63246],{"type":259,"value":661},{"type":254,"tag":622,"props":63248,"children":63249},{"style":664},[63250],{"type":259,"value":63251},"Microsoft.PreIndexed.Package",{"type":254,"tag":622,"props":63253,"children":63254},{"style":629},[63255],{"type":259,"value":9909},{"type":254,"tag":622,"props":63257,"children":63258},{"class":624,"line":4216},[63259],{"type":254,"tag":622,"props":63260,"children":63261},{"style":629},[63262],{"type":259,"value":57346},{"type":254,"tag":622,"props":63264,"children":63265},{"class":624,"line":4224},[63266],{"type":254,"tag":622,"props":63267,"children":63268},{"style":629},[63269],{"type":259,"value":57354},{"type":254,"tag":622,"props":63271,"children":63272},{"class":624,"line":4268},[63273],{"type":254,"tag":622,"props":63274,"children":63275},{"style":629},[63276],{"type":259,"value":7094},{"type":254,"tag":622,"props":63278,"children":63279},{"class":624,"line":4276},[63280,63284,63288,63292,63296],{"type":254,"tag":622,"props":63281,"children":63282},{"style":629},[63283],{"type":259,"value":62948},{"type":254,"tag":622,"props":63285,"children":63286},{"style":714},[63287],{"type":259,"value":62953},{"type":254,"tag":622,"props":63289,"children":63290},{"style":629},[63291],{"type":259,"value":652},{"type":254,"tag":622,"props":63293,"children":63294},{"style":629},[63295],{"type":259,"value":3274},{"type":254,"tag":622,"props":63297,"children":63298},{"style":1202},[63299],{"type":259,"value":2314},{"type":254,"tag":622,"props":63301,"children":63302},{"class":624,"line":4332},[63303],{"type":254,"tag":622,"props":63304,"children":63305},{"style":629},[63306],{"type":259,"value":62973},{"type":254,"tag":622,"props":63308,"children":63309},{"class":624,"line":4376},[63310],{"type":254,"tag":622,"props":63311,"children":63312},{"style":629},[63313],{"type":259,"value":12482},{"type":254,"tag":622,"props":63315,"children":63316},{"class":624,"line":4384},[63317,63321,63325,63329,63333,63337,63342],{"type":254,"tag":622,"props":63318,"children":63319},{"style":629},[63320],{"type":259,"value":62988},{"type":254,"tag":622,"props":63322,"children":63323},{"style":741},[63324],{"type":259,"value":62993},{"type":254,"tag":622,"props":63326,"children":63327},{"style":629},[63328],{"type":259,"value":652},{"type":254,"tag":622,"props":63330,"children":63331},{"style":629},[63332],{"type":259,"value":474},{"type":254,"tag":622,"props":63334,"children":63335},{"style":629},[63336],{"type":259,"value":661},{"type":254,"tag":622,"props":63338,"children":63339},{"style":664},[63340],{"type":259,"value":63341},"Microsoft.AzureCLI",{"type":254,"tag":622,"props":63343,"children":63344},{"style":629},[63345],{"type":259,"value":9909},{"type":254,"tag":622,"props":63347,"children":63348},{"class":624,"line":4416},[63349],{"type":254,"tag":622,"props":63350,"children":63351},{"style":629},[63352],{"type":259,"value":63022},{"type":254,"tag":622,"props":63354,"children":63355},{"class":624,"line":4456},[63356],{"type":254,"tag":622,"props":63357,"children":63358},{"style":629},[63359],{"type":259,"value":12482},{"type":254,"tag":622,"props":63361,"children":63362},{"class":624,"line":4488},[63363,63367,63371,63375,63379,63383,63388],{"type":254,"tag":622,"props":63364,"children":63365},{"style":629},[63366],{"type":259,"value":62988},{"type":254,"tag":622,"props":63368,"children":63369},{"style":741},[63370],{"type":259,"value":62993},{"type":254,"tag":622,"props":63372,"children":63373},{"style":629},[63374],{"type":259,"value":652},{"type":254,"tag":622,"props":63376,"children":63377},{"style":629},[63378],{"type":259,"value":3274},{"type":254,"tag":622,"props":63380,"children":63381},{"style":629},[63382],{"type":259,"value":661},{"type":254,"tag":622,"props":63384,"children":63385},{"style":664},[63386],{"type":259,"value":63387},"Microsoft.PowerToys",{"type":254,"tag":622,"props":63389,"children":63390},{"style":629},[63391],{"type":259,"value":9909},{"type":254,"tag":622,"props":63393,"children":63394},{"class":624,"line":4496},[63395],{"type":254,"tag":622,"props":63396,"children":63397},{"style":629},[63398],{"type":259,"value":63073},{"type":254,"tag":622,"props":63400,"children":63401},{"class":624,"line":4564},[63402],{"type":254,"tag":622,"props":63403,"children":63404},{"style":629},[63405],{"type":259,"value":63081},{"type":254,"tag":622,"props":63407,"children":63408},{"class":624,"line":4572},[63409,63413,63417,63421,63425],{"type":254,"tag":622,"props":63410,"children":63411},{"style":629},[63412],{"type":259,"value":62948},{"type":254,"tag":622,"props":63414,"children":63415},{"style":714},[63416],{"type":259,"value":63093},{"type":254,"tag":622,"props":63418,"children":63419},{"style":629},[63420],{"type":259,"value":652},{"type":254,"tag":622,"props":63422,"children":63423},{"style":629},[63424],{"type":259,"value":3274},{"type":254,"tag":622,"props":63426,"children":63427},{"style":1202},[63428],{"type":259,"value":2314},{"type":254,"tag":622,"props":63430,"children":63431},{"class":624,"line":4644},[63432],{"type":254,"tag":622,"props":63433,"children":63434},{"style":629},[63435],{"type":259,"value":11957},{"type":254,"tag":622,"props":63437,"children":63438},{"class":624,"line":4652},[63439,63443,63447,63451,63455,63459,63464,63468],{"type":254,"tag":622,"props":63440,"children":63441},{"style":629},[63442],{"type":259,"value":63120},{"type":254,"tag":622,"props":63444,"children":63445},{"style":741},[63446],{"type":259,"value":63125},{"type":254,"tag":622,"props":63448,"children":63449},{"style":629},[63450],{"type":259,"value":652},{"type":254,"tag":622,"props":63452,"children":63453},{"style":629},[63454],{"type":259,"value":3274},{"type":254,"tag":622,"props":63456,"children":63457},{"style":629},[63458],{"type":259,"value":661},{"type":254,"tag":622,"props":63460,"children":63461},{"style":664},[63462],{"type":259,"value":63463},"https://winget.azureedge.net/cache",{"type":254,"tag":622,"props":63465,"children":63466},{"style":629},[63467],{"type":259,"value":652},{"type":254,"tag":622,"props":63469,"children":63470},{"style":629},[63471],{"type":259,"value":676},{"type":254,"tag":622,"props":63473,"children":63474},{"class":624,"line":4660},[63475,63479,63483,63487,63491,63495,63500,63504],{"type":254,"tag":622,"props":63476,"children":63477},{"style":629},[63478],{"type":259,"value":63120},{"type":254,"tag":622,"props":63480,"children":63481},{"style":741},[63482],{"type":259,"value":63162},{"type":254,"tag":622,"props":63484,"children":63485},{"style":629},[63486],{"type":259,"value":652},{"type":254,"tag":622,"props":63488,"children":63489},{"style":629},[63490],{"type":259,"value":3274},{"type":254,"tag":622,"props":63492,"children":63493},{"style":629},[63494],{"type":259,"value":661},{"type":254,"tag":622,"props":63496,"children":63497},{"style":664},[63498],{"type":259,"value":63499},"Microsoft.Winget.Source_8wekyb3d8bbwe",{"type":254,"tag":622,"props":63501,"children":63502},{"style":629},[63503],{"type":259,"value":652},{"type":254,"tag":622,"props":63505,"children":63506},{"style":629},[63507],{"type":259,"value":676},{"type":254,"tag":622,"props":63509,"children":63510},{"class":624,"line":4692},[63511,63515,63519,63523,63527,63531,63535,63539],{"type":254,"tag":622,"props":63512,"children":63513},{"style":629},[63514],{"type":259,"value":63120},{"type":254,"tag":622,"props":63516,"children":63517},{"style":741},[63518],{"type":259,"value":2254},{"type":254,"tag":622,"props":63520,"children":63521},{"style":629},[63522],{"type":259,"value":652},{"type":254,"tag":622,"props":63524,"children":63525},{"style":629},[63526],{"type":259,"value":3274},{"type":254,"tag":622,"props":63528,"children":63529},{"style":629},[63530],{"type":259,"value":661},{"type":254,"tag":622,"props":63532,"children":63533},{"style":664},[63534],{"type":259,"value":37214},{"type":254,"tag":622,"props":63536,"children":63537},{"style":629},[63538],{"type":259,"value":652},{"type":254,"tag":622,"props":63540,"children":63541},{"style":629},[63542],{"type":259,"value":676},{"type":254,"tag":622,"props":63544,"children":63545},{"class":624,"line":4700},[63546,63550,63554,63558,63562,63566,63570],{"type":254,"tag":622,"props":63547,"children":63548},{"style":629},[63549],{"type":259,"value":63120},{"type":254,"tag":622,"props":63551,"children":63552},{"style":741},[63553],{"type":259,"value":771},{"type":254,"tag":622,"props":63555,"children":63556},{"style":629},[63557],{"type":259,"value":652},{"type":254,"tag":622,"props":63559,"children":63560},{"style":629},[63561],{"type":259,"value":3274},{"type":254,"tag":622,"props":63563,"children":63564},{"style":629},[63565],{"type":259,"value":661},{"type":254,"tag":622,"props":63567,"children":63568},{"style":664},[63569],{"type":259,"value":63251},{"type":254,"tag":622,"props":63571,"children":63572},{"style":629},[63573],{"type":259,"value":9909},{"type":254,"tag":622,"props":63575,"children":63576},{"class":624,"line":4756},[63577],{"type":254,"tag":622,"props":63578,"children":63579},{"style":629},[63580],{"type":259,"value":57346},{"type":254,"tag":622,"props":63582,"children":63583},{"class":624,"line":4808},[63584],{"type":254,"tag":622,"props":63585,"children":63586},{"style":629},[63587],{"type":259,"value":7144},{"type":254,"tag":622,"props":63589,"children":63590},{"class":624,"line":4864},[63591],{"type":254,"tag":622,"props":63592,"children":63593},{"style":629},[63594],{"type":259,"value":63595},"    ],\n",{"type":254,"tag":622,"props":63597,"children":63598},{"class":624,"line":4892},[63599,63603,63608,63612,63616,63620,63625],{"type":254,"tag":622,"props":63600,"children":63601},{"style":629},[63602],{"type":259,"value":711},{"type":254,"tag":622,"props":63604,"children":63605},{"style":644},[63606],{"type":259,"value":63607},"WinGetVersion",{"type":254,"tag":622,"props":63609,"children":63610},{"style":629},[63611],{"type":259,"value":652},{"type":254,"tag":622,"props":63613,"children":63614},{"style":629},[63615],{"type":259,"value":3274},{"type":254,"tag":622,"props":63617,"children":63618},{"style":629},[63619],{"type":259,"value":661},{"type":254,"tag":622,"props":63621,"children":63622},{"style":664},[63623],{"type":259,"value":63624},"0.4.11391",{"type":254,"tag":622,"props":63626,"children":63627},{"style":629},[63628],{"type":259,"value":9909},{"type":254,"tag":622,"props":63630,"children":63631},{"class":624,"line":4900},[63632],{"type":254,"tag":622,"props":63633,"children":63634},{"style":629},[63635],{"type":259,"value":1111},{"type":254,"tag":255,"props":63637,"children":63638},{},[63639,63641,63646],{"type":259,"value":63640},"It contains packages from Microsoft Store and packages from the ",{"type":254,"tag":389,"props":63642,"children":63644},{"className":63643},[],[63645],{"type":259,"value":37214},{"type":259,"value":63647}," package repository hence the 2 arrays of packages (one by each source).",{"type":254,"tag":427,"props":63649,"children":63650},{"icon":62755},[63651],{"type":254,"tag":255,"props":63652,"children":63653},{},[63654,63656,63662],{"type":259,"value":63655},"You can easily edit this JSON in vscode with autocompletion thanks to the link to the JSON ",{"type":254,"tag":389,"props":63657,"children":63659},{"className":63658},[],[63660],{"type":259,"value":63661},"schema",{"type":259,"value":63663}," at the beginning of the JSON.",{"type":254,"tag":255,"props":63665,"children":63666},{},[63667],{"type":259,"value":63668},"Import is great but there are still things missing like the ability to silently install the applications which is possible with the install command.",{"type":254,"tag":282,"props":63670,"children":63671},{"id":6186},[63672],{"type":259,"value":37936},{"type":254,"tag":255,"props":63674,"children":63675},{},[63676,63678,63683],{"type":259,"value":63677},"Chocolatey will continue to be my main package manager for now: on the one hand for the number of packages available and on the other hand for being able to specify some parameters for a package installation (like the workload and components to install for Visual Studio 2019). Yet, ",{"type":254,"tag":389,"props":63679,"children":63681},{"className":63680},[],[63682],{"type":259,"value":37214},{"type":259,"value":63684}," will be part of my toolbox as well to install some packages (including Microsoft Store applications) and I expect it to continue to get better and better.",{"type":254,"tag":1260,"props":63686,"children":63687},{},[63688],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":63690},[63691,63692,63693,63694],{"id":62659,"depth":635,"text":62662},{"id":62722,"depth":635,"text":62725},{"id":62781,"depth":635,"text":62784},{"id":6186,"depth":635,"text":37936},"content:1.posts:17.winget-import.md","1.posts/17.winget-import.md",{"_path":55,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":54,"description":63698,"lead":63699,"date":63700,"image":63701,"badge":63703,"tags":63704,"body":63707,"_type":1275,"_id":64984,"_source":231,"_file":64985,"_extension":1278},"In this article, I want to talk about a few things in .NET such as HTTP requests with an Http Client, HTTP message handlers, records... For the theoretical aspect of these topics, I think the official documentation on docs.microsoft.com and many blog articles already explain them very well, better than I could ever do. But what I am interested in here is to talk about these topics through a case study.","A story about records, HTTP message handlers, HTTP client extensions...","2021-04-24T00:00:00.000Z",{"src":63702},"/images/western_1.jpg",{"label":1286},[10627,246,63705,37463,63706],"records","refit",{"type":251,"children":63708,"toc":64970},[63709,63713,63719,63750,63847,63853,63866,63880,63959,63979,64397,64423,64432,64445,64454,64502,64511,64523,64547,64556,64561,64567,64595,64604,64616,64621,64629,64662,64670,64703,64711,64723,64728,64739,64745,64773,64782,64794,64808,64813,64832,64841,64852,64866,64887,64896,64916,64925,64937,64946,64957,64961,64966],{"type":254,"tag":255,"props":63710,"children":63711},{},[63712],{"type":259,"value":63698},{"type":254,"tag":282,"props":63714,"children":63716},{"id":63715},"introducing-the-case-study",[63717],{"type":259,"value":63718},"Introducing the case study",{"type":254,"tag":255,"props":63720,"children":63721},{},[63722,63724,63735,63737,63748],{"type":259,"value":63723},"I wrote a very basic ASP.NET Core API ",{"type":254,"tag":262,"props":63725,"children":63728},{"href":63726,"rel":63727},"https://github.com/TechWatching/MyLotrApi/tree/a70b6f91fcbcc30a3c3a3616799e3e85817b7906",[266],[63729],{"type":254,"tag":389,"props":63730,"children":63732},{"className":63731},[],[63733],{"type":259,"value":63734},"MyLotrApi",{"type":259,"value":63736}," that exposes some data from the \"Lord of the Rings\" universe. This API calls another existing API ",{"type":254,"tag":262,"props":63738,"children":63741},{"href":63739,"rel":63740},"https://the-one-api.dev/",[266],[63742],{"type":254,"tag":389,"props":63743,"children":63745},{"className":63744},[],[63746],{"type":259,"value":63747},"The One API",{"type":259,"value":63749}," to retrieve this data. The code is quite simple:",{"type":254,"tag":332,"props":63751,"children":63752},{},[63753,63791,63834],{"type":254,"tag":336,"props":63754,"children":63755},{},[63756,63758,63764,63766],{"type":259,"value":63757},"a controller ",{"type":254,"tag":389,"props":63759,"children":63761},{"className":63760},[],[63762],{"type":259,"value":63763},"LotrController",{"type":259,"value":63765}," with 2 routes\n",{"type":254,"tag":332,"props":63767,"children":63768},{},[63769,63780],{"type":254,"tag":336,"props":63770,"children":63771},{},[63772,63778],{"type":254,"tag":389,"props":63773,"children":63775},{"className":63774},[],[63776],{"type":259,"value":63777},"GET /popularmovies",{"type":259,"value":63779}," that returns the movies in the \"Lord of the Rings\" universe with a rotten tomatoes score above 80",{"type":254,"tag":336,"props":63781,"children":63782},{},[63783,63789],{"type":254,"tag":389,"props":63784,"children":63786},{"className":63785},[],[63787],{"type":259,"value":63788},"GET /famousorcs",{"type":259,"value":63790}," that returns orc characters from the \"Lord of the Rings\" universe",{"type":254,"tag":336,"props":63792,"children":63793},{},[63794,63796,63802,63804],{"type":259,"value":63795},"a service ",{"type":254,"tag":389,"props":63797,"children":63799},{"className":63798},[],[63800],{"type":259,"value":63801},"TheOneApiService",{"type":259,"value":63803}," that\n",{"type":254,"tag":332,"props":63805,"children":63806},{},[63807,63817,63829],{"type":254,"tag":336,"props":63808,"children":63809},{},[63810,63812],{"type":259,"value":63811},"makes the HTTP calls to the API ",{"type":254,"tag":389,"props":63813,"children":63815},{"className":63814},[],[63816],{"type":259,"value":63747},{"type":254,"tag":336,"props":63818,"children":63819},{},[63820,63822,63827],{"type":259,"value":63821},"has one method for each endpoint of the API ",{"type":254,"tag":389,"props":63823,"children":63825},{"className":63824},[],[63826],{"type":259,"value":63747},{"type":259,"value":63828}," that is used",{"type":254,"tag":336,"props":63830,"children":63831},{},[63832],{"type":259,"value":63833},"uses NewtonSoft for deserializing responses",{"type":254,"tag":336,"props":63835,"children":63836},{},[63837,63839,63845],{"type":259,"value":63838},"a ",{"type":254,"tag":389,"props":63840,"children":63842},{"className":63841},[],[63843],{"type":259,"value":63844},"Models",{"type":259,"value":63846}," class that contains the different data models used by the API",{"type":254,"tag":282,"props":63848,"children":63850},{"id":63849},"about-using-records",[63851],{"type":259,"value":63852},"About using records",{"type":254,"tag":255,"props":63854,"children":63855},{},[63856,63858,63864],{"type":259,"value":63857},"Instead of using basic C# classes for the models in this API, I used ",{"type":254,"tag":262,"props":63859,"children":63862},{"href":63860,"rel":63861},"https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#record-types",[266],[63863],{"type":259,"value":63705},{"type":259,"value":63865},". Many people are talking about records nowadays because it is one of the latest trendy features of C# 9. Unfortunately, that makes other people think records are just another syntactic sugar added to C# that they do not need to use in their code. Yet, there are a lot of benefits in using records.",{"type":254,"tag":255,"props":63867,"children":63868},{},[63869,63871,63878],{"type":259,"value":63870},"In my sample, I declared my models with the ",{"type":254,"tag":262,"props":63872,"children":63875},{"href":63873,"rel":63874},"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record#positional-syntax-for-property-definition",[266],[63876],{"type":259,"value":63877},"positional syntax for property definition",{"type":259,"value":63879}," which is very concise. Conciseness might not be something important for you but for me, it means fewer lines of code to write and to maintain and more clearness.",{"type":254,"tag":612,"props":63881,"children":63883},{"className":1423,"code":63882,"language":1425,"meta":205,"style":205},"public record Movie(string Name, int RuntimeInMinutes, int BudgetInMillions, float RottenTomatesScore);\n",[63884],{"type":254,"tag":389,"props":63885,"children":63886},{"__ignoreMap":205},[63887],{"type":254,"tag":622,"props":63888,"children":63889},{"class":624,"line":625},[63890,63894,63898,63903,63907,63911,63915,63919,63923,63928,63932,63936,63941,63945,63950,63955],{"type":254,"tag":622,"props":63891,"children":63892},{"style":644},[63893],{"type":259,"value":1437},{"type":254,"tag":622,"props":63895,"children":63896},{"style":714},[63897],{"type":259,"value":1442},{"type":254,"tag":622,"props":63899,"children":63900},{"style":714},[63901],{"type":259,"value":63902}," Movie",{"type":254,"tag":622,"props":63904,"children":63905},{"style":629},[63906],{"type":259,"value":1668},{"type":254,"tag":622,"props":63908,"children":63909},{"style":629},[63910],{"type":259,"value":2851},{"type":254,"tag":622,"props":63912,"children":63913},{"style":714},[63914],{"type":259,"value":1465},{"type":254,"tag":622,"props":63916,"children":63917},{"style":629},[63918],{"type":259,"value":3149},{"type":254,"tag":622,"props":63920,"children":63921},{"style":629},[63922],{"type":259,"value":14190},{"type":254,"tag":622,"props":63924,"children":63925},{"style":714},[63926],{"type":259,"value":63927}," RuntimeInMinutes",{"type":254,"tag":622,"props":63929,"children":63930},{"style":629},[63931],{"type":259,"value":3149},{"type":254,"tag":622,"props":63933,"children":63934},{"style":629},[63935],{"type":259,"value":14190},{"type":254,"tag":622,"props":63937,"children":63938},{"style":714},[63939],{"type":259,"value":63940}," BudgetInMillions",{"type":254,"tag":622,"props":63942,"children":63943},{"style":629},[63944],{"type":259,"value":3149},{"type":254,"tag":622,"props":63946,"children":63947},{"style":629},[63948],{"type":259,"value":63949}," float",{"type":254,"tag":622,"props":63951,"children":63952},{"style":714},[63953],{"type":259,"value":63954}," RottenTomatesScore",{"type":254,"tag":622,"props":63956,"children":63957},{"style":629},[63958],{"type":259,"value":1554},{"type":254,"tag":255,"props":63960,"children":63961},{},[63962,63964,63970,63972,63977],{"type":259,"value":63963},"You can notice that I put my records in one place (the ",{"type":254,"tag":389,"props":63965,"children":63967},{"className":63966},[],[63968],{"type":259,"value":63969},"Models.cs",{"type":259,"value":63971}," file), partly because for this example it was faster and simpler than creating a file for each model. But when you think carefully about it, it is not such a bad thing: in one look you have a pretty good view of the different models the code is using without having to browse different files. Of course, in a real project with a lot of code, I would have grouped my models by business concern and separated these different concerns in different files with more expressive names than ",{"type":254,"tag":389,"props":63973,"children":63975},{"className":63974},[],[63976],{"type":259,"value":63844},{"type":259,"value":63978},". But I think the convention of using one file by class is not completely relevant in the case of records.",{"type":254,"tag":612,"props":63980,"children":63982},{"className":1423,"code":63981,"language":1425,"meta":205,"style":205},"using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace MyLotrApi\n{\n    public record Movie(string Name, int RuntimeInMinutes, int BudgetInMillions, float RottenTomatesScore);\n    \n    public record MovieResponse(IList\u003CMovie> Docs, int Total);\n\n    public record Character(string Name, string Realm);\n\n    public record CharacterResponse(IList\u003CCharacter> Docs, int Total);\n}\n",[63983],{"type":254,"tag":389,"props":63984,"children":63985},{"__ignoreMap":205},[63986,64001,64034,64058,64082,64115,64122,64134,64141,64208,64215,64274,64281,64326,64333,64390],{"type":254,"tag":622,"props":63987,"children":63988},{"class":624,"line":625},[63989,63993,63997],{"type":254,"tag":622,"props":63990,"children":63991},{"style":741},[63992],{"type":259,"value":1604},{"type":254,"tag":622,"props":63994,"children":63995},{"style":1202},[63996],{"type":259,"value":3688},{"type":254,"tag":622,"props":63998,"children":63999},{"style":629},[64000],{"type":259,"value":2350},{"type":254,"tag":622,"props":64002,"children":64003},{"class":624,"line":635},[64004,64008,64012,64016,64021,64025,64030],{"type":254,"tag":622,"props":64005,"children":64006},{"style":741},[64007],{"type":259,"value":1604},{"type":254,"tag":622,"props":64009,"children":64010},{"style":1202},[64011],{"type":259,"value":3688},{"type":254,"tag":622,"props":64013,"children":64014},{"style":629},[64015],{"type":259,"value":594},{"type":254,"tag":622,"props":64017,"children":64018},{"style":1202},[64019],{"type":259,"value":64020},"Collections",{"type":254,"tag":622,"props":64022,"children":64023},{"style":629},[64024],{"type":259,"value":594},{"type":254,"tag":622,"props":64026,"children":64027},{"style":1202},[64028],{"type":259,"value":64029},"Generic",{"type":254,"tag":622,"props":64031,"children":64032},{"style":629},[64033],{"type":259,"value":2350},{"type":254,"tag":622,"props":64035,"children":64036},{"class":624,"line":679},[64037,64041,64045,64049,64054],{"type":254,"tag":622,"props":64038,"children":64039},{"style":741},[64040],{"type":259,"value":1604},{"type":254,"tag":622,"props":64042,"children":64043},{"style":1202},[64044],{"type":259,"value":3688},{"type":254,"tag":622,"props":64046,"children":64047},{"style":629},[64048],{"type":259,"value":594},{"type":254,"tag":622,"props":64050,"children":64051},{"style":1202},[64052],{"type":259,"value":64053},"Linq",{"type":254,"tag":622,"props":64055,"children":64056},{"style":629},[64057],{"type":259,"value":2350},{"type":254,"tag":622,"props":64059,"children":64060},{"class":624,"line":705},[64061,64065,64069,64073,64078],{"type":254,"tag":622,"props":64062,"children":64063},{"style":741},[64064],{"type":259,"value":1604},{"type":254,"tag":622,"props":64066,"children":64067},{"style":1202},[64068],{"type":259,"value":3688},{"type":254,"tag":622,"props":64070,"children":64071},{"style":629},[64072],{"type":259,"value":594},{"type":254,"tag":622,"props":64074,"children":64075},{"style":1202},[64076],{"type":259,"value":64077},"Text",{"type":254,"tag":622,"props":64079,"children":64080},{"style":629},[64081],{"type":259,"value":2350},{"type":254,"tag":622,"props":64083,"children":64084},{"class":624,"line":732},[64085,64089,64093,64097,64102,64106,64111],{"type":254,"tag":622,"props":64086,"children":64087},{"style":741},[64088],{"type":259,"value":1604},{"type":254,"tag":622,"props":64090,"children":64091},{"style":1202},[64092],{"type":259,"value":3688},{"type":254,"tag":622,"props":64094,"children":64095},{"style":629},[64096],{"type":259,"value":594},{"type":254,"tag":622,"props":64098,"children":64099},{"style":1202},[64100],{"type":259,"value":64101},"Threading",{"type":254,"tag":622,"props":64103,"children":64104},{"style":629},[64105],{"type":259,"value":594},{"type":254,"tag":622,"props":64107,"children":64108},{"style":1202},[64109],{"type":259,"value":64110},"Tasks",{"type":254,"tag":622,"props":64112,"children":64113},{"style":629},[64114],{"type":259,"value":2350},{"type":254,"tag":622,"props":64116,"children":64117},{"class":624,"line":759},[64118],{"type":254,"tag":622,"props":64119,"children":64120},{"emptyLinePlaceholder":1699},[64121],{"type":259,"value":1702},{"type":254,"tag":622,"props":64123,"children":64124},{"class":624,"line":798},[64125,64129],{"type":254,"tag":622,"props":64126,"children":64127},{"style":714},[64128],{"type":259,"value":14135},{"type":254,"tag":622,"props":64130,"children":64131},{"style":1202},[64132],{"type":259,"value":64133}," MyLotrApi\n",{"type":254,"tag":622,"props":64135,"children":64136},{"class":624,"line":836},[64137],{"type":254,"tag":622,"props":64138,"children":64139},{"style":629},[64140],{"type":259,"value":632},{"type":254,"tag":622,"props":64142,"children":64143},{"class":624,"line":862},[64144,64148,64152,64156,64160,64164,64168,64172,64176,64180,64184,64188,64192,64196,64200,64204],{"type":254,"tag":622,"props":64145,"children":64146},{"style":644},[64147],{"type":259,"value":1970},{"type":254,"tag":622,"props":64149,"children":64150},{"style":714},[64151],{"type":259,"value":1442},{"type":254,"tag":622,"props":64153,"children":64154},{"style":714},[64155],{"type":259,"value":63902},{"type":254,"tag":622,"props":64157,"children":64158},{"style":629},[64159],{"type":259,"value":1668},{"type":254,"tag":622,"props":64161,"children":64162},{"style":629},[64163],{"type":259,"value":2851},{"type":254,"tag":622,"props":64165,"children":64166},{"style":714},[64167],{"type":259,"value":1465},{"type":254,"tag":622,"props":64169,"children":64170},{"style":629},[64171],{"type":259,"value":3149},{"type":254,"tag":622,"props":64173,"children":64174},{"style":629},[64175],{"type":259,"value":14190},{"type":254,"tag":622,"props":64177,"children":64178},{"style":714},[64179],{"type":259,"value":63927},{"type":254,"tag":622,"props":64181,"children":64182},{"style":629},[64183],{"type":259,"value":3149},{"type":254,"tag":622,"props":64185,"children":64186},{"style":629},[64187],{"type":259,"value":14190},{"type":254,"tag":622,"props":64189,"children":64190},{"style":714},[64191],{"type":259,"value":63940},{"type":254,"tag":622,"props":64193,"children":64194},{"style":629},[64195],{"type":259,"value":3149},{"type":254,"tag":622,"props":64197,"children":64198},{"style":629},[64199],{"type":259,"value":63949},{"type":254,"tag":622,"props":64201,"children":64202},{"style":714},[64203],{"type":259,"value":63954},{"type":254,"tag":622,"props":64205,"children":64206},{"style":629},[64207],{"type":259,"value":1554},{"type":254,"tag":622,"props":64209,"children":64210},{"class":624,"line":900},[64211],{"type":254,"tag":622,"props":64212,"children":64213},{"style":1202},[64214],{"type":259,"value":7336},{"type":254,"tag":622,"props":64216,"children":64217},{"class":624,"line":938},[64218,64222,64226,64231,64235,64239,64243,64248,64252,64257,64261,64265,64270],{"type":254,"tag":622,"props":64219,"children":64220},{"style":644},[64221],{"type":259,"value":1970},{"type":254,"tag":622,"props":64223,"children":64224},{"style":714},[64225],{"type":259,"value":1442},{"type":254,"tag":622,"props":64227,"children":64228},{"style":714},[64229],{"type":259,"value":64230}," MovieResponse",{"type":254,"tag":622,"props":64232,"children":64233},{"style":629},[64234],{"type":259,"value":1668},{"type":254,"tag":622,"props":64236,"children":64237},{"style":714},[64238],{"type":259,"value":15221},{"type":254,"tag":622,"props":64240,"children":64241},{"style":629},[64242],{"type":259,"value":1743},{"type":254,"tag":622,"props":64244,"children":64245},{"style":714},[64246],{"type":259,"value":64247},"Movie",{"type":254,"tag":622,"props":64249,"children":64250},{"style":629},[64251],{"type":259,"value":6813},{"type":254,"tag":622,"props":64253,"children":64254},{"style":714},[64255],{"type":259,"value":64256}," Docs",{"type":254,"tag":622,"props":64258,"children":64259},{"style":629},[64260],{"type":259,"value":3149},{"type":254,"tag":622,"props":64262,"children":64263},{"style":629},[64264],{"type":259,"value":14190},{"type":254,"tag":622,"props":64266,"children":64267},{"style":714},[64268],{"type":259,"value":64269}," Total",{"type":254,"tag":622,"props":64271,"children":64272},{"style":629},[64273],{"type":259,"value":1554},{"type":254,"tag":622,"props":64275,"children":64276},{"class":624,"line":976},[64277],{"type":254,"tag":622,"props":64278,"children":64279},{"emptyLinePlaceholder":1699},[64280],{"type":259,"value":1702},{"type":254,"tag":622,"props":64282,"children":64283},{"class":624,"line":1014},[64284,64288,64292,64297,64301,64305,64309,64313,64317,64322],{"type":254,"tag":622,"props":64285,"children":64286},{"style":644},[64287],{"type":259,"value":1970},{"type":254,"tag":622,"props":64289,"children":64290},{"style":714},[64291],{"type":259,"value":1442},{"type":254,"tag":622,"props":64293,"children":64294},{"style":714},[64295],{"type":259,"value":64296}," Character",{"type":254,"tag":622,"props":64298,"children":64299},{"style":629},[64300],{"type":259,"value":1668},{"type":254,"tag":622,"props":64302,"children":64303},{"style":629},[64304],{"type":259,"value":2851},{"type":254,"tag":622,"props":64306,"children":64307},{"style":714},[64308],{"type":259,"value":1465},{"type":254,"tag":622,"props":64310,"children":64311},{"style":629},[64312],{"type":259,"value":3149},{"type":254,"tag":622,"props":64314,"children":64315},{"style":629},[64316],{"type":259,"value":1975},{"type":254,"tag":622,"props":64318,"children":64319},{"style":714},[64320],{"type":259,"value":64321}," Realm",{"type":254,"tag":622,"props":64323,"children":64324},{"style":629},[64325],{"type":259,"value":1554},{"type":254,"tag":622,"props":64327,"children":64328},{"class":624,"line":1052},[64329],{"type":254,"tag":622,"props":64330,"children":64331},{"emptyLinePlaceholder":1699},[64332],{"type":259,"value":1702},{"type":254,"tag":622,"props":64334,"children":64335},{"class":624,"line":1078},[64336,64340,64344,64349,64353,64357,64361,64366,64370,64374,64378,64382,64386],{"type":254,"tag":622,"props":64337,"children":64338},{"style":644},[64339],{"type":259,"value":1970},{"type":254,"tag":622,"props":64341,"children":64342},{"style":714},[64343],{"type":259,"value":1442},{"type":254,"tag":622,"props":64345,"children":64346},{"style":714},[64347],{"type":259,"value":64348}," CharacterResponse",{"type":254,"tag":622,"props":64350,"children":64351},{"style":629},[64352],{"type":259,"value":1668},{"type":254,"tag":622,"props":64354,"children":64355},{"style":714},[64356],{"type":259,"value":15221},{"type":254,"tag":622,"props":64358,"children":64359},{"style":629},[64360],{"type":259,"value":1743},{"type":254,"tag":622,"props":64362,"children":64363},{"style":714},[64364],{"type":259,"value":64365},"Character",{"type":254,"tag":622,"props":64367,"children":64368},{"style":629},[64369],{"type":259,"value":6813},{"type":254,"tag":622,"props":64371,"children":64372},{"style":714},[64373],{"type":259,"value":64256},{"type":254,"tag":622,"props":64375,"children":64376},{"style":629},[64377],{"type":259,"value":3149},{"type":254,"tag":622,"props":64379,"children":64380},{"style":629},[64381],{"type":259,"value":14190},{"type":254,"tag":622,"props":64383,"children":64384},{"style":714},[64385],{"type":259,"value":64269},{"type":254,"tag":622,"props":64387,"children":64388},{"style":629},[64389],{"type":259,"value":1554},{"type":254,"tag":622,"props":64391,"children":64392},{"class":624,"line":1087},[64393],{"type":254,"tag":622,"props":64394,"children":64395},{"style":629},[64396],{"type":259,"value":1111},{"type":254,"tag":255,"props":64398,"children":64399},{},[64400,64402,64407,64409,64414,64416,64421],{"type":259,"value":64401},"Another interesting feature of records is that it is easy to declare immutable data models with them. That is exactly what I did here by declaring my data models as immutable records for the objects returned by ",{"type":254,"tag":389,"props":64403,"children":64405},{"className":64404},[],[64406],{"type":259,"value":63747},{"type":259,"value":64408},". Indeed it makes perfect sense in this context where my API queries ",{"type":254,"tag":389,"props":64410,"children":64412},{"className":64411},[],[64413],{"type":259,"value":63747},{"type":259,"value":64415},", eventually filters the objects returned depending on what it is looking to expose and returns them without modifying their content. In no way the code should modify the data retrieved from ",{"type":254,"tag":389,"props":64417,"children":64419},{"className":64418},[],[64420],{"type":259,"value":63747},{"type":259,"value":64422}," and immutable records can easily guarantee that: if the code tries to change something it won't compile.",{"type":254,"tag":255,"props":64424,"children":64425},{},[64426],{"type":254,"tag":1161,"props":64427,"children":64431},{"alt":64428,"className":64429,"src":64430},"Code showing immutability of records.",[1165,1166],"/posts/images/onceuponatime_records_1.png",[],{"type":254,"tag":255,"props":64433,"children":64434},{},[64435,64437,64443],{"type":259,"value":64436},"There is one more reason why using records in this API is a good idea: value equality. When writing unit tests I often have the case where I want to assert that an object (or a collection of objects) returned by the method under test is the same that the object(s) I was expected. However, doing an ",{"type":254,"tag":389,"props":64438,"children":64440},{"className":64439},[],[64441],{"type":259,"value":64442},"Assert.AreEqual",{"type":259,"value":64444}," on two variables of a reference type does not work because by default they are only equal if they refer to the same object.",{"type":254,"tag":255,"props":64446,"children":64447},{},[64448],{"type":254,"tag":1161,"props":64449,"children":64453},{"alt":64450,"className":64451,"src":64452},"Code showing classes not having value equality",[1165,1166],"onceuponatime_records_3.png",[],{"type":254,"tag":255,"props":64455,"children":64456},{},[64457,64459,64470,64472,64478,64480,64486,64487,64493,64495,64500],{"type":259,"value":64458},"As a workaround, you can compare the properties of your two variables (if they are value type properties) or you can use the library ",{"type":254,"tag":262,"props":64460,"children":64463},{"href":64461,"rel":64462},"https://github.com/fluentassertions/fluentassertions",[266],[64464],{"type":254,"tag":389,"props":64465,"children":64467},{"className":64466},[],[64468],{"type":259,"value":64469},"FluentAssertions",{"type":259,"value":64471}," for your asserts. But the real solution to make your asserts work is to implement ",{"type":254,"tag":389,"props":64473,"children":64475},{"className":64474},[],[64476],{"type":259,"value":64477},"IEquatable\u003CT>",{"type":259,"value":64479},", and overrides ",{"type":254,"tag":389,"props":64481,"children":64483},{"className":64482},[],[64484],{"type":259,"value":64485},"Object.Equals(Object)",{"type":259,"value":10347},{"type":254,"tag":389,"props":64488,"children":64490},{"className":64489},[],[64491],{"type":259,"value":64492},"Object.GetHashCode()",{"type":259,"value":64494}," on the classes you want to compare. It's the solution I often use, it's a bit cumbersome because it makes you write a lot of boilerplate for your data models but it helps you a lot with your unit tests assertions. The good news, if you are using records is that all this code is already done for you, you have value equality by default so an ",{"type":254,"tag":389,"props":64496,"children":64498},{"className":64497},[],[64499],{"type":259,"value":64442},{"type":259,"value":64501}," between two variables of a record type will work if all the property and field values match.",{"type":254,"tag":255,"props":64503,"children":64504},{},[64505],{"type":254,"tag":1161,"props":64506,"children":64510},{"alt":64507,"className":64508,"src":64509},"Code showing value equality of records",[1165,1166],"/posts/images/onceuponatime_records_2.png",[],{"type":254,"tag":282,"props":64512,"children":64514},{"id":64513},"what-can-be-improved-in-theoneapiservice",[64515,64517,64522],{"type":259,"value":64516},"What can be improved in ",{"type":254,"tag":389,"props":64518,"children":64520},{"className":64519},[],[64521],{"type":259,"value":63801},{"type":259,"value":1980},{"type":254,"tag":255,"props":64524,"children":64525},{},[64526,64528,64538,64540,64545],{"type":259,"value":64527},"Enough talking about records, let's have a closer look at ",{"type":254,"tag":262,"props":64529,"children":64532},{"href":64530,"rel":64531},"https://github.com/TechWatching/MyLotrApi/blob/a70b6f91fcbcc30a3c3a3616799e3e85817b7906/src/MyLotrApi/TheOneApiService.cs",[266],[64533],{"type":254,"tag":389,"props":64534,"children":64536},{"className":64535},[],[64537],{"type":259,"value":63801},{"type":259,"value":64539}," and see what we can improve. At first sight, the code looks fine, just 2 methods that use an ",{"type":254,"tag":389,"props":64541,"children":64543},{"className":64542},[],[64544],{"type":259,"value":1565},{"type":259,"value":64546}," to make a get request, ensure that the response is ok (throw an exception otherwise), retrieve the response content as a string, and deserialize it into their corresponding types with NewtonSoft. Basic code that we can often see.",{"type":254,"tag":255,"props":64548,"children":64549},{},[64550],{"type":254,"tag":1161,"props":64551,"children":64555},{"alt":64552,"className":64553,"src":64554},"The OneApiService first code version.",[1165,1166],"/posts/images/onceuponatime_theoneapiservice_1.png",[],{"type":254,"tag":255,"props":64557,"children":64558},{},[64559],{"type":259,"value":64560},"Yet, it seems that there is a bit of code duplication between the methods, not a problem as we only have two methods but it can quickly become one if we add other methods. So what can we do about that?",{"type":254,"tag":455,"props":64562,"children":64564},{"id":64563},"adding-a-private-method-that-factorizes-the-code",[64565],{"type":259,"value":64566},"Adding a private method that factorizes the code",{"type":254,"tag":255,"props":64568,"children":64569},{},[64570,64572,64578,64580,64586,64587,64593],{"type":259,"value":64571},"This solution is something I often see: people wrap the common logic between their methods in a private method that gets called by the others. So we end up having a generic ",{"type":254,"tag":389,"props":64573,"children":64575},{"className":64574},[],[64576],{"type":259,"value":64577},"Send",{"type":259,"value":64579}," that does all the job (request, response handling, deserialization ...), and our 2 methods ",{"type":254,"tag":389,"props":64581,"children":64583},{"className":64582},[],[64584],{"type":259,"value":64585},"GetMovies",{"type":259,"value":10347},{"type":254,"tag":389,"props":64588,"children":64590},{"className":64589},[],[64591],{"type":259,"value":64592},"GetCharacters",{"type":259,"value":64594}," that have become quite trivial.",{"type":254,"tag":255,"props":64596,"children":64597},{},[64598],{"type":254,"tag":1161,"props":64599,"children":64603},{"alt":64600,"className":64601,"src":64602},"The OneApiService factorizing code in a private method.",[1165,1166],"/posts/images/onceuponatime_theoneapiservice_2.png",[],{"type":254,"tag":255,"props":64605,"children":64606},{},[64607,64609,64615],{"type":259,"value":64608},"(this code can be found ",{"type":254,"tag":262,"props":64610,"children":64613},{"href":64611,"rel":64612},"https://github.com/TechWatching/MyLotrApi/blob/3e5561cb71678e432e970edcac6509581e3aaecd/src/MyLotrApi/TheOneApiService.cs",[266],[64614],{"type":259,"value":1414},{"type":259,"value":2309},{"type":254,"tag":255,"props":64617,"children":64618},{},[64619],{"type":259,"value":64620},"Well, the code is fine, nothing to say about that but I don't like this solution at all 😁. And here is why:",{"type":254,"tag":476,"props":64622,"children":64623},{},[64624],{"type":254,"tag":336,"props":64625,"children":64626},{},[64627],{"type":259,"value":64628},"It adds complexity and makes the code more difficult to read",{"type":254,"tag":255,"props":64630,"children":64631},{},[64632,64634,64639,64640,64645,64647,64652,64654,64660],{"type":259,"value":64633},"When I look at the ",{"type":254,"tag":389,"props":64635,"children":64637},{"className":64636},[],[64638],{"type":259,"value":64585},{"type":259,"value":31561},{"type":254,"tag":389,"props":64641,"children":64643},{"className":64642},[],[64644],{"type":259,"value":64592},{"type":259,"value":64646}," I don't know exactly what they do, the business logic is hidden in the private ",{"type":254,"tag":389,"props":64648,"children":64650},{"className":64649},[],[64651],{"type":259,"value":64577},{"type":259,"value":64653}," method. This private method in itself is more complicated than the previous duplicated code because it has to handle different cases, like the fact that the request can be a POST or a PUT, hence the use of an ",{"type":254,"tag":389,"props":64655,"children":64657},{"className":64656},[],[64658],{"type":259,"value":64659},"HttpRequestMessage",{"type":259,"value":64661}," with the potential content to send in the request. In fact, it's quite clear that this method does too many different things.",{"type":254,"tag":476,"props":64663,"children":64664},{"start":635},[64665],{"type":254,"tag":336,"props":64666,"children":64667},{},[64668],{"type":259,"value":64669},"It abstracts the use of the HttpClient",{"type":254,"tag":255,"props":64671,"children":64672},{},[64673,64675,64680,64682,64687,64689,64694,64696,64701],{"type":259,"value":64674},"I have nothing against abstractions, on the contrary, but I think they should add value and not completely hide what we are using under the hood. And here we are concealing the fact we are using an HttpClient, that does a ",{"type":254,"tag":389,"props":64676,"children":64678},{"className":64677},[],[64679],{"type":259,"value":1199},{"type":259,"value":64681}," request on the \"character\" route for instance. Of course, we can find part of that information in the parameters provided to the ",{"type":254,"tag":389,"props":64683,"children":64685},{"className":64684},[],[64686],{"type":259,"value":64577},{"type":259,"value":64688}," method but it is not as clear as calling the ",{"type":254,"tag":389,"props":64690,"children":64692},{"className":64691},[],[64693],{"type":259,"value":15108},{"type":259,"value":64695}," method of the ",{"type":254,"tag":389,"props":64697,"children":64699},{"className":64698},[],[64700],{"type":259,"value":1565},{"type":259,"value":64702}," as we are used to doing. Using an HttpClient already abstracts the complexity of creating an Http request and sending it, we do not need to trade this abstraction off for another less understandable abstraction.",{"type":254,"tag":476,"props":64704,"children":64705},{"start":679},[64706],{"type":254,"tag":336,"props":64707,"children":64708},{},[64709],{"type":259,"value":64710},"It makes the code hard to maintain",{"type":254,"tag":255,"props":64712,"children":64713},{},[64714,64716,64721],{"type":259,"value":64715},"Because all the main methods of this service call this private method, it will be difficult to change something in it without breaking something else. If someday we need to do something specific linked to the HTTP call for one case it will be hard to implement it in the ",{"type":254,"tag":389,"props":64717,"children":64719},{"className":64718},[],[64720],{"type":259,"value":64577},{"type":259,"value":64722}," method, the only solution will be to add optional parameters and do a lot of conditions in the code which will quickly become dirty.",{"type":254,"tag":255,"props":64724,"children":64725},{},[64726],{"type":259,"value":64727},"If you are fond of SOLID, what I am just saying is that this solution does not satisfy several SOLID principles like the Single responsibility principle and the Open-closed principle.",{"type":254,"tag":255,"props":64729,"children":64730},{},[64731,64733,64738],{"type":259,"value":64732},"So what could be done to improve this code if it is not by factorizing it in a private method. As we said, the code in the service does too much things so maybe it's time to remove some concerns from the ",{"type":254,"tag":389,"props":64734,"children":64736},{"className":64735},[],[64737],{"type":259,"value":63801},{"type":259,"value":594},{"type":254,"tag":455,"props":64740,"children":64742},{"id":64741},"implementing-an-http-message-handler-to-simplify-the-code",[64743],{"type":259,"value":64744},"Implementing an HTTP message handler to simplify the code",{"type":254,"tag":255,"props":64746,"children":64747},{},[64748,64750,64756,64758,64763,64765,64771],{"type":259,"value":64749},"I already talked about using an HTTP message handler / delegating handler in ",{"type":254,"tag":262,"props":64751,"children":64754},{"href":64752,"rel":64753},"https://www.techwatching.dev/posts/delegating-handler",[266],[64755],{"type":259,"value":43878},{"type":259,"value":64757},". When registered with an HTTP client, it is a piece of code that all the HTTP requests you do on this HTTP client will go through. Hence it is a nice way of factorizing code that we want to apply to all the requests to ",{"type":254,"tag":389,"props":64759,"children":64761},{"className":64760},[],[64762],{"type":259,"value":63747},{"type":259,"value":64764}," like the fact of throwing the ",{"type":254,"tag":389,"props":64766,"children":64768},{"className":64767},[],[64769],{"type":259,"value":64770},"TheOneApiException",{"type":259,"value":64772}," when the HTTP responses are not successful.",{"type":254,"tag":255,"props":64774,"children":64775},{},[64776],{"type":254,"tag":1161,"props":64777,"children":64781},{"alt":64778,"className":64779,"src":64780},"The code the OneApiErrorDelegatingHandler.",[1165,1166],"/posts/images/onceuponatime_handler_1.png",[],{"type":254,"tag":255,"props":64783,"children":64784},{},[64785,64787,64793],{"type":259,"value":64786},"(This code can be found ",{"type":254,"tag":262,"props":64788,"children":64791},{"href":64789,"rel":64790},"https://github.com/TechWatching/MyLotrApi/blob/9dfd9966044560d6e13c71d93eeba775e02bd18d/src/MyLotrApi/Services/HttpMessageHandlers/TheOneApiErrorDelegatingHandler.cs",[266],[64792],{"type":259,"value":1414},{"type":259,"value":2309},{"type":254,"tag":455,"props":64795,"children":64797},{"id":64796},"using-httpclientjsonextensions-to-get-rid-of-the-deserialization-code",[64798,64800,64806],{"type":259,"value":64799},"Using ",{"type":254,"tag":389,"props":64801,"children":64803},{"className":64802},[],[64804],{"type":259,"value":64805},"HttpClientJsonExtensions",{"type":259,"value":64807}," to get rid of the deserialization code",{"type":254,"tag":255,"props":64809,"children":64810},{},[64811],{"type":259,"value":64812},"In the current code, there are some lines to read the response content as a string and deserialize it. The interesting code in the service is the fact of doing a GET or a POST to retrieve some data, not the boilerplate code to handle deserialization. So it would be great to be able to remove it. In .NET Framework, there used to be some HTTP client extensions that worked with NewtonSoft to do that.",{"type":254,"tag":255,"props":64814,"children":64815},{},[64816,64818,64830],{"type":259,"value":64817},"In .NET Core there is no longer that, however, there are HttpClient method extensions that use System.Text.Json to make the request and deserialize the response content in one line. For that, you need to reference the ",{"type":254,"tag":262,"props":64819,"children":64822},{"href":64820,"rel":64821},"https://www.nuget.org/packages/System.Net.Http.Json",[266],[64823,64828],{"type":254,"tag":389,"props":64824,"children":64826},{"className":64825},[],[64827],{"type":259,"value":1573},{"type":259,"value":64829}," NuGet package",{"type":259,"value":64831}," and the code becomes simpler.",{"type":254,"tag":255,"props":64833,"children":64834},{},[64835],{"type":254,"tag":1161,"props":64836,"children":64840},{"alt":64837,"className":64838,"src":64839},"The code of GetCharacters method without manual deserialization.",[1165,1166],"/posts/images/onceuponatime_theoneapiservice_3.png",[],{"type":254,"tag":255,"props":64842,"children":64843},{},[64844,64845,64851],{"type":259,"value":64786},{"type":254,"tag":262,"props":64846,"children":64849},{"href":64847,"rel":64848},"https://github.com/TechWatching/MyLotrApi/blob/4ceca01826a0de4cca593c731f812ace874924a8/src/MyLotrApi/Services/TheOneApiService.cs",[266],[64850],{"type":259,"value":1414},{"type":259,"value":2309},{"type":254,"tag":455,"props":64853,"children":64855},{"id":64854},"exploring-an-alternative-to-implementing-the-interface-itheoneapiservice-with-refit",[64856,64858,64864],{"type":259,"value":64857},"Exploring an alternative to implementing the interface ",{"type":254,"tag":389,"props":64859,"children":64861},{"className":64860},[],[64862],{"type":259,"value":64863},"ITheOneApiService",{"type":259,"value":64865}," with Refit",{"type":254,"tag":255,"props":64867,"children":64868},{},[64869,64871,64876,64878,64885],{"type":259,"value":64870},"I am pretty happy with how we improved and simplified the code thanks to the handler and the HTTP extensions methods. Yet I think I cannot end this article without mentioning ",{"type":254,"tag":262,"props":64872,"children":64874},{"href":14998,"rel":64873},[266],[64875],{"type":259,"value":15002},{"type":259,"value":64877},": the automatic type-safe REST library for .NET. The idea behind this ",{"type":254,"tag":262,"props":64879,"children":64882},{"href":64880,"rel":64881},"https://reactiveui.github.io/refit/",[266],[64883],{"type":259,"value":64884},"library",{"type":259,"value":64886}," is that you only have to define the interface specifying the routes you want to query and the library will generate an implementation that does the calls for you with an HttpClient. No need to implement that yourself anymore, so less code to maintain for the same result.",{"type":254,"tag":255,"props":64888,"children":64889},{},[64890],{"type":254,"tag":1161,"props":64891,"children":64895},{"alt":64892,"className":64893,"src":64894},"The code of the IOneApiService interface using Refit.",[1165,1166],"/posts/images/onceuponatime_refit_2.png",[],{"type":254,"tag":255,"props":64897,"children":64898},{},[64899,64901,64906,64908,64914],{"type":259,"value":64900},"Refit even automatically handles query parameters by mapping them to the public properties of the object in parameter of a ",{"type":254,"tag":389,"props":64902,"children":64904},{"className":64903},[],[64905],{"type":259,"value":1199},{"type":259,"value":64907}," method in the interface. To name differently your property from the query parameter you can simply add an ",{"type":254,"tag":389,"props":64909,"children":64911},{"className":64910},[],[64912],{"type":259,"value":64913},"AliasAs",{"type":259,"value":64915}," attribute as I did here.",{"type":254,"tag":255,"props":64917,"children":64918},{},[64919],{"type":254,"tag":1161,"props":64920,"children":64924},{"alt":64921,"className":64922,"src":64923},"The code of CharacterQueryParam.",[1165,1166],"/posts/images/onceuponatime_refit_3.png",[],{"type":254,"tag":255,"props":64926,"children":64927},{},[64928,64930,64935],{"type":259,"value":64929},"Using Refit might not be appropriate to your use case when you have very specific things to do but for a basic REST service like ",{"type":254,"tag":389,"props":64931,"children":64933},{"className":64932},[],[64934],{"type":259,"value":63801},{"type":259,"value":64936},", it is perfect. Refit has other nice features but what I really appreciate is that it also works with delegating handlers. So I can use Refit and still use the delegating handler I previously created.",{"type":254,"tag":255,"props":64938,"children":64939},{},[64940],{"type":254,"tag":1161,"props":64941,"children":64945},{"alt":64942,"className":64943,"src":64944},"The code for registering the services using Refit.",[1165,1166],"/posts/images/onceuponatime_refit_1.png",[],{"type":254,"tag":255,"props":64947,"children":64948},{},[64949,64950,64956],{"type":259,"value":64786},{"type":254,"tag":262,"props":64951,"children":64954},{"href":64952,"rel":64953},"https://github.com/TechWatching/MyLotrApi/blob/76f85099bdb8c747717bf3e61007c276d5055e6f/src/MyLotrApi/Startup.cs",[266],[64955],{"type":259,"value":1414},{"type":259,"value":2309},{"type":254,"tag":282,"props":64958,"children":64959},{"id":10572},[64960],{"type":259,"value":10575},{"type":254,"tag":255,"props":64962,"children":64963},{},[64964],{"type":259,"value":64965},"In this article, we have seen how we can improve some .NET code while talking about records, delegating handlers, refit...",{"type":254,"tag":1260,"props":64967,"children":64968},{},[64969],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":64971},[64972,64973,64974,64983],{"id":63715,"depth":635,"text":63718},{"id":63849,"depth":635,"text":63852},{"id":64513,"depth":635,"text":64975,"children":64976},"What can be improved in TheOneApiService?",[64977,64978,64979,64981],{"id":64563,"depth":679,"text":64566},{"id":64741,"depth":679,"text":64744},{"id":64796,"depth":679,"text":64980},"Using HttpClientJsonExtensions to get rid of the deserialization code",{"id":64854,"depth":679,"text":64982},"Exploring an alternative to implementing the interface ITheOneApiService with Refit",{"id":10572,"depth":635,"text":10575},"content:1.posts:16.once-upon-a-time-in-dotnet.md","1.posts/16.once-upon-a-time-in-dotnet.md",{"_path":52,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":51,"description":64987,"lead":64988,"date":64989,"image":64990,"badge":64991,"tags":64992,"body":64994,"_type":1275,"_id":65163,"_source":231,"_file":65164,"_extension":1278},"This week I learned a few things related to vscode and especially about the Azure IoT Hub vscode extension.","Azure tenant in vs code and Azure IoT Hub vs code extension.","2021-04-10T00:00:00.000Z",{"src":9258},{"label":9260},[9262,37215,64993,6260],"Azure IoT",{"type":251,"children":64995,"toc":65159},[64996,65009,65015,65029,65049,65058,65072,65078,65083,65095,65104,65109,65118,65123,65132,65146,65155],{"type":254,"tag":255,"props":64997,"children":64998},{},[64999,65001,65007],{"type":259,"value":65000},"This week I learned a few things related to vscode and especially about the ",{"type":254,"tag":389,"props":65002,"children":65004},{"className":65003},[],[65005],{"type":259,"value":65006},"Azure IoT Hub",{"type":259,"value":65008}," vscode extension.",{"type":254,"tag":282,"props":65010,"children":65012},{"id":65011},"accessing-azure-resources-in-vscode-from-a-specific-tenant",[65013],{"type":259,"value":65014},"Accessing Azure resources in vscode from a specific tenant.",{"type":254,"tag":255,"props":65016,"children":65017},{},[65018,65020,65027],{"type":259,"value":65019},"There are a lot of vscode extensions (like the ones in ",{"type":254,"tag":262,"props":65021,"children":65024},{"href":65022,"rel":65023},"https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-node-azure-pack",[266],[65025],{"type":259,"value":65026},"this extension pack",{"type":259,"value":65028},") that allow you to access and manipulate your Azure resources from Visual Studio Code.",{"type":254,"tag":255,"props":65030,"children":65031},{},[65032,65034,65047],{"type":259,"value":65033},"This ",{"type":254,"tag":262,"props":65035,"children":65038},{"href":65036,"rel":65037},"https://marketplace.visualstudio.com/items?itemName=ms-vscode.azure-account",[266],[65039,65041],{"type":259,"value":65040},"specific extension ",{"type":254,"tag":389,"props":65042,"children":65044},{"className":65043},[],[65045],{"type":259,"value":65046},"Azure Account",{"type":259,"value":65048}," makes it easy for you to sign in to your Azure account and select the subscription you want to use. But what if you are working in a consulting company that works for different companies? How do you indicate the tenant of the customer to sign in to? Well, the answer is quite simple, the extension exposes a setting in vscode to specify a specific tenant to use.",{"type":254,"tag":255,"props":65050,"children":65051},{},[65052],{"type":254,"tag":1161,"props":65053,"children":65057},{"alt":65054,"className":65055,"src":65056},"Azure tenant settings in vscode.",[1165,1166],"/posts/images/w142021tips_vscode_1.png",[],{"type":254,"tag":427,"props":65059,"children":65060},{"icon":62755},[65061],{"type":254,"tag":255,"props":65062,"children":65063},{},[65064,65066],{"type":259,"value":65065},"If you like to update the settings in JSON mode the key to use is ",{"type":254,"tag":389,"props":65067,"children":65069},{"className":65068},[],[65070],{"type":259,"value":65071},"azure.tenant",{"type":254,"tag":282,"props":65073,"children":65075},{"id":65074},"azure-iot-hub-vs-code-extension-is-awesome",[65076],{"type":259,"value":65077},"Azure IoT Hub vs code extension is awesome.",{"type":254,"tag":255,"props":65079,"children":65080},{},[65081],{"type":259,"value":65082},"I think everything is in the title. I knew about this extension before this week but I did not take the time to really use it. However this week it helped me a lot for my sprint demo where I needed to show new functionalities I implemented on an IoT backend: modify device twin tags in the IoT Hub and send some messages to an IoT device under certain conditions.",{"type":254,"tag":255,"props":65084,"children":65085},{},[65086,65088,65093],{"type":259,"value":65087},"The Azure IoT Hub extension adds an ",{"type":254,"tag":389,"props":65089,"children":65091},{"className":65090},[],[65092],{"type":259,"value":65006},{"type":259,"value":65094}," view in the Explorer where you can see the devices of the IoT Hub you selected and can do some actions on them.",{"type":254,"tag":255,"props":65096,"children":65097},{},[65098],{"type":254,"tag":1161,"props":65099,"children":65103},{"alt":65100,"className":65101,"src":65102},"Azure IoT Hub view in vscode.",[1165,1166],"/posts/images/w142021tips_vscode_2.png",[],{"type":254,"tag":255,"props":65105,"children":65106},{},[65107],{"type":259,"value":65108},"There are also a lot of commands you can use to interact with an IoT Hub and its devices.",{"type":254,"tag":255,"props":65110,"children":65111},{},[65112],{"type":254,"tag":1161,"props":65113,"children":65117},{"alt":65114,"className":65115,"src":65116},"Azure IoT Hub commands in vscode command palette.",[1165,1166],"/posts/images/w142021tips_vscode_3.png",[],{"type":254,"tag":255,"props":65119,"children":65120},{},[65121],{"type":259,"value":65122},"You can do pretty much anything you want from sending C2D / D2C messages to monitoring C2D / D2C messages. I found it quite nice to be able to directly visualize and edit a device twin as a JSON  document in vs code.",{"type":254,"tag":255,"props":65124,"children":65125},{},[65126],{"type":254,"tag":1161,"props":65127,"children":65131},{"alt":65128,"className":65129,"src":65130},"Device twin json in vscode.",[1165,1166],"/posts/images/w142021tips_vscode_4.png",[],{"type":254,"tag":255,"props":65133,"children":65134},{},[65135,65137,65144],{"type":259,"value":65136},"To quickly test something related to IoT Hub, the Azure IoT Hub extension is a very useful tool. An interesting alternative to this tool is the ",{"type":254,"tag":262,"props":65138,"children":65141},{"href":65139,"rel":65140},"https://github.com/Azure/azure-iot-explorer/releases",[266],[65142],{"type":259,"value":65143},"Azure IoT Explorer",{"type":259,"value":65145}," which is more user-friendly thanks to its simple UI but does not have all the features.",{"type":254,"tag":255,"props":65147,"children":65148},{},[65149],{"type":254,"tag":1161,"props":65150,"children":65154},{"alt":65151,"className":65152,"src":65153},"Device twin json in Azure Iot Explorer.",[1165,1166],"/posts/images/w142021tips_iotexplorer_1.png",[],{"type":254,"tag":255,"props":65156,"children":65157},{},[65158],{"type":259,"value":10040},{"title":205,"searchDepth":635,"depth":635,"links":65160},[65161,65162],{"id":65011,"depth":635,"text":65014},{"id":65074,"depth":635,"text":65077},"content:1.posts:15.w14-2021-tips-learned-this-week.md","1.posts/15.w14-2021-tips-learned-this-week.md",{"_path":49,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":48,"description":65166,"lead":65167,"date":65168,"image":65169,"badge":65170,"tags":65171,"body":65173,"_type":1275,"_id":65569,"_source":231,"_file":65570,"_extension":1278},"This week I learned a few things related to versioning an application","Version in git tag with Azure Pipelines and in application insight logs.","2021-03-26T00:00:00.000Z",{"src":9258},{"label":9260},[9262,16850,51166,65172],"Application Insights",{"type":251,"children":65174,"toc":65565},[65175,65179,65185,65198,65203,65225,65230,65306,65340,65353,65397,65409,65415,65428,65437,65442,65468,65557,65561],{"type":254,"tag":255,"props":65176,"children":65177},{},[65178],{"type":259,"value":65166},{"type":254,"tag":282,"props":65180,"children":65182},{"id":65181},"create-a-git-tag-from-an-azure-pipeline",[65183],{"type":259,"value":65184},"Create a git tag from an Azure Pipeline.",{"type":254,"tag":255,"props":65186,"children":65187},{},[65188,65190,65196],{"type":259,"value":65189},"Creating a git tag for your repository stored in Azure DevOps can be done quite easily by creating a tag in your local repository and pushing it to Azure DevOps or by simply manually creating it from the ",{"type":254,"tag":389,"props":65191,"children":65193},{"className":65192},[],[65194],{"type":259,"value":65195},"Tags",{"type":259,"value":65197}," page in Azure DevOps. So why bother creating a tag from an Azure Pipeline?",{"type":254,"tag":255,"props":65199,"children":65200},{},[65201],{"type":259,"value":65202},"Doing things manually is error-prone and takes time, so for repetitive tasks, it is a good idea to automate them. And Azure Pipelines are great at automating things especially when it is relative to building or deploying code. In my team what we wanted was to have our CI/CD pipeline compute in what version was the code we were building and automatically tag the commit built with that version.",{"type":254,"tag":255,"props":65204,"children":65205},{},[65206,65208,65215,65217,65224],{"type":259,"value":65207},"Computing the version in an azure pipeline is not the topic here, so let's just say there are multiple ways to do that like using ",{"type":254,"tag":262,"props":65209,"children":65212},{"href":65210,"rel":65211},"https://docs.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops#counter",[266],[65213],{"type":259,"value":65214},"variables and the counter expression",{"type":259,"value":65216}," or using the ",{"type":254,"tag":262,"props":65218,"children":65221},{"href":65219,"rel":65220},"https://marketplace.visualstudio.com/items?itemName=gittools.gittools",[266],[65222],{"type":259,"value":65223},"gitversion task",{"type":259,"value":594},{"type":254,"tag":255,"props":65226,"children":65227},{},[65228],{"type":259,"value":65229},"Once you know the version you can use the git command line in a script task to create the tag and push it.",{"type":254,"tag":612,"props":65231,"children":65233},{"className":51570,"code":65232,"language":233,"meta":205,"style":205},"- script: |\n    git tag $(VersionPreviouslyComputed)\n    git push origin $(VersionPreviouslyComputed)\n  displayName: Tag version\n  workingDirectory: $(Build.SourcesDirectory)\n",[65234],{"type":254,"tag":389,"props":65235,"children":65236},{"__ignoreMap":205},[65237,65257,65265,65273,65289],{"type":254,"tag":622,"props":65238,"children":65239},{"class":624,"line":625},[65240,65244,65249,65253],{"type":254,"tag":622,"props":65241,"children":65242},{"style":629},[65243],{"type":259,"value":17065},{"type":254,"tag":622,"props":65245,"children":65246},{"style":768},[65247],{"type":259,"value":65248}," script",{"type":254,"tag":622,"props":65250,"children":65251},{"style":629},[65252],{"type":259,"value":474},{"type":254,"tag":622,"props":65254,"children":65255},{"style":1196},[65256],{"type":259,"value":16698},{"type":254,"tag":622,"props":65258,"children":65259},{"class":624,"line":635},[65260],{"type":254,"tag":622,"props":65261,"children":65262},{"style":664},[65263],{"type":259,"value":65264},"    git tag $(VersionPreviouslyComputed)\n",{"type":254,"tag":622,"props":65266,"children":65267},{"class":624,"line":679},[65268],{"type":254,"tag":622,"props":65269,"children":65270},{"style":664},[65271],{"type":259,"value":65272},"    git push origin $(VersionPreviouslyComputed)\n",{"type":254,"tag":622,"props":65274,"children":65275},{"class":624,"line":705},[65276,65280,65284],{"type":254,"tag":622,"props":65277,"children":65278},{"style":768},[65279],{"type":259,"value":25132},{"type":254,"tag":622,"props":65281,"children":65282},{"style":629},[65283],{"type":259,"value":474},{"type":254,"tag":622,"props":65285,"children":65286},{"style":664},[65287],{"type":259,"value":65288}," Tag version\n",{"type":254,"tag":622,"props":65290,"children":65291},{"class":624,"line":732},[65292,65297,65301],{"type":254,"tag":622,"props":65293,"children":65294},{"style":768},[65295],{"type":259,"value":65296},"  workingDirectory",{"type":254,"tag":622,"props":65298,"children":65299},{"style":629},[65300],{"type":259,"value":474},{"type":254,"tag":622,"props":65302,"children":65303},{"style":664},[65304],{"type":259,"value":65305}," $(Build.SourcesDirectory)\n",{"type":254,"tag":255,"props":65307,"children":65308},{},[65309,65311,65317,65319,65324,65325,65331,65333,65339],{"type":259,"value":65310},"For this script to work, you have to ensure that the identity that executes your pipeline has the right to push a tag on your repository. Concretely you have to give the ",{"type":254,"tag":389,"props":65312,"children":65314},{"className":65313},[],[65315],{"type":259,"value":65316},"contribute",{"type":259,"value":65318}," permission to the ",{"type":254,"tag":9718,"props":65320,"children":65321},{},[65322],{"type":259,"value":65323},"user",{"type":259,"value":24717},{"type":254,"tag":389,"props":65326,"children":65328},{"className":65327},[],[65329],{"type":259,"value":65330},"Project Collection Build Service ({your organization})",{"type":259,"value":65332}," as described ",{"type":254,"tag":262,"props":65334,"children":65337},{"href":65335,"rel":65336},"https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/git-commands?view=azure-devops&tabs=yaml#grant-version-control-permissions-to-the-build-service",[266],[65338],{"type":259,"value":1414},{"type":259,"value":594},{"type":254,"tag":255,"props":65341,"children":65342},{},[65343,65345,65351],{"type":259,"value":65344},"Moreover, you need to add an extra checkout task at the beginning of your pipeline. By default, you don't have to add this task, pipelines automatically do a checkout. But in this case, you want to set to true the parameter ",{"type":254,"tag":389,"props":65346,"children":65348},{"className":65347},[],[65349],{"type":259,"value":65350},"persistsCredentials",{"type":259,"value":65352}," to reuse the same credentials used for the initial checkout in the following other git operations in your pipelines.",{"type":254,"tag":612,"props":65354,"children":65356},{"className":51570,"code":65355,"language":233,"meta":205,"style":205},"- checkout: self\n  persistCredentials: true\n",[65357],{"type":254,"tag":389,"props":65358,"children":65359},{"__ignoreMap":205},[65360,65381],{"type":254,"tag":622,"props":65361,"children":65362},{"class":624,"line":625},[65363,65367,65372,65376],{"type":254,"tag":622,"props":65364,"children":65365},{"style":629},[65366],{"type":259,"value":17065},{"type":254,"tag":622,"props":65368,"children":65369},{"style":768},[65370],{"type":259,"value":65371}," checkout",{"type":254,"tag":622,"props":65373,"children":65374},{"style":629},[65375],{"type":259,"value":474},{"type":254,"tag":622,"props":65377,"children":65378},{"style":664},[65379],{"type":259,"value":65380}," self\n",{"type":254,"tag":622,"props":65382,"children":65383},{"class":624,"line":635},[65384,65389,65393],{"type":254,"tag":622,"props":65385,"children":65386},{"style":768},[65387],{"type":259,"value":65388},"  persistCredentials",{"type":254,"tag":622,"props":65390,"children":65391},{"style":629},[65392],{"type":259,"value":474},{"type":254,"tag":622,"props":65394,"children":65395},{"style":7715},[65396],{"type":259,"value":1075},{"type":254,"tag":255,"props":65398,"children":65399},{},[65400,65402,65408],{"type":259,"value":65401},"If you are not using a Microsoft-hosted agent but your own on-premise agent, you can have a problem when you delete a tag that was created by a pipeline. Indeed tags in the local repository of an on-premise agent are not automatically fetched and pruned so your following build can fail if it tries to create a tag that still exists locally (even if does not on the remote repository). To avoid that you can do add following command in your script ",{"type":254,"tag":389,"props":65403,"children":65405},{"className":65404},[],[65406],{"type":259,"value":65407},"git fetch origin refs/tags/*:refs/tags/* --prune",{"type":259,"value":594},{"type":254,"tag":282,"props":65410,"children":65412},{"id":65411},"application-version-in-logs-in-application-insights",[65413],{"type":259,"value":65414},"Application version in logs in Application Insights",{"type":254,"tag":255,"props":65416,"children":65417},{},[65418,65420,65426],{"type":259,"value":65419},"When developing applications in Azure, Application Insights is a key component to monitor these applications. But as for many components, we sometimes do not know how to use it to its full potential.\nThis week a colleague told me about a very basic feature that I did not know about: logs in Application Insights contain the version of the application that sends the logs. Indeed there is a property ",{"type":254,"tag":389,"props":65421,"children":65423},{"className":65422},[],[65424],{"type":259,"value":65425},"application_Version",{"type":259,"value":65427}," in each log with the version number of the application.",{"type":254,"tag":255,"props":65429,"children":65430},{},[65431],{"type":254,"tag":1161,"props":65432,"children":65436},{"alt":65433,"className":65434,"src":65435},"Application Insights logs.",[1165,1166],"/posts/images/w122021tips_ai_1.png",[],{"type":254,"tag":255,"props":65438,"children":65439},{},[65440],{"type":259,"value":65441},"It might look not very interesting but it can be really useful to have that in order to filter logs to a specific version in Log Analytics queries.",{"type":254,"tag":255,"props":65443,"children":65444},{},[65445,65447,65453,65454,65459,65461,65466],{"type":259,"value":65446},"One thing to note though is that by default Application Insights sets the ",{"type":254,"tag":389,"props":65448,"children":65450},{"className":65449},[],[65451],{"type":259,"value":65452},"AssemblyVersion",{"type":259,"value":54635},{"type":254,"tag":389,"props":65455,"children":65457},{"className":65456},[],[65458],{"type":259,"value":65425},{"type":259,"value":65460}," property. So if you keep that by default, do not forget to set the ",{"type":254,"tag":389,"props":65462,"children":65464},{"className":65463},[],[65465],{"type":259,"value":65452},{"type":259,"value":65467}," with the correct version number when you build the application code. In Azure Pipelines it can be done like that:",{"type":254,"tag":612,"props":65469,"children":65471},{"className":51570,"code":65470,"language":233,"meta":205,"style":205},"- task: DotNetCoreCLI@2\n  displayName: Build\n  inputs:\n    command: build\n    arguments: -p:AssemblyVersion=$(VersionPreviouslyComputed)\n",[65472],{"type":254,"tag":389,"props":65473,"children":65474},{"__ignoreMap":205},[65475,65495,65511,65523,65540],{"type":254,"tag":622,"props":65476,"children":65477},{"class":624,"line":625},[65478,65482,65486,65490],{"type":254,"tag":622,"props":65479,"children":65480},{"style":629},[65481],{"type":259,"value":17065},{"type":254,"tag":622,"props":65483,"children":65484},{"style":768},[65485],{"type":259,"value":22604},{"type":254,"tag":622,"props":65487,"children":65488},{"style":629},[65489],{"type":259,"value":474},{"type":254,"tag":622,"props":65491,"children":65492},{"style":664},[65493],{"type":259,"value":65494}," DotNetCoreCLI@2\n",{"type":254,"tag":622,"props":65496,"children":65497},{"class":624,"line":635},[65498,65502,65506],{"type":254,"tag":622,"props":65499,"children":65500},{"style":768},[65501],{"type":259,"value":25132},{"type":254,"tag":622,"props":65503,"children":65504},{"style":629},[65505],{"type":259,"value":474},{"type":254,"tag":622,"props":65507,"children":65508},{"style":664},[65509],{"type":259,"value":65510}," Build\n",{"type":254,"tag":622,"props":65512,"children":65513},{"class":624,"line":679},[65514,65519],{"type":254,"tag":622,"props":65515,"children":65516},{"style":768},[65517],{"type":259,"value":65518},"  inputs",{"type":254,"tag":622,"props":65520,"children":65521},{"style":629},[65522],{"type":259,"value":16416},{"type":254,"tag":622,"props":65524,"children":65525},{"class":624,"line":705},[65526,65531,65535],{"type":254,"tag":622,"props":65527,"children":65528},{"style":768},[65529],{"type":259,"value":65530},"    command",{"type":254,"tag":622,"props":65532,"children":65533},{"style":629},[65534],{"type":259,"value":474},{"type":254,"tag":622,"props":65536,"children":65537},{"style":664},[65538],{"type":259,"value":65539}," build\n",{"type":254,"tag":622,"props":65541,"children":65542},{"class":624,"line":732},[65543,65548,65552],{"type":254,"tag":622,"props":65544,"children":65545},{"style":768},[65546],{"type":259,"value":65547},"    arguments",{"type":254,"tag":622,"props":65549,"children":65550},{"style":629},[65551],{"type":259,"value":474},{"type":254,"tag":622,"props":65553,"children":65554},{"style":664},[65555],{"type":259,"value":65556}," -p:AssemblyVersion=$(VersionPreviouslyComputed)\n",{"type":254,"tag":255,"props":65558,"children":65559},{},[65560],{"type":259,"value":10040},{"type":254,"tag":1260,"props":65562,"children":65563},{},[65564],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":65566},[65567,65568],{"id":65181,"depth":635,"text":65184},{"id":65411,"depth":635,"text":65414},"content:1.posts:14.w12-2021-tips-learned-this-week.md","1.posts/14.w12-2021-tips-learned-this-week.md",{"_path":46,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":45,"description":65572,"lead":65573,"date":65574,"image":65575,"badge":65576,"tags":65577,"body":65578,"_type":1275,"_id":65779,"_source":231,"_file":65780,"_extension":1278},"I often see developers talking on Twitter or Dev.to about things they have learned during the previous day or the previous week. I like the idea so I decided to write my first article about tips I learned during this past week. I am not intending to write an article like this every week but from time to time when I feel I have something interesting to share or that I want to keep track of for myself.","Windows Terminal startup actions, a git config setting for submodules, and a better IntelliSense for azure pipelines vscode extension.","2021-03-07T00:00:00.000Z",{"src":9258},{"label":9260},[9262,16850,245,38712,51166],{"type":251,"children":65579,"toc":65774},[65580,65584,65590,65595,65600,65640,65649,65655,65660,65680,65708,65720,65726,65739,65744,65753,65766,65770],{"type":254,"tag":255,"props":65581,"children":65582},{},[65583],{"type":259,"value":65572},{"type":254,"tag":282,"props":65585,"children":65587},{"id":65586},"start-windows-terminal-with-multiple-panes-thanks-to-startup-actions",[65588],{"type":259,"value":65589},"Start Windows Terminal with multiple panes thanks to startup actions.",{"type":254,"tag":255,"props":65591,"children":65592},{},[65593],{"type":259,"value":65594},"The new Windows Terminal has evolved a lot since its first release in preview in 2019. It now has a lot of nice features and it keeps getting better which is awesome. I was previously using Cmder but Windows Terminal has quickly become my default terminal.",{"type":254,"tag":255,"props":65596,"children":65597},{},[65598],{"type":259,"value":65599},"One of the latest releases of Windows Terminal allows specifying startup actions in your settings which is great if you want your terminal to open multiple panes or tabs with some specific profiles when the terminal starts. You can find below an example where I tell the terminal to open 3 panes vertically on 3 different locations using my PowerShell profile for each one. It is especially interesting when your daily work is about working on different git repositories.",{"type":254,"tag":612,"props":65601,"children":65603},{"className":614,"code":65602,"language":616,"meta":205,"style":205},"\"startupActions\": \"new-tab -p PowerShell -d d:/dev/MyApi1; split-pane -p PowerShell -V -d d:/dev/MyApi2; split-pane -p PowerShell -V -d d:/dev/MyAzureFunctions\"\n",[65604],{"type":254,"tag":389,"props":65605,"children":65606},{"__ignoreMap":205},[65607],{"type":254,"tag":622,"props":65608,"children":65609},{"class":624,"line":625},[65610,65614,65619,65623,65627,65631,65636],{"type":254,"tag":622,"props":65611,"children":65612},{"style":629},[65613],{"type":259,"value":652},{"type":254,"tag":622,"props":65615,"children":65616},{"style":664},[65617],{"type":259,"value":65618},"startupActions",{"type":254,"tag":622,"props":65620,"children":65621},{"style":629},[65622],{"type":259,"value":652},{"type":254,"tag":622,"props":65624,"children":65625},{"style":1202},[65626],{"type":259,"value":9857},{"type":254,"tag":622,"props":65628,"children":65629},{"style":629},[65630],{"type":259,"value":652},{"type":254,"tag":622,"props":65632,"children":65633},{"style":664},[65634],{"type":259,"value":65635},"new-tab -p PowerShell -d d:/dev/MyApi1; split-pane -p PowerShell -V -d d:/dev/MyApi2; split-pane -p PowerShell -V -d d:/dev/MyAzureFunctions",{"type":254,"tag":622,"props":65637,"children":65638},{"style":629},[65639],{"type":259,"value":9909},{"type":254,"tag":255,"props":65641,"children":65642},{},[65643],{"type":254,"tag":1161,"props":65644,"children":65648},{"alt":65645,"className":65646,"src":65647},"A Windows terminal split int 3 PowerShell tabs.",[1165,1166],"/posts/images/w092021tips_terminal_1.png",[],{"type":254,"tag":282,"props":65650,"children":65652},{"id":65651},"a-git-config-setting-to-make-working-with-submodule-easier",[65653],{"type":259,"value":65654},"A git config setting to make working with submodule easier.",{"type":254,"tag":255,"props":65656,"children":65657},{},[65658],{"type":259,"value":65659},"On the projects I am working on, we are using submodules to share some code between different components. NuGet packages are great to share code between different projects or applications but it is sometimes a bit complicated to handle when you simply want to share a few models and services between an API and an Azure Function for instance. In these situations, it is easier to use submodules for which you don't have to handle versioning (you just reference in your repository a commit or a branch of the submodule you want to use) nor set up source link (you can directly debug the code from the submodules in your project).",{"type":254,"tag":255,"props":65661,"children":65662},{},[65663,65665,65670,65672,65678],{"type":259,"value":65664},"However, one drawback of using submodules is that you have to learn and execute a few additional git commands to manipulate submodules. Typically when you do a ",{"type":254,"tag":389,"props":65666,"children":65668},{"className":65667},[],[65669],{"type":259,"value":37984},{"type":259,"value":65671}," in your git repository, you have to do a ",{"type":254,"tag":389,"props":65673,"children":65675},{"className":65674},[],[65676],{"type":259,"value":65677},"git submodule update",{"type":259,"value":65679}," to update the submodules to their respective commit referenced in the \"super\" git repository. That is just one additional command but if you do that often it can quickly become boring 🥱.",{"type":254,"tag":255,"props":65681,"children":65682},{},[65683,65685,65691,65693,65698,65700,65707],{"type":259,"value":65684},"I never really look for a way to make that easier until this week where I discovered that you could pass the ",{"type":254,"tag":389,"props":65686,"children":65688},{"className":65687},[],[65689],{"type":259,"value":65690},"--recurse-submodules",{"type":259,"value":65692}," flag to a ",{"type":254,"tag":389,"props":65694,"children":65696},{"className":65695},[],[65697],{"type":259,"value":37984},{"type":259,"value":65699}," command to automate the process. It works for other commands as well as solving other similar inconveniences. And the best is that it is available as a setting to put in your git configuration. For more information have a look at ",{"type":254,"tag":262,"props":65701,"children":65704},{"href":65702,"rel":65703},"https://git-scm.com/book/en/v2/Git-Tools-Submodules",[266],[65705],{"type":259,"value":65706},"git documentation on the topic",{"type":259,"value":594},{"type":254,"tag":255,"props":65709,"children":65710},{},[65711,65713,65719],{"type":259,"value":65712},"So just execute the following command and be more productive with submodules 🐱‍🏍:\n",{"type":254,"tag":389,"props":65714,"children":65716},{"className":65715},[],[65717],{"type":259,"value":65718},"git config submodule.recurse true",{"type":259,"value":594},{"type":254,"tag":282,"props":65721,"children":65723},{"id":65722},"intellisense-for-azure-pipelines-custom-tasks-in-vscode",[65724],{"type":259,"value":65725},"IntelliSense for Azure Pipelines custom tasks in vscode.",{"type":254,"tag":255,"props":65727,"children":65728},{},[65729,65731,65737],{"type":259,"value":65730},"Azure Pipelines is an awesome CI/CD tool to automate your builds and deployments. The only problem when writing pipelines is that you have to write YAML  😿. I guess we just have to get used to writing YAML because it has become something used everywhere. Fortunately, there is a ",{"type":254,"tag":262,"props":65732,"children":65735},{"href":65733,"rel":65734},"https://marketplace.visualstudio.com/items?itemName=ms-azure-devops.azure-pipelines",[266],[65736],{"type":259,"value":53727},{"type":259,"value":65738}," that helps writing Azure Pipelines files by providing syntax highlighting and autocompletion for Azure Pipelines YAML files in vscode.",{"type":254,"tag":255,"props":65740,"children":65741},{},[65742],{"type":259,"value":65743},"The extension validates the YAML files by using a generic YAML schema containing the in-box tasks of Azure Pipelines yet that means it is not able to validate tasks that come from extensions you installed in Azure DevOps. But here comes the good news: you can provide your custom schema to the extension so that the extension knows how to validate all the Azure Pipelines tasks available in your Azure DevOps organization. That is not really a tip because it is a well-documented feature of the extension, I just did not take the time to read the documentation to find out about it 😅.",{"type":254,"tag":255,"props":65745,"children":65746},{},[65747],{"type":254,"tag":1161,"props":65748,"children":65752},{"alt":65749,"className":65750,"src":65751},"Documentation of the Azure Pipelines vscode extension about YAML schema.",[1165,1166],"/posts/images/w092021tips_vscodeextension_1.png",[],{"type":254,"tag":255,"props":65754,"children":65755},{},[65756,65758,65764],{"type":259,"value":65757},"As you can see in the documentation, you just have to download the custom schema of your DevOps organization which is located at this URL ",{"type":254,"tag":389,"props":65759,"children":65761},{"className":65760},[],[65762],{"type":259,"value":65763},"https://dev.azure.com/YOU-ORG-HERE/_apis/distributedtask/yamlschema",{"type":259,"value":65765},", reference it in you vs code workspace settings to make it work and enjoy IntelliSense on custom tasks.",{"type":254,"tag":255,"props":65767,"children":65768},{},[65769],{"type":259,"value":10040},{"type":254,"tag":1260,"props":65771,"children":65772},{},[65773],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":65775},[65776,65777,65778],{"id":65586,"depth":635,"text":65589},{"id":65651,"depth":635,"text":65654},{"id":65722,"depth":635,"text":65725},"content:1.posts:13.w09-2021-tips-learned-this-week.md","1.posts/13.w09-2021-tips-learned-this-week.md",{"_path":43,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":42,"description":65782,"lead":65783,"date":65784,"image":65785,"badge":65787,"tags":65788,"body":65790,"_type":1275,"_id":66255,"_source":231,"_file":66256,"_extension":1278},"DevOps has been the trendy term for quite a few years now. Lots of companies including the biggest companies in Silicon Valley have started adopting DevOps practices. However the DevOps journey is not an easy one, so you might wonder if DevOps is suited for you and your company. Do now worry anymore, you can forget about DevOps because your company is probably already using the best practices in IT, and these are not DevOps practices. Let's take a deep dive into some of these practices in this article.","Best practices in IT.","2021-02-11T00:00:00.000Z",{"src":65786},"/images/books_1.jpg",{"label":10059},[16349,65789],"IT",{"type":251,"children":65791,"toc":66242},[65792,65796,65804,65810,65815,65820,65829,65834,65840,65845,65863,65868,65891,65900,65905,65910,65916,65921,65939,65948,65953,65958,65964,65969,65974,65980,65985,65990,65995,66013,66022,66027,66056,66061,66067,66072,66077,66083,66088,66093,66098,66107,66112,66117,66123,66128,66133,66142,66147,66156,66162,66167,66172,66181,66186,66194,66200,66205,66210,66215],{"type":254,"tag":255,"props":65793,"children":65794},{},[65795],{"type":259,"value":65782},{"type":254,"tag":427,"props":65797,"children":65798},{"icon":62755},[65799],{"type":254,"tag":255,"props":65800,"children":65801},{},[65802],{"type":259,"value":65803},"The title as most of this article (apart from this paragraph and the conclusion) is highly ironic. I wanted to talk about the wrong ways of working in IT that I have seen or heard about, and I thought that if I talked about them as if they were best practices it would be more obvious why they are so wrong. Some of the \"best practices\" described in the article come from things I read on the web, some from things I witnessed in companies I worked for either as an employee or as a contractor, and some from discussions I had with IT people working in other companies. Yet, wherever they come from I deliberately exaggerated and transformed them to make them funny. The aim of this article is not to criticize everything but to make people realize that some decisions they take or ways of working they like can have a really bad impact on their projects, their team, other teams, and even their entire company.",{"type":254,"tag":282,"props":65805,"children":65807},{"id":65806},"no-spec-low-spec",[65808],{"type":259,"value":65809},"No-spec & Low-spec",{"type":254,"tag":255,"props":65811,"children":65812},{},[65813],{"type":259,"value":65814},"You may have heard of \"no-code and low-code\" platforms that aim at creating applications using GUI tools instead of writing code. With no-spec and low-spec, the concept is similar but applied to specifications: the goal is to create applications without providing specifications or the fewer specifications possible.",{"type":254,"tag":255,"props":65816,"children":65817},{},[65818],{"type":259,"value":65819},"The low spec practice is very useful when a Product Owner does not know what features need to be implemented in an application. By staying deliberately vague with \"high-level specifications\", unclear PowerPoint slides, or oral directives instead of a real backlog, a Product Owner can ensure that the lack of functional requirements will be the problem of the development team instead of his. Once the implementation is finished, the Product Owner can even ask the development team to write the specification retroactively based on the code they wrote. I am using the term \"Product Owner\" but when doing low spec the person responsible for the functional specifications is generally not part of the team developing the product and not very available to work with the developers which leads to creating funny roles like \"Proxy Product Owner\" in the team 🤔.",{"type":254,"tag":255,"props":65821,"children":65822},{},[65823],{"type":254,"tag":1161,"props":65824,"children":65828},{"alt":65825,"className":65826,"src":65827},"Posts it.",[1165,1166],"/posts/images/devopsfuture_postit_1.jpg",[],{"type":254,"tag":255,"props":65830,"children":65831},{},[65832],{"type":259,"value":65833},"When there is no written specification at all, we can talk about \"no spec\". For instance, a manager asking the developer team to \"build an API\" on a business domain without providing other details. \"No spec\" practice does not mean there is nothing to do, there is generally a need sometimes urgent 🔥 but no backlog, no user stories, no document that describes what is expected. There is another name for this (that Serverless enthusiasts may like more 😉): Specless. In the same way, Serverless does not mean there is no server, Specless does not mean there are no specifications, it is just that they are in the head of someone but not written anywhere. Specless/no spec is interesting when you need to provide an occupation to a developer team, by giving them a Specless project to do you can keep them busy quite a bit of time because developers will have to guess what the business need, to understand what they need to implement and to find to whom ask their questions.",{"type":254,"tag":282,"props":65835,"children":65837},{"id":65836},"the-lead-reviewer-recipe",[65838],{"type":259,"value":65839},"The lead reviewer recipe",{"type":254,"tag":255,"props":65841,"children":65842},{},[65843],{"type":259,"value":65844},"For this recipe you will need:",{"type":254,"tag":332,"props":65846,"children":65847},{},[65848,65853,65858],{"type":254,"tag":336,"props":65849,"children":65850},{},[65851],{"type":259,"value":65852},"a new fresh project (take one that is quite hard with complex business rules to implement that require closeness between developers and the Product Owner)",{"type":254,"tag":336,"props":65854,"children":65855},{},[65856],{"type":259,"value":65857},"an experienced developer in your company",{"type":254,"tag":336,"props":65859,"children":65860},{},[65861],{"type":259,"value":65862},"an IT consulting company with offshore developers",{"type":254,"tag":255,"props":65864,"children":65865},{},[65866],{"type":259,"value":65867},"Follow the following steps :",{"type":254,"tag":332,"props":65869,"children":65870},{},[65871,65876,65881,65886],{"type":254,"tag":336,"props":65872,"children":65873},{},[65874],{"type":259,"value":65875},"Convince your boss that the new project will cost a lot less if it is done by offshore developers that will be supervised by one of the experienced developers of your company (pay attention to only take into account the short term costs, do not take into consideration the technical debt or other long term costs)",{"type":254,"tag":336,"props":65877,"children":65878},{},[65879],{"type":259,"value":65880},"Negotiate a fixed-price contract with an IT consulting company to do your project (make the price very low to ensure the consulting company will have to put only inexperienced developers on the project to be profitable)",{"type":254,"tag":336,"props":65882,"children":65883},{},[65884],{"type":259,"value":65885},"Contact an experienced developer of your company, tell him you value his skills, and offer him a position of \"technical leader\" on a new project (insist on the fact that it is a real opportunity for him to supervise a team of developers)",{"type":254,"tag":336,"props":65887,"children":65888},{},[65889],{"type":259,"value":65890},"Organize the project so that all go through the experienced developer of your company: the Product Owner should only talk to him, technical specifications sent to the offshore developers should be written by him and all the code should be reviewed by him",{"type":254,"tag":255,"props":65892,"children":65893},{},[65894],{"type":254,"tag":1161,"props":65895,"children":65899},{"alt":65896,"className":65897,"src":65898},"A close up of an old fashioned typewriter.",[1165,1166],"/posts/images/devopsfuture_review_1.jpg",[],{"type":254,"tag":255,"props":65901,"children":65902},{},[65903],{"type":259,"value":65904},"And that is it, your former experienced developer should now have become a lead reviewer: he should only have time to write technical specifications, do code reviews and answer questions from the offshore developers.",{"type":254,"tag":255,"props":65906,"children":65907},{},[65908],{"type":259,"value":65909},"Unfortunately, lead reviewers do not last long. After some time without coding, they become less and less relevant and get bored of only reviewing code. But that should not stop you, once you know the recipe it is easy to make another lead reviewer 😉!",{"type":254,"tag":282,"props":65911,"children":65913},{"id":65912},"the-meeting-game",[65914],{"type":259,"value":65915},"The meeting game",{"type":254,"tag":255,"props":65917,"children":65918},{},[65919],{"type":259,"value":65920},"The meeting game is a game played side many companies. Generally, a manager asked someone to organize a meeting with people from different teams on a topic where some important decisions need to be taken. There are 3 phases in the game: before the meeting, during the meeting, and after the meeting:",{"type":254,"tag":332,"props":65922,"children":65923},{},[65924,65929,65934],{"type":254,"tag":336,"props":65925,"children":65926},{},[65927],{"type":259,"value":65928},"In the first phase, only the organizer of the meeting plays. His goal is to find a date and time for the meeting to take place, the sooner it is the more points he earns. It seems an easy task but it can be a real challenge to find a slot when all the attendees will be available. Indeed the meeting game has become so popular in companies that everyone has already several meeting game sessions in a day. Most active players can even have 8 in a single day 🙃! When sending the meeting invites, it is really important that the organizer provides the less information possible about the topic of the meeting in order not to compromise phase 2, you will understand why in a minute.",{"type":254,"tag":336,"props":65930,"children":65931},{},[65932],{"type":259,"value":65933},"In the second phase, all the attendees of the meeting play except for the organizer who acts as referee. The goal of this phase is to guess what the agenda of the meeting is. It seems a basic question but when playing the meeting game, the agenda is never sent in the invite so most attendees have no idea what the meeting is about. And with a manager who originally requested the meeting not necessarily present, a lot of participants in the meeting (the more players the better 😉) and many meetings in a day for everyone it can become quite hard to guess what the agenda is (sometimes even the organizer does not know exactly). Generally, for a 45 minutes meeting, the game begins 10 minutes after the start time of the meeting as there are always some people coming late from another meeting, the game lasts around 20 minutes the time for someone to find out what is the agenda, and there are 15 min left to discuss and take the decisions.",{"type":254,"tag":336,"props":65935,"children":65936},{},[65937],{"type":259,"value":65938},"In the third phase, attendees have to tell their respective teams what was said and apply the decisions made during the meeting. What makes this phase difficult is that in a meeting game it is recommended not to take notes and completely forbidden for anyone to write and send a meeting report (it is considered \"cheating\"). So participants have to act accordingly to what was decided during the meeting (and sometimes nothing has been settled) with nothing written and approved by everyone to help them. It is always quite funny to see some weeks later what everyone remembers of the meeting, and what actions have been taken.",{"type":254,"tag":255,"props":65940,"children":65941},{},[65942],{"type":254,"tag":1161,"props":65943,"children":65947},{"alt":65944,"className":65945,"src":65946},"A group of dices flying through the air.",[1165,1166],"/posts/images/devopsfuture_dices_1.jpg",[],{"type":254,"tag":255,"props":65949,"children":65950},{},[65951],{"type":259,"value":65952},"There is a variant of the meeting game where the goal of some players is to highjack the meeting by making people talk about other things than the initial topic of the meeting.",{"type":254,"tag":255,"props":65954,"children":65955},{},[65956],{"type":259,"value":65957},"If you feel that some teams in your company are becoming too much productive at work, it is a good idea to impose mandatory meeting game sessions weekly or even daily.",{"type":254,"tag":282,"props":65959,"children":65961},{"id":65960},"everything-as-mail",[65962],{"type":259,"value":65963},"Everything as mail",{"type":254,"tag":255,"props":65965,"children":65966},{},[65967],{"type":259,"value":65968},"You are probably familiar with the practice of treating most things related to IT projects as code: builds and releases pipelines as code, configuration as code, and infrastructure as code just to name a few. There are many benefits to have \"everything as code\": everything is under source control and easy to reuse, everything becomes easily testable, everything can be automated thus predictable and consistent...",{"type":254,"tag":255,"props":65970,"children":65971},{},[65972],{"type":259,"value":65973},"It sounds great but code is a developer thing so forget this and instead think of something that everyone already knows and uses: emails! Emails can be used for all sorts of things and that is what will see.",{"type":254,"tag":455,"props":65975,"children":65977},{"id":65976},"specifications-as-mail",[65978],{"type":259,"value":65979},"Specifications as mail",{"type":254,"tag":255,"props":65981,"children":65982},{},[65983],{"type":259,"value":65984},"Who needs to describe User Stories in Azure Boards, Confluence, Trello, or JetBrains Space when you can write the specifications directly in an email?",{"type":254,"tag":255,"props":65986,"children":65987},{},[65988],{"type":259,"value":65989},"You will not have to struggle to fill all the mandatory fields of a work item, organize the work to do in epics, features, user stories, or whatever, with an email you can write the specifications any way you like. People can respond to your email with additional requests or questions, and by grouping your emails by conversation you will always be able to find what has been \"specified\" on a topic. Your mailbox is your source control where you will find the entire history of what has been said or decided in a project. Moreover using specifications as mail values people with organized mailboxes: the ability to quickly find the email conversation that contains the specification you are looking for becomes a necessary skill.",{"type":254,"tag":255,"props":65991,"children":65992},{},[65993],{"type":259,"value":65994},"Some people may say that it is quite difficult to track all the work there is to do just by using emails. But that is not true, be sure that when it is important work, even if it is buried deep in a conversation of your mailbox there will always be someone to unearth the topic or feature specified in this conversation. When you use specifications as mail, there is even a system to prioritize what is important. You can prioritize items in three different ways:",{"type":254,"tag":332,"props":65996,"children":65997},{},[65998,66003,66008],{"type":254,"tag":336,"props":65999,"children":66000},{},[66001],{"type":259,"value":66002},"Firstly, mark an email as important (not very effective as everyone does that)",{"type":254,"tag":336,"props":66004,"children":66005},{},[66006],{"type":259,"value":66007},"Secondly, write your email in all caps (especially useful to answer your own email to tell everyone that the work you have asked for has not been done and you are not happy about it)",{"type":254,"tag":336,"props":66009,"children":66010},{},[66011],{"type":259,"value":66012},"And last but not least, add lots of additional recipients in your email, especially VIPs in your company (always fun to make people nervous by including their line manager or their manager's manager in the conversation)",{"type":254,"tag":255,"props":66014,"children":66015},{},[66016],{"type":254,"tag":1161,"props":66017,"children":66021},{"alt":66018,"className":66019,"src":66020},"A red envelope sitting on top of a white sheet of paper.",[1165,1166],"/posts/images/devopsfuture_mail_1.jpg",[],{"type":254,"tag":255,"props":66023,"children":66024},{},[66025],{"type":259,"value":66026},"If you are more a Word person to write your specifications, you can always attach your Word specifications in the emails you exchange. This way you are still doing specifications as mail while using your favorite text editor. In that scenario, what happens generally is the following:",{"type":254,"tag":332,"props":66028,"children":66029},{},[66030,66041,66046,66051],{"type":254,"tag":336,"props":66031,"children":66032},{},[66033,66035],{"type":259,"value":66034},"You send a document by email to your colleagues with a version number in its name for instance, ",{"type":254,"tag":389,"props":66036,"children":66038},{"className":66037},[],[66039],{"type":259,"value":66040},"Awesome specifications v1.0.docx",{"type":254,"tag":336,"props":66042,"children":66043},{},[66044],{"type":259,"value":66045},"One of your colleagues modifies it, adds a line at the beginning of the document to explain the changes he or she did, and increments the version number in the document's name before sending back the document to everyone",{"type":254,"tag":336,"props":66047,"children":66048},{},[66049],{"type":259,"value":66050},"The document continues to live like that for a while as you and your colleagues are modifying it",{"type":254,"tag":336,"props":66052,"children":66053},{},[66054],{"type":259,"value":66055},"You end up having many versions of the same document in your email box, and a specifications document that starts with 3 pages of history explaining the changes between versions",{"type":254,"tag":255,"props":66057,"children":66058},{},[66059],{"type":259,"value":66060},"Well, that is not a very environmentally friendly way of handling specifications versioning but it works fine as long as everyone takes the latest version of the specifications before modifying it and that 2 people do not do changes simultaneously.",{"type":254,"tag":455,"props":66062,"children":66064},{"id":66063},"bugs-as-mail",[66065],{"type":259,"value":66066},"Bugs as mail",{"type":254,"tag":255,"props":66068,"children":66069},{},[66070],{"type":259,"value":66071},"Bugs as mail are similar to specifications as mail. The concept is to use email instead of using any bug tracker like Jira, Azure Boards, or GitHub issues. Why would you bother to create a bug in a bug tracker and provide all the required information? If you spotted a bug, you do not want to wait for this bug to be planned on a sprint depending on its priority, to wait for it to be assigned to someone, and even less to have to regularly check the status of the bug to know if it has been corrected. It is much easier to directly send an email to the developer you think can help you and exchange by email until it gets solved.",{"type":254,"tag":255,"props":66073,"children":66074},{},[66075],{"type":259,"value":66076},"To prioritize bugs, you can use the same methods you use to prioritize specifications.",{"type":254,"tag":455,"props":66078,"children":66080},{"id":66079},"infrastructure-as-mail",[66081],{"type":259,"value":66082},"Infrastructure as mail",{"type":254,"tag":255,"props":66084,"children":66085},{},[66086],{"type":259,"value":66087},"Of the different \"as mail\" practices, this one is my favorite. For that one you need to have \"traditional horizontal teams\", understand interdependent teams divided according to the software architecture: a back-end team, a front-end team, an infrastructure team, a database team...",{"type":254,"tag":255,"props":66089,"children":66090},{},[66091],{"type":259,"value":66092},"Let's take an example for you to understand the infrastructure as mail practice. Imagine you are a member of the back-end team in charge of developing the Web API on the project. You are developing a new route that requires a specific configuration setting (or environment variable) that needs to be set on the platform that hosts your application (whether it is a virtual machine, an app service, or a docker container). As the developer of this new route, you are the most relevant person to set this new setting as you exactly know what is its purpose; however, it is not in your scope so you will have to contact the infrastructure team to do it. Therefore, you will send an email to the infrastructure team explaining what needs to be set so that they do it instead of you, and that's infrastructure as mail !📧",{"type":254,"tag":255,"props":66094,"children":66095},{},[66096],{"type":259,"value":66097},"Every change that you will need to do that is related to infrastructure will require you to send an email to the infrastructure team. If you store secrets in your application in a key vault and you need to add another secret in this key vault, you write an email to add this secret. If you need to set some configuration on a cloud resource your application is using, you write an email to have this resource configured for you. You do not care whether the change is tied to what you are developing or not, you are a developer-only here to write code, the infrastructure should not be your concern, you just have to send an email specifying what change you need in the infrastructure.",{"type":254,"tag":255,"props":66099,"children":66100},{},[66101],{"type":254,"tag":1161,"props":66102,"children":66106},{"alt":66103,"className":66104,"src":66105},"A wall with many different colored envelopes on it.",[1165,1166],"/posts/images/devopsfuture_mail_2.jpg",[],{"type":254,"tag":255,"props":66108,"children":66109},{},[66110],{"type":259,"value":66111},"It is however really important to have a clear separation between the developer team and the infrastructure team. You certainly do not want to have some infrastructure people directly in your team because they could understand what features you are developing and what you are trying to achieve when you request a change in the infrastructure and because you could understand how the infrastructure works. If you have a team on a functional perimeter that can easily develop functionalities and operate them, you will be able to push quickly features to production, the customers will be happy but the business people will think development and infrastructure are easy things and you will have less consideration, time and budget.  That is not what you want! To avoid that, make sure that developers have no idea how the infrastructure is implemented and that the infrastructure team has no idea what the application code does. In doing so, you can be sure that infrastructure modifications discussed by email will be misinterpreted, that they will lead to errors, and that synchronizing application deployment and infrastructure deployment will be hard. Thus, business people will value you and your colleagues because the work you do will look very complex and as a matter of fact be very complex.",{"type":254,"tag":255,"props":66113,"children":66114},{},[66115],{"type":259,"value":66116},"What is funny with infrastructure as mail is that you can have the best engineers implementing the infrastructure, using the latest infrastructure as code tools (Pulumi, Terraform ...) in the end it always comes to exchanging emails between people.",{"type":254,"tag":282,"props":66118,"children":66120},{"id":66119},"go-big-or-go-home",[66121],{"type":259,"value":66122},"Go big or go home",{"type":254,"tag":255,"props":66124,"children":66125},{},[66126],{"type":259,"value":66127},"I do not know if this one is a best practice or just common sense. When you are building a product that is used by many customers, what you want is to have a big impact on them and bring to the product lots of new features to make the customer happy, thus what you want is to \"go big or go home\". Nowadays many people are talking about short development cycles, only delivering a few features at a time but more quickly. It may work for their small projects but you are working on big projects. You do not want the customer to only have one or two features every three weeks, you want your product to be completely transformed with big changes to impress the customer even if it takes 2 years to make them.",{"type":254,"tag":255,"props":66129,"children":66130},{},[66131],{"type":259,"value":66132},"Some people say deploying many changes at a time can be risky, especially if you are not used to deploying often, issues can occur and you will not be prepared to handle that. Those people are fools, if you have done a big testing phase and you have qualified people to deploy your software nothing can go wrong! And if ever an incident occurs, just find the person responsible and fire that person: you will learn nothing from the incident but people will have a fear of failure and will be more careful next time 😈. Concerning the fact that the needs of the customer might not correspond to what you specified or might have changed the time for your waterfall project to go to production, you should not worry too much about that if you have done a solid specifications phase. To sum up, \"Never fail\" should be your motto and not non-sense phrases like \"fail fast, recover quickly\" or \"fail fast, learn fast\".",{"type":254,"tag":255,"props":66134,"children":66135},{},[66136],{"type":254,"tag":1161,"props":66137,"children":66141},{"alt":66138,"className":66139,"src":66140},"A small turtle walking across a sandy beach.",[1165,1166],"/posts/images/devopsfuture_tortoise_1.jpg",[],{"type":254,"tag":255,"props":66143,"children":66144},{},[66145],{"type":259,"value":66146},"If someone in your organization starts talking about continuous deployment, stop him right away. How your company should work is by doing waterfall projects with a big specification phase, a huge development phase, a long Quality Assurance phase, and the time necessary to deploy all that. Each major release of your software should follow this cycle to produce new big versions every 3 to 4 years for example. And if some bugs are discovered between these major releases, fix them but do not make the mistake to deploy the bug fixes right away. Doing a patch release just for a few bugs would be ridiculous and you would risk making the customer accustomed to having problems corrected quickly. No, make the fix, let it in a drawer and wait 6 months that there are enough other corrections to deploy to make another release. This is often called a \"maintenance release\" as it contains only bug fixes. I like to call such a release a cold fix, it is like a hotfix in production but you have waited so much time that you cannot call it 'hot' 🥶. If you want to please people that want to do continuous deployment, tell them you are doing a new thing called \"slow continuous deployment\". \"Go big or go home\" is probably the practice that has always been followed in your company, so there is no reason to change: it is not because many other companies like Google or Microsoft have changed their way of working to release their products more often that you should do the same. Yes, your competitors might out-innovate you by deploying new features faster than you do, but that is better than burdening yourself with trying to transform the organization of your company.",{"type":254,"tag":255,"props":66148,"children":66149},{},[66150],{"type":254,"tag":1161,"props":66151,"children":66155},{"alt":66152,"className":66153,"src":66154},"A small waterfall in the middle of a forest.",[1165,1166],"/posts/images/devopsfuture_waterfall_1.jpg",[],{"type":254,"tag":282,"props":66157,"children":66159},{"id":66158},"divide-and-rule-among-your-company",[66160],{"type":259,"value":66161},"Divide and rule among your company",{"type":254,"tag":255,"props":66163,"children":66164},{},[66165],{"type":259,"value":66166},"Supporters of DevOps often talk about changing the organization of a company to work with vertical teams instead of horizontal teams. The aim is to have self-organizing end-to-end teams that are cross-functional (with people that have different expertise and are not just back-end people, data people, IT operations people, or UX people together) and that do not depend directly on other teams to do their work. This is a big mistake because if you have self-organizing teams in your company you lose some power and control over everyone. It is better to stay with the traditional horizontal teams, which is something that does not require any culture-shift nor transformation of the way your company is structured.",{"type":254,"tag":255,"props":66168,"children":66169},{},[66170],{"type":259,"value":66171},"If you have watched Game of Thrones, the practice \"divide and rule among your company\" is something you will find nice. Imagine each horizontal team or each department of your company as a House in Game of Thrones looking for more power that is more budget and responsibility. Even if everyone is supposed to share a common goal, each team has its own objectives, its processes, its methodology, and uses its own tools. Without saying that teams are working against each other, teams are for sure not working together. When an issue occurs, the goal for a team is not to contribute to solving it as quickly as possible but it is to make sure it can not be held responsible for the issue. With this practice, there is a toxic culture that encourages teams to blame each other: front-end team blames the back-end team, back-end team blames the QA team, QA team blames the architecture team, and everyone blames the infrastructure team... In this context, as a product/program/department manager supervising multiple horizontal teams, it is quite easy for you to maintain your power and control everything because teams are too busy fighting each other to contest the management decisions. Besides, because of dependency between teams, everyone is waiting for everyone, so projects fall behind schedule. And when a project falls behind schedule, the common understanding is that the project needs more people, thus more people are hired to work on your projects, of course, it solves nothing (quite the contrary in fact) but having more people means more budget so more responsibility for you. It is easier to play an influential role in your company when you supervise 6 teams of 10 people each than when you only have 2 teams of 4 people to manage.",{"type":254,"tag":255,"props":66173,"children":66174},{},[66175],{"type":254,"tag":1161,"props":66176,"children":66180},{"alt":66177,"className":66178,"src":66179},"A close up of a chess board with pieces on it.",[1165,1166],"/posts/images/devopsfuture_chess_1.jpg",[],{"type":254,"tag":255,"props":66182,"children":66183},{},[66184],{"type":259,"value":66185},"Of course, playing \"divide and rule among your company\" with good old does horizontal teams does not look the most trendy thing to the outside world yet it is not that complicated to be among the cool kids anyway. There is probably in your company an Application Life Management team, or maybe a team in charge of automating build and release pipelines for all the other teams, or even a team dedicated to writing scripts to build your software infrastructure. You can take that team or merge these 3 teams in one if you have these 3 teams and then just rename it to \"DevOps team\". Of course, you are not applying DevOps practices by doing that, you just have a bunch of people in a team called \"DevOps\" that automate stuff for the rest of the company, but who cares? Most people do not know what DevOps is about so they will just think \"What an innovative company, they are doing DevOps, they even have a team dedicated to doing DevOps\". In addition to that, people in this new team will feel great because they will have a new and nice job title to put on their CV: \"DevOps Engineer\" (which is non-sense as DevOps is not a job title but whatever... recruiters are looking for this). If you prefer you can also use the name \"Site Reliability Engineering\" for the name of your new team, what is important is not what the name means but how it sounds and how it can make your company shine to the outside world.",{"type":254,"tag":31130,"props":66187,"children":66189},{"className":66188},[31133,31134],[66190],{"type":254,"tag":31137,"props":66191,"children":66193},{"id":66192},"852879869998501889",[],{"type":254,"tag":282,"props":66195,"children":66197},{"id":66196},"final-words",[66198],{"type":259,"value":66199},"Final words",{"type":254,"tag":255,"props":66201,"children":66202},{},[66203],{"type":259,"value":66204},"I hope this article made you laugh or at least smile a little bit because it probably reminds you of similar \"best practices\" you have seen. And most importantly I hope it made you think about practices in your company that could be improved.",{"type":254,"tag":255,"props":66206,"children":66207},{},[66208],{"type":259,"value":66209},"Do not worry, despite all I said I am a big fan of DevOps principles 💖. I think DevOps can help to deliver more value to the end-users of the applications we are developing. Yet, DevOps practices are not an easy thing to apply as it often implies transforming the way the company is organized. Because DevOps is challenging, people prefer to keep the bad practices they gradually adopted over the years, practices like the ones I described in this article.",{"type":254,"tag":255,"props":66211,"children":66212},{},[66213],{"type":259,"value":66214},"Indeed despite our best efforts, we all make bad choices but instead of considering that it is too late to change things, we should learn from our mistakes, try to make things better, and keep improving over time. Adopting DevOps practices is a journey that never really ends: there is always something to improve.",{"type":254,"tag":255,"props":66216,"children":66217},{},[66218,66220,66226,66227,66232,66234,66240],{"type":259,"value":66219},"If you are interested in DevOps, I highly recommend reading ",{"type":254,"tag":389,"props":66221,"children":66223},{"className":66222},[],[66224],{"type":259,"value":66225},"The Phoenix Project",{"type":259,"value":10347},{"type":254,"tag":389,"props":66228,"children":66230},{"className":66229},[],[66231],{"type":259,"value":41557},{"type":259,"value":66233},", two captivating novels about DevOps that I enjoyed reading. Companies talking about how their DevOps journey is also a great way to learn more about DevOps, for instance, there is this ",{"type":254,"tag":262,"props":66235,"children":66238},{"href":66236,"rel":66237},"https://youtu.be/kgDdywJTBZ4",[266],[66239],{"type":259,"value":6292},{"type":259,"value":66241}," from Microsoft which is great. And that is the end of this article, I wish you an amazing DevOps journey too.",{"title":205,"searchDepth":635,"depth":635,"links":66243},[66244,66245,66246,66247,66252,66253,66254],{"id":65806,"depth":635,"text":65809},{"id":65836,"depth":635,"text":65839},{"id":65912,"depth":635,"text":65915},{"id":65960,"depth":635,"text":65963,"children":66248},[66249,66250,66251],{"id":65976,"depth":679,"text":65979},{"id":66063,"depth":679,"text":66066},{"id":66079,"depth":679,"text":66082},{"id":66119,"depth":635,"text":66122},{"id":66158,"depth":635,"text":66161},{"id":66196,"depth":635,"text":66199},"content:1.posts:12.devops-future.md","1.posts/12.devops-future.md",{"_path":40,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":39,"description":66258,"lead":39920,"date":66259,"image":66260,"badge":66262,"tags":66263,"body":66264,"_type":1275,"_id":66830,"_source":231,"_file":66831,"_extension":1278},"When I started writing this article I did not realize it would be so long so I decided afterward to split it into three parts that will each talk about an aspect of doing a technology watch:","2020-10-29T00:00:00.000Z",{"src":66261},"/images/passion_1.jpg",{"label":10059},[10062],{"type":251,"children":66265,"toc":66811},[66266,66270,66304,66309,66315,66320,66342,66348,66353,66359,66364,66369,66378,66383,66389,66394,66402,66407,66413,66418,66423,66428,66437,66442,66448,66461,66467,66472,66481,66487,66492,66505,66514,66528,66534,66565,66574,66579,66607,66612,66621,66627,66632,66638,66643,66648,66653,66659,66664,66669,66678,66683,66716,66725,66730,66736,66741,66761,66766,66792,66796,66801,66806],{"type":254,"tag":255,"props":66267,"children":66268},{},[66269],{"type":259,"value":66258},{"type":254,"tag":476,"props":66271,"children":66272},{},[66273,66283,66293],{"type":254,"tag":336,"props":66274,"children":66275},{},[66276],{"type":254,"tag":262,"props":66277,"children":66280},{"href":66278,"rel":66279},"https://techwatching.dev/posts/technology-watch-part1",[266],[66281],{"type":259,"value":66282},"Starting your journey with blogs and blogging/microblogging platforms",{"type":254,"tag":336,"props":66284,"children":66285},{},[66286],{"type":254,"tag":262,"props":66287,"children":66290},{"href":66288,"rel":66289},"https://www.techwatching.dev/posts/technology-watch-part2",[266],[66291],{"type":259,"value":66292},"Where to learn?",{"type":254,"tag":336,"props":66294,"children":66295},{},[66296,66302],{"type":254,"tag":262,"props":66297,"children":66300},{"href":66298,"rel":66299},"https://techwatching.dev/posts/technology-watch-part3",[266],[66301],{"type":259,"value":39920},{"type":259,"value":66303}," (this article)",{"type":254,"tag":255,"props":66305,"children":66306},{},[66307],{"type":259,"value":66308},"In the previous articles, we talked about reading blogs, using blogging/microblogging platforms, the places where to find interesting content, the different tools to learn new things... In fact, we talked mostly about things you can do all alone but I think that doing a technology watch should not be a lonely journey and that to do it you should be part of the community.",{"type":254,"tag":282,"props":66310,"children":66312},{"id":66311},"what-does-it-mean-to-be-part-of-the-community",[66313],{"type":259,"value":66314},"What does it mean \"to be part of the community\"?",{"type":254,"tag":255,"props":66316,"children":66317},{},[66318],{"type":259,"value":66319},"It is important to remind you that you are not alone in your technology watch. There are thousands and thousands of other developers working with the same technology stack or in the same area you are. These people are also trying to learn new skills and to share their existing knowledge with others. I think a technology watch should not only involve passively reading IT news. As I said when I talked about blogging and micro-blogging platforms, it is also nice to interact and discuss with other people. This is what I mean by \"be part of the community\": you are not alone, you are part of a technology community and it can help you with your technology watch and more.",{"type":254,"tag":255,"props":66321,"children":66322},{},[66323,66325,66332,66334,66340],{"type":259,"value":66324},"When I started writing this section about being part of the community, I came across this ",{"type":254,"tag":262,"props":66326,"children":66329},{"href":66327,"rel":66328},"https://getinvolved.hanselman.com/",[266],[66330],{"type":259,"value":66331},"free course",{"type":259,"value":66333}," from Scott Hanselman and Rob Conery called ",{"type":254,"tag":389,"props":66335,"children":66337},{"className":66336},[],[66338],{"type":259,"value":66339},"Get Involved",{"type":259,"value":66341}," which deals with similar topics that the ones I want to talk about here. This video production is from 2013 but most of its content is still relevant so I highly recommend it to you. Indeed be part of the community entails getting involved in this community and I want to show you how you can do that.",{"type":254,"tag":282,"props":66343,"children":66345},{"id":66344},"participate-in-qa-platforms",[66346],{"type":259,"value":66347},"Participate in Q&A platforms",{"type":254,"tag":255,"props":66349,"children":66350},{},[66351],{"type":259,"value":66352},"As a developer, I am sure you spend a lot of time looking for answers to issues you face on the internet, and that many times you find them on a Q&A website. People's questions and answers help you but you can contribute to help your peers as well by asking pertinent questions and by answering questions on these Q&A websites.",{"type":254,"tag":455,"props":66354,"children":66356},{"id":66355},"stackoverflow-the-famous-qa-platform",[66357],{"type":259,"value":66358},"StackOverflow: the famous Q&A platform",{"type":254,"tag":255,"props":66360,"children":66361},{},[66362],{"type":259,"value":66363},"Which software developer has never heard or used StackOverflow? Some people even mock developers by saying that their job is just about knowing how to use a search engine and browse StackOverflow. That is very reductive 😮 yet these are two important skills for a developer to have.",{"type":254,"tag":255,"props":66365,"children":66366},{},[66367],{"type":259,"value":66368},"If you face an issue and are looking for help there is a good chance that someone already has faced this same issue and created a question on StackOverflow about it. StackOverflow has all sorts of developer questions grouped by tags you can follow. The more you contribute (by answering other people question or asking questions) the more privileges your gain: vote up, vote down, reduce ads...",{"type":254,"tag":255,"props":66370,"children":66371},{},[66372],{"type":254,"tag":1161,"props":66373,"children":66377},{"alt":66374,"className":66375,"src":66376},"Stackoverflow tags page.",[1165,1166],"/posts/images/techwatch_stackoverflow_1.png",[],{"type":254,"tag":255,"props":66379,"children":66380},{},[66381],{"type":259,"value":66382},"I do not do it enough myself but I think it is important to contribute to StackOverflow or another Q&A platform. Whatever your seniority, there surely are some questions you can answer or comment and often questions will teach you something. Moreover, we often are in the consumer position using other people's questions to solve an issue we have but not enough in the contributor position of helping others too, so I think we should try to change that.",{"type":254,"tag":455,"props":66384,"children":66386},{"id":66385},"microsoft-qa-a-new-qa-platform",[66387],{"type":259,"value":66388},"Microsoft Q&A: a new Q&A platform",{"type":254,"tag":255,"props":66390,"children":66391},{},[66392],{"type":259,"value":66393},"Microsoft Q&A is the new kid in town. Do not expect to find the same amount of content compared to 12 years old Q&A platforms like StackOverflow. But still, I think it is important to mention it as it is now the official Microsoft site for technical questions on Microsoft products and services (and in particular Azure services).",{"type":254,"tag":255,"props":66395,"children":66396},{},[66397],{"type":254,"tag":1161,"props":66398,"children":66401},{"alt":39771,"className":66399,"src":66400},[1165,1166],"/posts/images/techwatch_qanda_1.png",[],{"type":254,"tag":255,"props":66403,"children":66404},{},[66405],{"type":259,"value":66406},"So if you have some knowledge about a specific Microsoft product or service, do not hesitate to follow the corresponding tag on Microsoft Q&A and answer some questions.",{"type":254,"tag":282,"props":66408,"children":66410},{"id":66409},"sharing-is-learning",[66411],{"type":259,"value":66412},"Sharing is learning",{"type":254,"tag":255,"props":66414,"children":66415},{},[66416],{"type":259,"value":66417},"A good way to get involved in the community is to share the experience and knowledge you have acquired on some topics over the years. Whether it be in a blog, a podcast, a YouTube channel, or a Twitter account you can positively impact the tech community. 📰 🎵 📺",{"type":254,"tag":255,"props":66419,"children":66420},{},[66421],{"type":259,"value":66422},"However, when you create and share IT content you are not only helping others you are also helping yourself. You don't see how? Well, let's say you want to write a blog article about a library you recently used in a project. To write your article, you will have to do some additional search about this library and dive into parts of the library you did not have the opportunity to explore. In the end, you are consolidating your knowledge and continuing your path on your technology watch journey 😉.",{"type":254,"tag":255,"props":66424,"children":66425},{},[66426],{"type":259,"value":66427},"But do not think you need to be a blogger or a Youtuber to share IT content with others. You can start just by doing it in your company: tell colleagues about a library you used to solve an issue, debate the front framework to choose for your next project, share with them a blog article you found interesting... I noticed that we sometimes only have technical discussions with people directly working with us on a project and do not think of exchanging with colleagues from other departments or working on different projects. 📢 You probably have an enterprise social networking service like Yammer in your company and such social networks are the perfect tools to communicate with other people in your company to share IT news, seek advice from experts about a framework you don't master, show an interesting architecture you implemented on a project... If you don't have that, just use the existing communication tools you already have: for instance both Microsoft Teams and Slack channels are nice places to discuss about technology. And I am pretty sure the time you will \"lose\" by publishing some content inside your company communication tools will be quickly compensated by what you will learn from discussing with others.",{"type":254,"tag":255,"props":66429,"children":66430},{},[66431],{"type":254,"tag":1161,"props":66432,"children":66436},{"alt":66433,"className":66434,"src":66435},"A typewriter with a sheet of paper that says sharing.",[1165,1166],"/posts/images/techwatch_sharing_1.jpg",[],{"type":254,"tag":255,"props":66438,"children":66439},{},[66440],{"type":259,"value":66441},"Another great way of sharing your knowledge is to do a technical presentation about a technology in your company. Be sure that the time you will spend preparing the presentation will make you learn a lot more than what you initially knew. And you will probably learn too from the exchange you will have with your colleagues that will attend your presentation. Most IT companies have this kind of internal talks during lunch breaks or after work to help people in the company benefit from experiences from each other. Of course, if you feel up to doing a talk publicly you can also do your talk at a conference or at a meetup which brings us to the next topic.",{"type":254,"tag":282,"props":66443,"children":66445},{"id":66444},"meet-the-community",[66446],{"type":259,"value":66447},"Meet the community",{"type":254,"tag":255,"props":66449,"children":66450},{},[66451,66453,66459],{"type":259,"value":66452},"User groups and conferences are the perfect places to interact with the community. You will meet there lots of technology enthusiasts that, like you, came to learn and to talk to fellow developers. Not only you will attend awesome talks but you will build your professional network. If you are interested in the .NET Community there is a dedicated page ",{"type":254,"tag":262,"props":66454,"children":66457},{"href":66455,"rel":66456},"https://dotnet.microsoft.com/platform/community",[266],[66458],{"type":259,"value":1414},{"type":259,"value":66460}," where you will find all sorts of ways of interacting with the .NET community.",{"type":254,"tag":455,"props":66462,"children":66464},{"id":66463},"discover-user-groups-and-meetups",[66465],{"type":259,"value":66466},"Discover user groups and meetups",{"type":254,"tag":255,"props":66468,"children":66469},{},[66470],{"type":259,"value":66471},"A user group is a group of people that use the same technology, service, or product (it could be .NET, Java, Azure, AWS for instance) and that regularly meet to talk about it, share news... A meetup is pretty similar: a community of people caring about the same thing (software craftmanship or Artificial Intelligence for example) and that organizes events to talk and learn about it. Whatever the technology you are working with, there is a very good chance that there is a user group or a meetup about it you can join near your location. That is the opportunity for you to meet passionate people outside your company and see how they use a technology in their work and exchange ideas with them. Such meetings generally take place after the working day so it is easier to attend than if it was during office hours.",{"type":254,"tag":255,"props":66473,"children":66474},{},[66475],{"type":254,"tag":1161,"props":66476,"children":66480},{"alt":66477,"className":66478,"src":66479},".NET Foundation meetup group page.",[1165,1166],"/posts/images/techwatch_meetup_1.png",[],{"type":254,"tag":455,"props":66482,"children":66484},{"id":66483},"go-to-conferences",[66485],{"type":259,"value":66486},"Go to conferences",{"type":254,"tag":255,"props":66488,"children":66489},{},[66490],{"type":259,"value":66491},"If you can convince your company to let you attend some IT conferences in the year just go for it because IT conferences are great. Sometimes you will even learn more by attending an IT conference than you would by attending a training course. Be present at an event dedicated to technology, learning, and networking is just awesome 🤩. There are many IT conferences, all very different: some are organized by a company, some are community-driven, some are about a specific topic, some are about IT in general, some are a one-day event, some last several days, some cost a lot, some are completely free... But they all have something in common, they are events that gather passionate people about technology.",{"type":254,"tag":255,"props":66493,"children":66494},{},[66495,66497,66503],{"type":259,"value":66496},"We often talk about huge conferences organized by big companies but small local conferences are nice too. I live in Bordeaux where the BDX I/0 conference](",{"type":254,"tag":262,"props":66498,"children":66501},{"href":66499,"rel":66500},"https://www.bdxio.fr/",[266],[66502],{"type":259,"value":66499},{"type":259,"value":66504},") takes place, I have attended it a couple of times and I can only recommend it. The speakers are good, talks are interesting, and it's in my city: no plane or train to take to go there. Unfortunately, there are almost no talks about .NET or Azure at BDX I/O but that means it is the opportunity for me to step out of my comfort zone and learn about other technologies. It is also a place to see former colleagues and acquaintances, IT is a small world especially in Bordeaux. I am speaking about Bordeaux because it is where I live but there is likely a developer conference that takes place in your town so do not hesitate to attend it.",{"type":254,"tag":255,"props":66506,"children":66507},{},[66508],{"type":254,"tag":1161,"props":66509,"children":66513},{"alt":66510,"className":66511,"src":66512},"BDX I/O logo.",[1165,1166],"/posts/images/techwatch_bdxioio_1.jpg",[],{"type":254,"tag":255,"props":66515,"children":66516},{},[66517,66519,66526],{"type":259,"value":66518},"Under the current circumstances with the pandemic, a lot of tech conferences have been canceled but many have also gone virtual like Microsoft events. Companies had to reinvent their events that went online and it brought ",{"type":254,"tag":262,"props":66520,"children":66523},{"href":66521,"rel":66522},"https://youtu.be/cbKubzGN4z8",[266],[66524],{"type":259,"value":66525},"interesting things",{"type":259,"value":66527}," like more inclusivity. So even if it will not replace face-to-face interaction, attending virtual conferences is a good way to interact with the community.",{"type":254,"tag":455,"props":66529,"children":66531},{"id":66530},"find-other-community-events",[66532],{"type":259,"value":66533},"Find other community events",{"type":254,"tag":255,"props":66535,"children":66536},{},[66537,66539,66546,66547,66554,66556,66563],{"type":259,"value":66538},"Conferences and meetups are not the only events where you can meet the community. Your tech community and in particular people working on developing the tech probably organize live events to share news about how technology evolves. For instance, for .NET developers, there are .NET Community Standup sessions that are organized to show off community contributions and work done by the different .NET teams. Anyone can watch and ask its questions during these live events that you can find ",{"type":254,"tag":262,"props":66540,"children":66543},{"href":66541,"rel":66542},"https://dotnet.microsoft.com/platform/community/standup",[266],[66544],{"type":259,"value":66545},"on .NET Community Standup page of .NET website",{"type":259,"value":31561},{"type":254,"tag":262,"props":66548,"children":66551},{"href":66549,"rel":66550},"https://dotnet.microsoft.com/live",[266],[66552],{"type":259,"value":66553},"on .NET Live TV",{"type":259,"value":66555},". Another good example of community events like that is ",{"type":254,"tag":262,"props":66557,"children":66560},{"href":66558,"rel":66559},"https://www.youtube.com/c/AzureFunctions",[266],[66561],{"type":259,"value":66562},"Azure Functions Live",{"type":259,"value":66564}," that occurs once a month and is the occasion to ask questions to the Azure Functions team and learn what new features they have been developing.",{"type":254,"tag":255,"props":66566,"children":66567},{},[66568],{"type":254,"tag":1161,"props":66569,"children":66573},{"alt":66570,"className":66571,"src":66572},".NET Community Standup.",[1165,1166],"/posts/images/techwatch_dotnetcommunity_1.png",[],{"type":254,"tag":255,"props":66575,"children":66576},{},[66577],{"type":259,"value":66578},"Many community events are more about watching teams that explain what they are working on rather than interacting with them. But that is not always the case, they are events where you can engage more. For example, Microsoft organizes \"developer community calls\" on Microsoft 365 topics (Microsoft Graph, Microsoft Teams...) which consist of Microsoft Teams meetings once a month and open to everyone. During these calls, the Microsoft team working on the topic of the community call will:",{"type":254,"tag":332,"props":66580,"children":66581},{},[66582,66587,66592,66597,66602],{"type":254,"tag":336,"props":66583,"children":66584},{},[66585],{"type":259,"value":66586},"show you what they are currently developing and what are they planning to develop next",{"type":254,"tag":336,"props":66588,"children":66589},{},[66590],{"type":259,"value":66591},"ask for your feedback on what they are showing you (either directly during the call or by giving you a link to a survey)",{"type":254,"tag":336,"props":66593,"children":66594},{},[66595],{"type":259,"value":66596},"tell you how to test new features or how to access private previews",{"type":254,"tag":336,"props":66598,"children":66599},{},[66600],{"type":259,"value":66601},"answer your questions and sometimes try to help with issues you are facing with the technology",{"type":254,"tag":336,"props":66603,"children":66604},{},[66605],{"type":259,"value":66606},"allow community members to present demonstrations of how they are using the technology or product in their context",{"type":254,"tag":255,"props":66608,"children":66609},{},[66610],{"type":259,"value":66611},"These community calls are meetings, so even if they are lots of people in them and you stay most of the time on mute while people are presenting, there is always a time where you can ask your questions. Being able to talk to people building the technology or the product you are using, to explain your needs and issues, to ask questions, and to give feedback is I think an awesome opportunity and the reason why these community events are so important.",{"type":254,"tag":255,"props":66613,"children":66614},{},[66615],{"type":254,"tag":1161,"props":66616,"children":66620},{"alt":66617,"className":66618,"src":66619},"List of Microsoft 365 community calls.",[1165,1166],"/posts/images/techwatch_m365community_1.png",[],{"type":254,"tag":282,"props":66622,"children":66624},{"id":66623},"contribute-to-open-source",[66625],{"type":259,"value":66626},"Contribute to open source",{"type":254,"tag":255,"props":66628,"children":66629},{},[66630],{"type":259,"value":66631},"This part could probably be the topic of an entire blog post and I guess you can already find on the web lots of articles that are written about contributing to open source. So I will try to keep it short and to focus on some aspects that are important in the context of a technology watch.",{"type":254,"tag":455,"props":66633,"children":66635},{"id":66634},"write-open-source-software",[66636],{"type":259,"value":66637},"Write open source software",{"type":254,"tag":255,"props":66639,"children":66640},{},[66641],{"type":259,"value":66642},"Maybe you have a side project that you develop during your free time that others could benefit from. Contributing to open source can be as easy as publishing your side project on GitHub or any other hosting platform for open source projects. If other developers are interested in your project, they might help you build it or simply give you feedback which is always nice.",{"type":254,"tag":255,"props":66644,"children":66645},{},[66646],{"type":259,"value":66647},"You may also want to contribute to an existing open source project that you are interested in and become a contributor to this project. Starting on an existing project which might be a few years old can seem difficult but most open source projects have tags to identify tasks that are suitable for newcomers like \"up-for-grabs\", \"good-first-issue\", \"beginner-friendly\".",{"type":254,"tag":255,"props":66649,"children":66650},{},[66651],{"type":259,"value":66652},"And sometimes, contributing happens naturally as part of your job without particularly wanting to contribute to open source. Let's take an example: the project you are working on has an issue that seems to be linked to an open source library you are using. You identify the bug causing the issue and create an issue to report it, you find the correction and create a pull request to correct the library, your pull request is merged and a new version of the library is released that solves your initial issue. That way you contributed to an open source project while doing your job and working for your project.",{"type":254,"tag":455,"props":66654,"children":66656},{"id":66655},"become-familiar-with-github",[66657],{"type":259,"value":66658},"Become familiar with GitHub",{"type":254,"tag":255,"props":66660,"children":66661},{},[66662],{"type":259,"value":66663},"It seems difficult not to mention GitHub when talking about open source. There are many great platforms people can use to host their open source projects but in my opinion, GitHub is the main one and has become the heart of the open source community.",{"type":254,"tag":255,"props":66665,"children":66666},{},[66667],{"type":259,"value":66668},"Nowadays as a developer, I think it is important to be familiar with GitHub. I know what some people will say: \"I don't have a side project hosted in GitHub and I don't really care about open source so I don't need to be familiar with GitHub\". Sure, you may have no interest in open source but the language, the framework and the libraries you are using are most likely to be open source and to have their code hosted in GitHub. That means you will end up using GitHub anyway.",{"type":254,"tag":255,"props":66670,"children":66671},{},[66672],{"type":254,"tag":1161,"props":66673,"children":66677},{"alt":66674,"className":66675,"src":66676},"GitHub website header.",[1165,1166],"/posts/images/techwatch_github_1.png",[],{"type":254,"tag":255,"props":66679,"children":66680},{},[66681],{"type":259,"value":66682},"To be honest I am quite surprised by how much time I spend on GitHub daily (and I assure you it is not to have a good time reading code from fun open source projects 😀). Yet my company or the companies I work for are not releasing their projects as open source software and are not using GitHub enterprise either. So why am I spending a lot of time on GitHub then? Well, here are some reasons why I need to use GitHub to do my job:",{"type":254,"tag":332,"props":66684,"children":66685},{},[66686,66691,66696,66701,66706,66711],{"type":254,"tag":336,"props":66687,"children":66688},{},[66689],{"type":259,"value":66690},"Look for a solution in the GitHub issues of a library or framework to help me solve an issue I am facing",{"type":254,"tag":336,"props":66692,"children":66693},{},[66694],{"type":259,"value":66695},"Use GitHub issues of an SDK to report a bug I found when using this SDK on my project",{"type":254,"tag":336,"props":66697,"children":66698},{},[66699],{"type":259,"value":66700},"Read the documentation in the GitHub wiki of a library my project uses",{"type":254,"tag":336,"props":66702,"children":66703},{},[66704],{"type":259,"value":66705},"Look for code samples on how to use a library or framework I need to implement on my project",{"type":254,"tag":336,"props":66707,"children":66708},{},[66709],{"type":259,"value":66710},"Deep dive into the code of a library or framework to understand how things are working",{"type":254,"tag":336,"props":66712,"children":66713},{},[66714],{"type":259,"value":66715},"Check the release notes of a dependency I want to upgrade on my project",{"type":254,"tag":255,"props":66717,"children":66718},{},[66719],{"type":254,"tag":1161,"props":66720,"children":66724},{"alt":66721,"className":66722,"src":66723},"An hourglass sitting on top of a pile of rocks.",[1165,1166],"/posts/images/techwatch_time_1.jpg",[],{"type":254,"tag":255,"props":66726,"children":66727},{},[66728],{"type":259,"value":66729},"These are just examples but they are many more. As I wrote, I use a lot GitHub Issues which is a really useful tool to report a bug, create a feature request, keep yourself informed about bugs or requests created by others (just click on the subscribe button in an issue), discuss on problems you could have and find an existing solution on closed issues. And when you think of it, doing that is already contributing to open source projects. You may not be writing code on a project but filling a detailed bug report and providing the code to reproduce it is already helpful. And by discussing with the maintainers of a project and other members of the community on an issue thread you can affect how a product will evolve.",{"type":254,"tag":455,"props":66731,"children":66733},{"id":66732},"contributing-not-only-a-question-of-writing-code",[66734],{"type":259,"value":66735},"Contributing, not only a question of writing code",{"type":254,"tag":255,"props":66737,"children":66738},{},[66739],{"type":259,"value":66740},"We often think contributing to open source is just pushing commits to an open source repository but it is far more than that. A good illustration of that is those 2 tweets from Miguel de Icaza (known for starting Gnome, Mono, and Xamarin projects):",{"type":254,"tag":31130,"props":66742,"children":66746},{"className":66743},[31133,66744,31134,66745],"space-x-4","items-center",[66747,66754],{"type":254,"tag":31130,"props":66748,"children":66749},{},[66750],{"type":254,"tag":31137,"props":66751,"children":66753},{"id":66752},"1100751069372735488",[],{"type":254,"tag":31130,"props":66755,"children":66756},{},[66757],{"type":254,"tag":31137,"props":66758,"children":66760},{"id":66759},"675514897065709568",[],{"type":254,"tag":255,"props":66762,"children":66763},{},[66764],{"type":259,"value":66765},"As Miguel says, documentation is very important so contributing to documentation is as much important as contributing to code. If you read the previous article from this series, you know that all Microsoft documentation is open source on GitHub hence easily editable. When browsing a page of Microsoft documentation you just have to click on the edit button to navigate to the markdown file corresponding to this page on GitHub and you can easily do a pull request to submit a change. I am mentioning it to tell you that documentation can often be an easy way to contribute to open source.",{"type":254,"tag":255,"props":66767,"children":66768},{},[66769,66771,66778,66779,66784,66785,66790],{"type":259,"value":66770},"I already talked about how creating issues and participating in existing issues of an open source project is a way of contributing to it. I talked about GitHub Issues to discuss some topics with the rest of the community but it might not be the most suitable tool for that. When it will come out of Beta, GitHub Discussions will probably be a better place than GitHub Issues to have a discussion, but meanwhile, they are other options. Indeed, many open source communities use other tools like ",{"type":254,"tag":262,"props":66772,"children":66775},{"href":66773,"rel":66774},"https://gitter.im/",[266],[66776],{"type":259,"value":66777},"Gitter",{"type":259,"value":16280},{"type":254,"tag":262,"props":66780,"children":66782},{"href":39814,"rel":66781},[266],[66783],{"type":259,"value":39818},{"type":259,"value":16287},{"type":254,"tag":262,"props":66786,"children":66788},{"href":39822,"rel":66787},[266],[66789],{"type":259,"value":39826},{"type":259,"value":66791}," (not only for 🎮 😉) to talk. But whatever the tool, interacting with others is essential to contributing to an open source project.",{"type":254,"tag":282,"props":66793,"children":66794},{"id":10572},[66795],{"type":259,"value":10575},{"type":254,"tag":255,"props":66797,"children":66798},{},[66799],{"type":259,"value":66800},"This article concludes my series of articles about how to do a technology watch. It was the opportunity for me to share my vision of an IT technology watch which involves following IT content, learning from different sources / through different ways, interacting with developers of your community, and contributing to this community.",{"type":254,"tag":255,"props":66802,"children":66803},{},[66804],{"type":259,"value":66805},"Even if you are organized, doing a proper technology watch takes a lot of time so it is really important to make your company understand that it is an essential part of your job and you should have dedicated time to do it. The choice of a technology might be what will make your next project successful or not, and you have better chances to be able to make the right choice if you stay up to date with the latest IT changes. And do not forget learning is everyone's business, whatever its seniority, time to do a technology watch should not be kept for experienced developers only.",{"type":254,"tag":255,"props":66807,"children":66808},{},[66809],{"type":259,"value":66810},"In this article, I told you how I was doing my technology watch but there are as many ways as people doing a technology watch: choose what suits you best! I hope you will have learned new things reading these articles about doing a technology watch, even if it's just a new resource, a link to bookmark, or a tool to use.",{"title":205,"searchDepth":635,"depth":635,"links":66812},[66813,66814,66818,66819,66824,66829],{"id":66311,"depth":635,"text":66314},{"id":66344,"depth":635,"text":66347,"children":66815},[66816,66817],{"id":66355,"depth":679,"text":66358},{"id":66385,"depth":679,"text":66388},{"id":66409,"depth":635,"text":66412},{"id":66444,"depth":635,"text":66447,"children":66820},[66821,66822,66823],{"id":66463,"depth":679,"text":66466},{"id":66483,"depth":679,"text":66486},{"id":66530,"depth":679,"text":66533},{"id":66623,"depth":635,"text":66626,"children":66825},[66826,66827,66828],{"id":66634,"depth":679,"text":66637},{"id":66655,"depth":679,"text":66658},{"id":66732,"depth":679,"text":66735},{"id":10572,"depth":635,"text":10575},"content:1.posts:11.technology-watch-part3.md","1.posts/11.technology-watch-part3.md",{"_path":37,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":36,"description":66833,"lead":66834,"date":66835,"image":66836,"badge":66838,"tags":66839,"body":66840,"_type":1275,"_id":67217,"_source":231,"_file":67218,"_extension":1278},"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":66837},"/images/lightning_1.jpg",{"label":1286},[1362,40204,6260,2924],{"type":251,"children":66841,"toc":67209},[66842,66863,66868,66874,66901,66910,66923,66932,66937,66943,66955,66960,66969,66974,67025,67036,67045,67050,67063,67074,67115,67124,67129,67138,67143,67149,67183,67191,67196,67200,67205],{"type":254,"tag":255,"props":66843,"children":66844},{},[66845,66847,66853,66854,66861],{"type":259,"value":66846},"The possibility to add configuration sources in Azure Functions has just been released with the latest version of ",{"type":254,"tag":389,"props":66848,"children":66850},{"className":66849},[],[66851],{"type":259,"value":66852},"Microsoft.Azure.Functions.Extensions",{"type":259,"value":28819},{"type":254,"tag":262,"props":66855,"children":66858},{"href":66856,"rel":66857},"https://www.nuget.org/packages/Microsoft.Azure.Functions.Extensions/1.1.0",[266],[66859],{"type":259,"value":66860},"NuGet package",{"type":259,"value":66862},". 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":254,"tag":255,"props":66864,"children":66865},{},[66866],{"type":259,"value":66867},"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":254,"tag":282,"props":66869,"children":66871},{"id":66870},"a-quick-reminder-about-configuration-and-secrets-in-azure-functions",[66872],{"type":259,"value":66873},"A quick reminder about configuration and secrets in Azure Functions",{"type":254,"tag":255,"props":66875,"children":66876},{},[66877,66879,66884,66886,66892,66894,66899],{"type":259,"value":66878},"Configuration used by functions in a Function App is stored in settings that can be set in the ",{"type":254,"tag":389,"props":66880,"children":66882},{"className":66881},[],[66883],{"type":259,"value":2924},{"type":259,"value":66885}," section of a Function App in Azure Portal. When developing locally you have to use a ",{"type":254,"tag":389,"props":66887,"children":66889},{"className":66888},[],[66890],{"type":259,"value":66891},"local.settings.json",{"type":259,"value":66893}," file that will contain copies of the settings stored in Azure portal. The settings from ",{"type":254,"tag":389,"props":66895,"children":66897},{"className":66896},[],[66898],{"type":259,"value":66891},{"type":259,"value":66900}," 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":254,"tag":255,"props":66902,"children":66903},{},[66904],{"type":254,"tag":1161,"props":66905,"children":66909},{"alt":66906,"className":66907,"src":66908},"Azure Function local settings file.",[1165,1166],"/posts/images/functionssecrets_localsettings_1.png",[],{"type":254,"tag":255,"props":66911,"children":66912},{},[66913,66915,66921],{"type":259,"value":66914},"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":254,"tag":262,"props":66916,"children":66918},{"href":55966,"rel":66917},[266],[66919],{"type":259,"value":66920},"Key Vault references",{"type":259,"value":66922}," 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":254,"tag":255,"props":66924,"children":66925},{},[66926],{"type":254,"tag":1161,"props":66927,"children":66931},{"alt":66928,"className":66929,"src":66930},"Function App application settings in Azure portal.",[1165,1166],"/posts/images/functionssecrets_portal_1.png",[],{"type":254,"tag":255,"props":66933,"children":66934},{},[66935],{"type":259,"value":66936},"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":254,"tag":282,"props":66938,"children":66940},{"id":66939},"the-downside-of-key-vault-references-the-local-debugging-experience",[66941],{"type":259,"value":66942},"The downside of Key Vault references: the local debugging experience",{"type":254,"tag":255,"props":66944,"children":66945},{},[66946,66948,66953],{"type":259,"value":66947},"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":254,"tag":389,"props":66949,"children":66951},{"className":66950},[],[66952],{"type":259,"value":66891},{"type":259,"value":66954}," 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":254,"tag":255,"props":66956,"children":66957},{},[66958],{"type":259,"value":66959},"If you use Visual Studio there is a GUI that will help you compare/modify local settings and Azure settings.",{"type":254,"tag":255,"props":66961,"children":66962},{},[66963],{"type":254,"tag":1161,"props":66964,"children":66968},{"alt":66965,"className":66966,"src":66967},"Function App application settings in Visual Studio.",[1165,1166],"/posts/images/functionssecrets_vs_1.png",[],{"type":254,"tag":255,"props":66970,"children":66971},{},[66972],{"type":259,"value":66973},"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":254,"tag":612,"props":66975,"children":66977},{"className":16836,"code":66976,"language":16838,"meta":205,"style":205},"func azure functionapp fetch-app-settings $FUNCTION_APP_NAME\nfunc settings decrypt\n",[66978],{"type":254,"tag":389,"props":66979,"children":66980},{"__ignoreMap":205},[66981,67009],{"type":254,"tag":622,"props":66982,"children":66983},{"class":624,"line":625},[66984,66989,66994,66999,67004],{"type":254,"tag":622,"props":66985,"children":66986},{"style":714},[66987],{"type":259,"value":66988},"func",{"type":254,"tag":622,"props":66990,"children":66991},{"style":664},[66992],{"type":259,"value":66993}," azure",{"type":254,"tag":622,"props":66995,"children":66996},{"style":664},[66997],{"type":259,"value":66998}," functionapp",{"type":254,"tag":622,"props":67000,"children":67001},{"style":664},[67002],{"type":259,"value":67003}," fetch-app-settings",{"type":254,"tag":622,"props":67005,"children":67006},{"style":1202},[67007],{"type":259,"value":67008}," $FUNCTION_APP_NAME\n",{"type":254,"tag":622,"props":67010,"children":67011},{"class":624,"line":635},[67012,67016,67020],{"type":254,"tag":622,"props":67013,"children":67014},{"style":714},[67015],{"type":259,"value":66988},{"type":254,"tag":622,"props":67017,"children":67018},{"style":664},[67019],{"type":259,"value":55394},{"type":254,"tag":622,"props":67021,"children":67022},{"style":664},[67023],{"type":259,"value":67024}," decrypt\n",{"type":254,"tag":255,"props":67026,"children":67027},{},[67028,67030,67035],{"type":259,"value":67029},"This is an example of a generated ",{"type":254,"tag":389,"props":67031,"children":67033},{"className":67032},[],[67034],{"type":259,"value":66891},{"type":259,"value":24340},{"type":254,"tag":255,"props":67037,"children":67038},{},[67039],{"type":254,"tag":1161,"props":67040,"children":67044},{"alt":67041,"className":67042,"src":67043},"Generated local settings file.",[1165,1166],"/posts/images/functionssecrets_localsettings_2.png",[],{"type":254,"tag":255,"props":67046,"children":67047},{},[67048],{"type":259,"value":67049},"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":254,"tag":255,"props":67051,"children":67052},{},[67053,67055,67062],{"type":259,"value":67054},"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":254,"tag":262,"props":67056,"children":67059},{"href":67057,"rel":67058},"https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-3.1",[266],[67060],{"type":259,"value":67061},"Key Vault configuration provider",{"type":259,"value":594},{"type":254,"tag":282,"props":67064,"children":67066},{"id":67065},"here-comes-ifunctionsconfigurationbuilder",[67067,67068],{"type":259,"value":60247},{"type":254,"tag":389,"props":67069,"children":67071},{"className":67070},[],[67072],{"type":259,"value":67073},"IFunctionsConfigurationBuilder",{"type":254,"tag":255,"props":67075,"children":67076},{},[67077,67079,67084,67086,67092,67094,67100,67102,67107,67108,67113],{"type":259,"value":67078},"If you are already familiar with dependency injection in Azure Functions, you already know the ",{"type":254,"tag":389,"props":67080,"children":67082},{"className":67081},[],[67083],{"type":259,"value":66852},{"type":259,"value":67085}," NuGet package that allows you to inherit from the ",{"type":254,"tag":389,"props":67087,"children":67089},{"className":67088},[],[67090],{"type":259,"value":67091},"FunctionStartup",{"type":259,"value":67093}," abstract class and register the different services you want to inject into your functions (you can find more about that in the ",{"type":254,"tag":262,"props":67095,"children":67098},{"href":67096,"rel":67097},"https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection",[266],[67099],{"type":259,"value":6340},{"type":259,"value":67101},"). In the latest version of this NuGet package, a new virtual method has been added to ",{"type":254,"tag":389,"props":67103,"children":67105},{"className":67104},[],[67106],{"type":259,"value":67091},{"type":259,"value":9857},{"type":254,"tag":389,"props":67109,"children":67111},{"className":67110},[],[67112],{"type":259,"value":47053},{"type":259,"value":67114},". It allows you to specify additional configuration sources you would need in your functions.",{"type":254,"tag":255,"props":67116,"children":67117},{},[67118],{"type":254,"tag":1161,"props":67119,"children":67123},{"alt":67120,"className":67121,"src":67122},"FunctionsStartup abstract class.",[1165,1166],"/posts/images/functionssecrets_vs_2.png",[],{"type":254,"tag":255,"props":67125,"children":67126},{},[67127],{"type":259,"value":67128},"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":254,"tag":255,"props":67130,"children":67131},{},[67132],{"type":254,"tag":1161,"props":67133,"children":67137},{"alt":67134,"className":67135,"src":67136},"Debugging configuration in Startup file in Visual Studio.",[1165,1166],"/posts/images/functionssecrets_vs_3.png",[],{"type":254,"tag":255,"props":67139,"children":67140},{},[67141],{"type":259,"value":67142},"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":254,"tag":282,"props":67144,"children":67146},{"id":67145},"the-triggers-case",[67147],{"type":259,"value":67148},"The triggers case",{"type":254,"tag":255,"props":67150,"children":67151},{},[67152,67154,67166,67168,67172,67174,67181],{"type":259,"value":67153},"Well in my title I said ",{"type":254,"tag":19837,"props":67155,"children":67156},{},[67157,67159,67164],{"type":259,"value":67158},"\"you ",{"type":254,"tag":9718,"props":67160,"children":67161},{},[67162],{"type":259,"value":67163},"almost",{"type":259,"value":67165}," no longer need key vault references\"",{"type":259,"value":67167}," and the ",{"type":254,"tag":9718,"props":67169,"children":67170},{},[67171],{"type":259,"value":67163},{"type":259,"value":67173}," is important. As the ",{"type":254,"tag":262,"props":67175,"children":67178},{"href":67176,"rel":67177},"https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection#customizing-configuration-sources",[266],[67179],{"type":259,"value":67180},"Azure Functions documentation",{"type":259,"value":67182}," about customizing configuration sources mentions:",{"type":254,"tag":427,"props":67184,"children":67185},{"icon":567},[67186],{"type":254,"tag":255,"props":67187,"children":67188},{},[67189],{"type":259,"value":67190},"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":254,"tag":255,"props":67192,"children":67193},{},[67194],{"type":259,"value":67195},"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":254,"tag":282,"props":67197,"children":67198},{"id":10572},[67199],{"type":259,"value":10575},{"type":254,"tag":255,"props":67201,"children":67202},{},[67203],{"type":259,"value":67204},"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":254,"tag":1260,"props":67206,"children":67207},{},[67208],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":67210},[67211,67212,67213,67215,67216],{"id":66870,"depth":635,"text":66873},{"id":66939,"depth":635,"text":66942},{"id":67065,"depth":635,"text":67214},"Here comes IFunctionsConfigurationBuilder",{"id":67145,"depth":635,"text":67148},{"id":10572,"depth":635,"text":10575},"content:1.posts:10.azure-functions-custom-configuration.md","1.posts/10.azure-functions-custom-configuration.md",{"_path":34,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":33,"description":67220,"lead":66292,"date":67221,"image":67222,"badge":67224,"tags":67225,"body":67226,"_type":1275,"_id":67884,"_source":231,"_file":67885,"_extension":1278},"Learn tech efficiently: blogs, docs, videos, interactive sites.","2020-09-18T00:00:00.000Z",{"src":67223},"/images/classroom_1.jpg",{"label":10059},[10062],{"type":251,"children":67227,"toc":67863},[67228,67232,67261,67266,67272,67277,67283,67288,67311,67339,67348,67360,67366,67420,67425,67434,67453,67462,67468,67473,67479,67484,67493,67533,67539,67553,67562,67568,67582,67588,67593,67599,67613,67618,67624,67630,67643,67657,67662,67668,67682,67691,67697,67702,67707,67716,67745,67751,67764,67796,67805,67825,67834,67839,67844,67849,67853,67858],{"type":254,"tag":255,"props":67229,"children":67230},{},[67231],{"type":259,"value":66258},{"type":254,"tag":476,"props":67233,"children":67234},{},[67235,67243,67253],{"type":254,"tag":336,"props":67236,"children":67237},{},[67238],{"type":254,"tag":262,"props":67239,"children":67241},{"href":40099,"rel":67240},[266],[67242],{"type":259,"value":66282},{"type":254,"tag":336,"props":67244,"children":67245},{},[67246,67252],{"type":254,"tag":262,"props":67247,"children":67250},{"href":67248,"rel":67249},"https://techwatching.dev/posts/technology-watch-part2",[266],[67251],{"type":259,"value":66292},{"type":259,"value":66303},{"type":254,"tag":336,"props":67254,"children":67255},{},[67256],{"type":254,"tag":262,"props":67257,"children":67259},{"href":66298,"rel":67258},[266],[67260],{"type":259,"value":39920},{"type":254,"tag":255,"props":67262,"children":67263},{},[67264],{"type":259,"value":67265},"In the previous article, we talked about blog posts and blogging/microblogging platforms. But a technology watch is not only about reading IT news and commenting on people's posts, it is above all about learning new things. That is why you have to find which medium/resource or combination of media/resources will be more efficient for you to learn from (in fact it depends on what type of learner you are).",{"type":254,"tag":282,"props":67267,"children":67269},{"id":67268},"reading",[67270],{"type":259,"value":67271},"Reading",{"type":254,"tag":255,"props":67273,"children":67274},{},[67275],{"type":259,"value":67276},"I already talked in my previous article about reading blog posts, tweets ... but these are not the only resources to read. 📚",{"type":254,"tag":455,"props":67278,"children":67280},{"id":67279},"documentation-is-your-friend",[67281],{"type":259,"value":67282},"Documentation is your friend",{"type":254,"tag":255,"props":67284,"children":67285},{},[67286],{"type":259,"value":67287},"When you want to learn more about a technology, a framework, or a tool the best place to start is its documentation 📄. Because it can sometimes be quite boring to read, documentation is often overlooked unless looking for a specific piece of information. It is a shame because there is so much useful content you can find in good documentation: concepts to understand, a functionality you did not know, tutorials to do ...",{"type":254,"tag":255,"props":67289,"children":67290},{},[67291,67293,67300,67302,67309],{"type":259,"value":67292},"Microsoft's documentation used to be scattered all over the web with lots of different sites depending on the products and the teams building them. So what they did is they built a new documentation platform from scratch to create ",{"type":254,"tag":262,"props":67294,"children":67297},{"href":67295,"rel":67296},"https://docs.microsoft.com/",[266],[67298],{"type":259,"value":67299},"Microsoft Docs",{"type":259,"value":67301}," (to know more about it watch this ",{"type":254,"tag":262,"props":67303,"children":67306},{"href":67304,"rel":67305},"https://youtu.be/EpYYe6aQjJM",[266],[67307],{"type":259,"value":67308},"NDC talk",{"type":259,"value":67310},") and it is now in my opinion, one of the best documentation platforms:",{"type":254,"tag":332,"props":67312,"children":67313},{},[67314,67319,67324,67329,67334],{"type":254,"tag":336,"props":67315,"children":67316},{},[67317],{"type":259,"value":67318},"all the Microsoft documentation are centralized in one place",{"type":254,"tag":336,"props":67320,"children":67321},{},[67322],{"type":259,"value":67323},"documentation is open source on GitHub for you to contribute to it",{"type":254,"tag":336,"props":67325,"children":67326},{},[67327],{"type":259,"value":67328},"content is easily searchable and can be bookmarked",{"type":254,"tag":336,"props":67330,"children":67331},{},[67332],{"type":259,"value":67333},"there is an API Browser to quickly find the API reference you are looking for",{"type":254,"tag":336,"props":67335,"children":67336},{},[67337],{"type":259,"value":67338},"there are nice little features like downloading documentation as PDF or dark mode 🐱‍👤",{"type":254,"tag":255,"props":67340,"children":67341},{},[67342],{"type":254,"tag":1161,"props":67343,"children":67347},{"alt":67344,"className":67345,"src":67346},"Microsoft Lean website",[1165,1166],"/posts/images/techwatch_msdocs_1.png",[],{"type":254,"tag":255,"props":67349,"children":67350},{},[67351,67353,67359],{"type":259,"value":67352},"If you are not convinced to go and have a look at Microsoft Docs, maybe you will be when I will tell you that some parts of the documentation are interactive. That means some samples of code can be run directly in the browser, for instance, the C# code on this ",{"type":254,"tag":262,"props":67354,"children":67357},{"href":67355,"rel":67356},"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions#code-try-4",[266],[67358],{"type":259,"value":51342},{"type":259,"value":594},{"type":254,"tag":455,"props":67361,"children":67363},{"id":67362},"choose-which-books-to-read",[67364],{"type":259,"value":67365},"Choose which books to read",{"type":254,"tag":255,"props":67367,"children":67368},{},[67369,67371,67382,67383,67394,67395,67406,67407,67418],{"type":259,"value":67370},"If you love reading, then grab a tech book to go into a topic in depth 📖. Unless you are only working on preview technologies, it is very likely that someone has written a book on the technology you are working on. They are books about everything in IT and there are also publishers specialized in programming and computer technology topics like ",{"type":254,"tag":262,"props":67372,"children":67375},{"href":67373,"rel":67374},"https://www.manning.com/",[266],[67376],{"type":254,"tag":389,"props":67377,"children":67379},{"className":67378},[],[67380],{"type":259,"value":67381},"Manning Publications",{"type":259,"value":16280},{"type":254,"tag":262,"props":67384,"children":67387},{"href":67385,"rel":67386},"https://www.oreilly.com/",[266],[67388],{"type":254,"tag":389,"props":67389,"children":67391},{"className":67390},[],[67392],{"type":259,"value":67393},"O'Reilly",{"type":259,"value":16280},{"type":254,"tag":262,"props":67396,"children":67399},{"href":67397,"rel":67398},"https://www.microsoftpressstore.com/",[266],[67400],{"type":254,"tag":389,"props":67401,"children":67403},{"className":67402},[],[67404],{"type":259,"value":67405},"Microsoft Press",{"type":259,"value":16280},{"type":254,"tag":262,"props":67408,"children":67411},{"href":67409,"rel":67410},"https://www.packtpub.com/",[266],[67412],{"type":254,"tag":389,"props":67413,"children":67415},{"className":67414},[],[67416],{"type":259,"value":67417},"Packt",{"type":259,"value":67419}," just to name a few. Books published by this kind of companies are written by experts in their field so should be of good quality and allow you to deep dive into things.",{"type":254,"tag":255,"props":67421,"children":67422},{},[67423],{"type":259,"value":67424},"The downside of IT books is that technologies are moving fast, so books about a specific technology quickly become outdated. What is the point of spending time reading about .NET Framework 1.0 or Windows XP books (even if the books are wonderfully written) now that we are working in .NET Core 3.1 (and soon in .NET 5.0 🐱‍🏍) and Windows 10? I don't say you wouldn't learn anything by doing so but your time would probably be better spent learning other things. And some things are important whatever the technology, the programming language, or the context: writing clean code, be able to refactor code (and especially legacy code), using Agile practices, knowing some design patterns, understanding domain driven design ... Use your favorite search engine and you will find many lists of must-read books for Software developers that talk about these topics. Some of these books have been written 20 years ago and are still completely relevant so my advice is to choose these kinds of books about general software development rather than technology-specific books.",{"type":254,"tag":255,"props":67426,"children":67427},{},[67428],{"type":254,"tag":1161,"props":67429,"children":67433},{"alt":67430,"className":67431,"src":67432},"A bunch of books stacked on top of each other.",[1165,1166],"/posts/images/techwatch_book_1.jpg",[],{"type":254,"tag":255,"props":67435,"children":67436},{},[67437,67439,67444,67446,67451],{"type":259,"value":67438},"I am fond of reading myself, yet I have trouble reading technology-related books. I am motivated at first but I find it hard not to get bored after a few chapters. Reading software books looks too much like work whereas reading should be a fun moment to relax. If you don't have this problem, reading books can be a big part of your technology watch! And whether or not you do, there is an excellent book about IT and DevOps that I really enjoyed reading and that I can only suggest you read: ",{"type":254,"tag":389,"props":67440,"children":67442},{"className":67441},[],[67443],{"type":259,"value":66225},{"type":259,"value":67445}," by Gene Kim, Kevin Behr, and George Spafford. If you have not heard of it before it is a novel, hence really easy and pleasant to read, in which you follow the problems faced by an IT manager in a company called Parts Unlimited. There is a sequel called ",{"type":254,"tag":389,"props":67447,"children":67449},{"className":67448},[],[67450],{"type":259,"value":41557},{"type":259,"value":67452}," by Gene Kim that I did not read yet but which is high on my reading list as I find awesome this kind of book that teaches you a lot in a narrative way. If you know other IT novels, don't hesitate to give their titles in the comments.",{"type":254,"tag":255,"props":67454,"children":67455},{},[67456],{"type":254,"tag":1161,"props":67457,"children":67461},{"alt":67458,"className":67459,"src":67460},"Covers of the Phoenix Project and the Unicorn Project books.",[1165,1166],"/posts/images/techwatch_books_2.png",[],{"type":254,"tag":282,"props":67463,"children":67465},{"id":67464},"watching-videos",[67466],{"type":259,"value":67467},"Watching videos",{"type":254,"tag":255,"props":67469,"children":67470},{},[67471],{"type":259,"value":67472},"If reading is not your thing then maybe watching software development videos will be a better option for you. Whatever the topic and the format (short or long videos) you will probably find lots and lots of videos 📺 to teach you what you want to learn.",{"type":254,"tag":455,"props":67474,"children":67476},{"id":67475},"find-lots-of-content-on-youtube",[67477],{"type":259,"value":67478},"Find lots of content on YouTube",{"type":254,"tag":255,"props":67480,"children":67481},{},[67482],{"type":259,"value":67483},"Among all the online video-sharing platforms you can find with technological content, the major one for me is YouTube, not because of its features but because of its diversity and wealth of content. As you can have multiple YouTube accounts linked to the same Google address you can create a dedicated account for your technological watch. Then you use this account to subscribe to all the channels you are interested in to be notified when a new video is published. Subscribing to a channel is a bit like adding a blog to your RSS feed reader, you will not watch all the videos but you want to be notified about new content in case something appeals to you. Moreover, a YouTube channel is kind of a blog with videos instead of articles. Therefore if you know some software developers with good teaching skills and they have a YouTube channel, don't hesitate to subscribe to it.",{"type":254,"tag":255,"props":67485,"children":67486},{},[67487],{"type":254,"tag":1161,"props":67488,"children":67492},{"alt":67489,"className":67490,"src":67491},"List of YouTube channels.",[1165,1166],"/posts/images/techwatch_youtube_2.png",[],{"type":254,"tag":255,"props":67494,"children":67495},{},[67496,67498,67505,67507,67514,67515,67522,67524,67531],{"type":259,"value":67497},"There are also many channels that are more organization or company channels that can be interesting too. For instance, there is a ",{"type":254,"tag":262,"props":67499,"children":67502},{"href":67500,"rel":67501},"https://www.youtube.com/channel/UCsMica-v34Irf9KVTh6xx-g",[266],[67503],{"type":259,"value":67504},"Microsoft Developer channel",{"type":259,"value":67506},", a ",{"type":254,"tag":262,"props":67508,"children":67511},{"href":67509,"rel":67510},"https://www.youtube.com/channel/UCe-f02uZgEXdHmHpC3loAQg",[266],[67512],{"type":259,"value":67513},"Xamarin Developers channel",{"type":259,"value":67506},{"type":254,"tag":262,"props":67516,"children":67519},{"href":67517,"rel":67518},"https://www.youtube.com/channel/UCvtT19MZW8dq5Wwfu6B0oxw",[266],[67520],{"type":259,"value":67521},".NET foundation channel",{"type":259,"value":67523}," and so on. I also liked the fact that some IT conferences organizations upload videos of their talks on their channel. One I particularly enjoy is ",{"type":254,"tag":262,"props":67525,"children":67528},{"href":67526,"rel":67527},"https://www.youtube.com/channel/UCTdw38Cw6jcm0atBPA39a0Q",[266],[67529],{"type":259,"value":67530},"NDC Conferences channel",{"type":259,"value":67532},". Unfortunately, I have never been to an NDC conference but their talks are awesome with great speakers and topics so it is nice to be able to watch their replays. Outside channels, on YouTube you can also come across interesting playlists about an IT topic in particular.",{"type":254,"tag":455,"props":67534,"children":67536},{"id":67535},"wants-microsoft-content-try-channel-9",[67537],{"type":259,"value":67538},"Wants Microsoft content? try Channel 9",{"type":254,"tag":255,"props":67540,"children":67541},{},[67542,67544,67551],{"type":259,"value":67543},"As someone working with Microsoft technologies every day, I cannot avoid talking about ",{"type":254,"tag":262,"props":67545,"children":67548},{"href":67546,"rel":67547},"https://channel9.msdn.com/",[266],[67549],{"type":259,"value":67550},"Channel 9",{"type":259,"value":67552},". Channel 9 is a Microsoft community site hosting videos mostly about Microsoft products and technologies. A lot of the content is organized in shows focused on an area like the Internet of Things, Artificial Intelligence, DevOps, Xamarin, Visual Studio... Episodes of these shows are published regularly. Usually, hosts shows are receiving a different guest in each episode to talk about a subject in particular, which is nice because these guests are experts in their field. Events (mostly Microsoft conferences) are live broadcasted on the site and talks of these events are available in replay there. You can sign in to the website with your Microsoft account to post comments under videos and subscribe to channels. One thing to know is that most of the videos published on Channel 9 are also published on YouTube where you can find them on specific channels.",{"type":254,"tag":255,"props":67554,"children":67555},{},[67556],{"type":254,"tag":1161,"props":67557,"children":67561},{"alt":67558,"className":67559,"src":67560},"Channel 9 website subscriptions.",[1165,1166],"/posts/images/techwatch_channel9_2.png",[],{"type":254,"tag":455,"props":67563,"children":67565},{"id":67564},"get-more-interaction-with-live-streams",[67566],{"type":259,"value":67567},"Get more interaction with live streams",{"type":254,"tag":255,"props":67569,"children":67570},{},[67571,67573,67580],{"type":259,"value":67572},"The rise of social networks showed that you don't need anymore a recording studio to make a good quality video. There are now a lot of IT professionals who share their knowledge by live streaming from home when developing software. Watching other developers live coding is one of the best ways to improve your development skills. Developers are sharing their thoughts while they are coding and you can generally interact with them and other people watching the stream via chat. It's way more interactive than just watching a recorded video. Many developer advocates are live streaming on ",{"type":254,"tag":262,"props":67574,"children":67577},{"href":67575,"rel":67576},"https://www.twitch.tv/",[266],[67578],{"type":259,"value":67579},"Twitch",{"type":259,"value":67581}," which is a service well known for video game live streaming. So it might be a good place to start if you want to start watching live streams.",{"type":254,"tag":455,"props":67583,"children":67585},{"id":67584},"finding-the-time-to-watch-videos",[67586],{"type":259,"value":67587},"Finding the time to watch videos",{"type":254,"tag":255,"props":67589,"children":67590},{},[67591],{"type":259,"value":67592},"Let's be honest, watching technical videos take time, a lot of time you may not have. But maybe you can replace one of the many Netflix TV shows I am sure you are watching with a Channel 9 show related to a domain you are interested in. And maybe there are some moments when what you do does not require your full attention and you can watch a technical video in parallel, while cooking or ironing for instance (be careful not to get burned yet 🔥). For me, it's ironing, as it is a boring activity I find it nice to be able to learn things while doing it (although if you ask my girlfriend it makes me quite slow at ironing my clothes 🤣). One tip to win a little bit of time when the speakers on a video are speaking slowly is to increase the playback speed of the video to 1,25 or 1,5 (most of the online video-sharing platforms have this feature). Of course, that will not work for live streams which is the reason why I don't often watch some: too much time-consuming.",{"type":254,"tag":282,"props":67594,"children":67596},{"id":67595},"podcasts-why-not-listening-to-one",[67597],{"type":259,"value":67598},"Podcasts: why not listening to one?",{"type":254,"tag":255,"props":67600,"children":67601},{},[67602,67604,67611],{"type":259,"value":67603},"There is a great offer of IT podcasts if listening to podcasts 🎙 is something you like. IT podcasts are often quite similar to the Channel 9 shows I talked about: they are focused on a domain (Cloud, DevOps, mobile development, ...) and podcasts hosts are receiving a different expert to talk about a topic (technology, framework, language, new feature, ...) in each episode. There are many free applications that you can download on your smartphone to search, subscribe and listen to podcasts. I only tried ",{"type":254,"tag":262,"props":67605,"children":67608},{"href":67606,"rel":67607},"https://play.google.com/store/apps/details?id=com.google.android.apps.podcasts&hl=en",[266],[67609],{"type":259,"value":67610},"Google Podcasts",{"type":259,"value":67612}," which does the job but don't hesitate to read applications comparison to find the best application for you.",{"type":254,"tag":255,"props":67614,"children":67615},{},[67616],{"type":259,"value":67617},"At first, I was quite reluctant to listen to podcasts as I think I am more a visual learner than an auditory learner. Moreover, I like to see code and not only listen to someone. But I tried anyway and I have to say some podcasts are really good and even if you don't see code you will hear about frameworks or tools you did not know about and improve your knowledge on a topic. It will never be my favorite way to learn software but I don't mind listening to a podcast episode while gardening 👨‍🌾.",{"type":254,"tag":282,"props":67619,"children":67621},{"id":67620},"learning-platforms-do-not-forget-them",[67622],{"type":259,"value":67623},"Learning platforms, do not forget them",{"type":254,"tag":455,"props":67625,"children":67627},{"id":67626},"what-are-they",[67628],{"type":259,"value":67629},"What are they ?",{"type":254,"tag":255,"props":67631,"children":67632},{},[67633,67635,67641],{"type":259,"value":67634},"Trying to answer the question ",{"type":254,"tag":389,"props":67636,"children":67638},{"className":67637},[],[67639],{"type":259,"value":67640},"Where to learn ?",{"type":259,"value":67642}," without talking about learning platforms would be wrong because they are interesting places where to acquire IT skills. So what can I say about learning platforms? Well, they are plenty of them, for some you have to pay a subscription fee whereas others are free of charge. Their content is often a mix of videos and tutorials. They sometimes offer course certificates of completion and often use gamification: the more courses you take the more badges and trophies you obtain and the higher is your level/rank on the platform.",{"type":254,"tag":255,"props":67644,"children":67645},{},[67646,67648,67655],{"type":259,"value":67647},"Learning platforms have a tendency to be bought or replaced by other services. Microsoft used to have different websites that offered training like Microsoft Virtual Academy (with lots of video courses) but they have been merged or replaced by the ",{"type":254,"tag":262,"props":67649,"children":67652},{"href":67650,"rel":67651},"https://docs.microsoft.com/en-us/learn/",[266],[67653],{"type":259,"value":67654},"Microsoft Learn platform",{"type":259,"value":67656},". One platform that I liked a lot was Code School but it disappeared when it was bought by another learning platform named Pluralsight. What I found great with Code School was that hands-on training was an important part of the courses which is often not the case with other services where learning just involves watching videos.",{"type":254,"tag":255,"props":67658,"children":67659},{},[67660],{"type":259,"value":67661},"So what learning platforms can I suggest to you?",{"type":254,"tag":455,"props":67663,"children":67665},{"id":67664},"edx-discover-a-great-open-source-platform",[67666],{"type":259,"value":67667},"edX: discover a great open source platform",{"type":254,"tag":255,"props":67669,"children":67670},{},[67671,67673,67680],{"type":259,"value":67672},"I like ",{"type":254,"tag":262,"props":67674,"children":67677},{"href":67675,"rel":67676},"https://www.edx.org/",[266],[67678],{"type":259,"value":67679},"edX",{"type":259,"value":67681}," which is a Massive Open Online Course (MOOC) provider created by Harvard and MIT and which is both non-profit and open source 🤩. They have courses from a lot of organizations (whether it be top-ranked universities or industry-leading companies) in a huge variety of domains (IT is just one among many). You can take their online courses for free and you only have to pay if you want to submit graded assessments, earn a certificate of completion, or access archived courses. Microsoft previously offered many courses on edX which was particularly useful to prepare for certifications but it does not seem the case anymore.",{"type":254,"tag":255,"props":67683,"children":67684},{},[67685],{"type":254,"tag":1161,"props":67686,"children":67690},{"alt":67687,"className":67688,"src":67689},"edX logo.",[1165,1166],"/posts/images/techwatch_edx.png",[],{"type":254,"tag":455,"props":67692,"children":67694},{"id":67693},"pluralsight-learn-from-subject-matter-experts",[67695],{"type":259,"value":67696},"Pluralsight: learn from subject-matter experts",{"type":254,"tag":255,"props":67698,"children":67699},{},[67700],{"type":259,"value":67701},"I mentioned earlier Pluralsight which offers video training courses for IT professionals. Courses are authored by subject-matter experts that earn royalties depending on the number of people watching their videos lessons. Of course the quality of the content depends on the author but Pluralsight courses are generally really good and well constructed. I find convenient the way the content is organized on the website: courses are grouped by path, each path corresponding to a topic, an area, or a certification (Microsoft, Google, AWS, ...). You can bookmark a course, follow an author, or create your channels to organize the content you are interested in.",{"type":254,"tag":255,"props":67703,"children":67704},{},[67705],{"type":259,"value":67706},"I would not rely entirely on Pluralsight to prepare for certification yet it is a huge advantage for revising. It is especially true for Microsoft certifications because of the number of courses authored by Microsoft Most Valuable Professionals. And there are also tests to measure your skills and see your skills gaps for a role. The website is not video training only, since Pluralsight acquired other learning platforms there is also some hands-on training (interactive courses and projects). Interactive courses are nice but there are too few of them and unless you are learning something from scratch you may find them too basic. That is for me the weak point of Pluralsight, not enough content to apply what is learned during video courses.",{"type":254,"tag":255,"props":67708,"children":67709},{},[67710],{"type":254,"tag":1161,"props":67711,"children":67715},{"alt":67712,"className":67713,"src":67714},"Pluralsight logo.",[1165,1166],"/posts/images/techwatch_pluralsight_1.png",[],{"type":254,"tag":255,"props":67717,"children":67718},{},[67719,67721,67727,67729,67735,67737,67744],{"type":259,"value":67720},"Unfortunately, all of this content is not free and is even pretty expensive unless your company can offer you a license. You can have one to several months for free (depending on your Visual Studio subscription) to test the platform by going to ",{"type":254,"tag":262,"props":67722,"children":67725},{"href":67723,"rel":67724},"https://my.visualstudio.com/Benefits",[266],[67726],{"type":259,"value":67723},{"type":259,"value":67728},". Another tip is to create an account ",{"type":254,"tag":262,"props":67730,"children":67733},{"href":67731,"rel":67732},"https://www.pluralsight.com/partners/microsoft/azure",[266],[67734],{"type":259,"value":1414},{"type":259,"value":67736}," that gives you access to a lot of Azure video courses for free thanks to a ",{"type":254,"tag":262,"props":67738,"children":67741},{"href":67739,"rel":67740},"https://help.pluralsight.com/help/microsoft-azure-courses",[266],[67742],{"type":259,"value":67743},"partnership between Microsoft and Pluralsight",{"type":259,"value":594},{"type":254,"tag":455,"props":67746,"children":67748},{"id":67747},"microsoft-learn-learn-by-doing",[67749],{"type":259,"value":67750},"Microsoft Learn: learn by doing",{"type":254,"tag":255,"props":67752,"children":67753},{},[67754,67756,67762],{"type":259,"value":67755},"Microsoft Learn is Microsoft new learning website launched in 2018. Course modules are categorized by products, roles, and levels which makes their content easily searchable. As for Pluralsight, modules are grouped by learning path that corresponds to a technology area or certification. The platform aims at providing people with self-paced guided learning about Microsoft products and technologies. Modules are divided into short steps which means that whether you have a 10 minutes slot or a 2 hours slot you will be able to learn at your own pace. You can bookmark modules or add them to ",{"type":254,"tag":389,"props":67757,"children":67759},{"className":67758},[],[67760],{"type":259,"value":67761},"collections",{"type":259,"value":67763}," to organize your learning content.",{"type":254,"tag":255,"props":67765,"children":67766},{},[67767,67769,67776,67778,67785,67787,67794],{"type":259,"value":67768},"Even if the platform is primarily focused on Microsoft products and technologies, you will find content about other technologies too like this ",{"type":254,"tag":262,"props":67770,"children":67773},{"href":67771,"rel":67772},"https://docs.microsoft.com/en-us/learn/paths/python-first-steps/",[266],[67774],{"type":259,"value":67775},"learning path for Python",{"type":259,"value":67777},". You will also find interesting tutorials on how to use an Azure service with a non-Microsoft technology, for instance ",{"type":254,"tag":262,"props":67779,"children":67782},{"href":67780,"rel":67781},"https://docs.microsoft.com/en-us/learn/modules/azure-spring-cloud-workshop/",[266],[67783],{"type":259,"value":67784},"this module",{"type":259,"value":67786}," about Spring Boot or ",{"type":254,"tag":262,"props":67788,"children":67791},{"href":67789,"rel":67790},"https://docs.microsoft.com/en-us/learn/modules/publish-app-service-static-web-app-api/",[266],[67792],{"type":259,"value":67793},"this one",{"type":259,"value":67795}," on how to deploy a SPA on an Azure Static Web App. Indeed, Microsoft's goal is to make you use their products: they want to show you that Azure is the \"best place for all your applications and data\" whatever the programming language or framework you are using. Yet whatever their purpose, the modules are well made and it's nice to find this kind of content on a Microsoft learning platform.",{"type":254,"tag":255,"props":67797,"children":67798},{},[67799],{"type":254,"tag":1161,"props":67800,"children":67804},{"alt":67801,"className":67802,"src":67803},"Microsoft Learn website.",[1165,1166],"/posts/images/techwatch_mslearn_1.png",[],{"type":254,"tag":255,"props":67806,"children":67807},{},[67808,67810,67815,67817,67824],{"type":259,"value":67809},"So far from what I told you, Microsoft Learn does not make the difference compared to other learning platforms. Yes, the content is of high quality overall, yes you can track your progress with knowledge checks and achievements (points, badges, and trophies) however apart from courses being authored by Microsoft all that is what most learning websites already offer. In fact, where Microsoft Learn shines is that it's a platform where you learn by doing. Text and videos are still an important part of the learning paths but they are mixed with hands-on tutorials to understand the concepts seen. Most of the time, tutorials have a context that illustrates a business or technical scenario that could happen in real life, it's not applying something just to apply something. Moreover, the tutorials often involve interactive code editing or manipulating Azure resources in Azure Cloud Shell (if you don't know what it is I mention it ",{"type":254,"tag":262,"props":67811,"children":67813},{"href":17271,"rel":67812},[266],[67814],{"type":259,"value":50948},{"type":259,"value":67816},"), all that directly available from the browser which I think is awesome. One thing I have not told you yet is that Microsoft Learn is completely free. It goes beyond that, the platform allows you to have access during a limited time to a free cloud subscription (they call it a \"Learn Sandbox\") to create and manage Azure resources you use in the tutorials: it saves you from paying the resources through your subscription or impacting an existing subscription. If you want to learn more about these great features you can read this ",{"type":254,"tag":262,"props":67818,"children":67821},{"href":67819,"rel":67820},"https://docs.microsoft.com/en-us/teamblog/microsoft-learn-tour",[266],[67822],{"type":259,"value":67823},"post",{"type":259,"value":594},{"type":254,"tag":255,"props":67826,"children":67827},{},[67828],{"type":254,"tag":1161,"props":67829,"children":67833},{"alt":67830,"className":67831,"src":67832},"An Azure tutorial on Microsoft Learn.",[1165,1166],"/posts/images/techwatch_mslearn_2.png",[],{"type":254,"tag":255,"props":67835,"children":67836},{},[67837],{"type":259,"value":67838},"I would not say that Microsoft Learn is perfect, no learning platform is and there are always things to improve. For instance, I think in some areas there are not enough advanced modules. Indeed sometimes we already know the basics of a technology but we need a learning path that teachzq us more advanced concepts about it and deep dive into it. And a thing I find unfortunate is that Microsoft Learn is not (yet?) open source, which means there is no way to contribute to a course or directly make a pull request to correct a mistake or a typing error. However, it is definitely a platform I suggest you try especially if you are working with Microsoft technologies.",{"type":254,"tag":455,"props":67840,"children":67841},{"id":43135},[67842],{"type":259,"value":67843},"What else ?",{"type":254,"tag":255,"props":67845,"children":67846},{},[67847],{"type":259,"value":67848},"Of course, there surely are a lot of other awesome learning platforms but these are the ones I use and like. Let me know what are the ones you like in the comments 😃! By the way, the book publishers I spoke of earlier often have their learning platform with offers combining books and online training. I did not try them myself but they should have interesting content. Anyway, the platform itself does not matter much, what matters is that it allows you to improve your skills.",{"type":254,"tag":282,"props":67850,"children":67851},{"id":43649},[67852],{"type":259,"value":43652},{"type":254,"tag":255,"props":67854,"children":67855},{},[67856],{"type":259,"value":67857},"In this second article of this blog post series about technology watch, I talked about where and how you can learn new things in IT. We saw you can read documentation and books, watch videos, listen to podcasts and use learning platforms. This is of course a non-exhaustive list of ways to learn but that is a good beginning.",{"type":254,"tag":255,"props":67859,"children":67860},{},[67861],{"type":259,"value":67862},"In the next article of this series, we will see what does it mean to be part of the community of developers and what it can bring to you in your technology watch.",{"title":205,"searchDepth":635,"depth":635,"links":67864},[67865,67869,67875,67876,67883],{"id":67268,"depth":635,"text":67271,"children":67866},[67867,67868],{"id":67279,"depth":679,"text":67282},{"id":67362,"depth":679,"text":67365},{"id":67464,"depth":635,"text":67467,"children":67870},[67871,67872,67873,67874],{"id":67475,"depth":679,"text":67478},{"id":67535,"depth":679,"text":67538},{"id":67564,"depth":679,"text":67567},{"id":67584,"depth":679,"text":67587},{"id":67595,"depth":635,"text":67598},{"id":67620,"depth":635,"text":67623,"children":67877},[67878,67879,67880,67881,67882],{"id":67626,"depth":679,"text":67629},{"id":67664,"depth":679,"text":67667},{"id":67693,"depth":679,"text":67696},{"id":67747,"depth":679,"text":67750},{"id":43135,"depth":679,"text":67843},{"id":43649,"depth":635,"text":43652},"content:1.posts:9.technology-watch-part2.md","1.posts/9.technology-watch-part2.md",{"_path":31,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":30,"description":67887,"lead":67888,"date":67889,"image":67890,"badge":67892,"tags":67893,"body":67894,"_type":1275,"_id":68606,"_source":231,"_file":68607,"_extension":1278},"Exploring essential technology watch strategies for developers, including leveraging blogs, microblogging platforms, and community engagement","Starting your journey with blogs and bloggings/microblogging platforms.","2020-09-16T00:00:00.000Z",{"src":67891},"/images/library_1.jpg",{"label":10059},[10062],{"type":251,"children":67895,"toc":68592},[67896,67900,67928,67934,67939,67944,67953,67958,67964,67969,67975,67980,68025,68034,68039,68044,68050,68055,68069,68078,68083,68096,68102,68107,68145,68154,68159,68222,68227,68260,68282,68288,68293,68299,68319,68328,68340,68377,68382,68388,68393,68401,68406,68444,68450,68463,68472,68477,68542,68547,68552,68560,68565,68570,68574,68579],{"type":254,"tag":255,"props":67897,"children":67898},{},[67899],{"type":259,"value":66258},{"type":254,"tag":476,"props":67901,"children":67902},{},[67903,67912,67920],{"type":254,"tag":336,"props":67904,"children":67905},{},[67906,67911],{"type":254,"tag":262,"props":67907,"children":67909},{"href":66278,"rel":67908},[266],[67910],{"type":259,"value":66282},{"type":259,"value":66303},{"type":254,"tag":336,"props":67913,"children":67914},{},[67915],{"type":254,"tag":262,"props":67916,"children":67918},{"href":67248,"rel":67917},[266],[67919],{"type":259,"value":67640},{"type":254,"tag":336,"props":67921,"children":67922},{},[67923],{"type":254,"tag":262,"props":67924,"children":67926},{"href":66298,"rel":67925},[266],[67927],{"type":259,"value":39920},{"type":254,"tag":282,"props":67929,"children":67931},{"id":67930},"about-technology-watch-in-it",[67932],{"type":259,"value":67933},"About technology watch in IT",{"type":254,"tag":255,"props":67935,"children":67936},{},[67937],{"type":259,"value":67938},"Everyone agrees that in Information Technology, where everything is evolving rapidly, doing some technology watch is very important. It's especially true for the job of developer which involves using fast-moving frameworks/libraries, trying new cloud components/tools, and keeping up to date with new programming language features/new languages...",{"type":254,"tag":255,"props":67940,"children":67941},{},[67942],{"type":259,"value":67943},"Often what you learn about a technology (whether it be reading a blog post, watching a video, or listening to a podcast) will change how you will tackle your next project and the technology choices you will make. That's why doing a technology watch is so important as it helps you always improving and succeeding in your next development projects. Even when working in a specific technology/environment, we always have something new to learn as everything keeps changing. I particularly like this quote from Einstein \"The more I learn, the more I realize how much I don't know\", it's exactly that with technology watch in software development: it's a journey that never ends.",{"type":254,"tag":255,"props":67945,"children":67946},{},[67947],{"type":254,"tag":1161,"props":67948,"children":67952},{"alt":67949,"className":67950,"src":67951},"A train track in the middle of a forest",[1165,1166],"/posts/images/techwatch_journey.jpg",[],{"type":254,"tag":255,"props":67954,"children":67955},{},[67956],{"type":259,"value":67957},"⏲ However finding the time to do a technology watch is not easy and not every IT company is ready to let their employees have some time for it (even if it could be profitable for the company in the end). Therefore it is something we often have to do in our spare time and it is not always easy to know where to start a technology watch and how to do it, hence this blog post series. The aim is to share with you different ways of learning new things in development, stay up to date with the latest IT news in your tech area, and how I do my technology watch. As my technology watch is mainly about .NET and Microsoft technologies this will be the opportunity to also share some sources and platforms especially interesting for .NET developers.",{"type":254,"tag":282,"props":67959,"children":67961},{"id":67960},"blogs-posts",[67962],{"type":259,"value":67963},"Blogs posts",{"type":254,"tag":255,"props":67965,"children":67966},{},[67967],{"type":259,"value":67968},"A great way to learn is to learn from others so reading blog posts from people sharing their experiences can be a good start to do a technology watch.",{"type":254,"tag":455,"props":67970,"children":67972},{"id":67971},"choose-which-blogs-to-read",[67973],{"type":259,"value":67974},"Choose which blogs to read",{"type":254,"tag":255,"props":67976,"children":67977},{},[67978],{"type":259,"value":67979},"If you have been a developer for a few years, you probably already have come across many interesting blogs when looking for the answer to an issue you had or more information on a specific topic. Some of these blogs might even come back frequently in your searches if the author works in the same field you work. These are the blogs you should follow and from which you should read articles because it's likely where you will find interesting articles for you.",{"type":254,"tag":255,"props":67981,"children":67982},{},[67983,67985,67992,67993,68000,68001,68008,68009,68016,68018,68024],{"type":259,"value":67984},"Often, tools or frameworks have an official blog so it might be a good idea to follow blogs from the ones you use. For example, Microsoft have a lot of great dev blogs like ",{"type":254,"tag":262,"props":67986,"children":67989},{"href":67987,"rel":67988},"https://devblogs.microsoft.com/dotnet/",[266],[67990],{"type":259,"value":67991},".NET Blog",{"type":259,"value":16280},{"type":254,"tag":262,"props":67994,"children":67997},{"href":67995,"rel":67996},"https://devblogs.microsoft.com/aspnet/",[266],[67998],{"type":259,"value":67999},"ASP.NET Blog",{"type":259,"value":16280},{"type":254,"tag":262,"props":68002,"children":68005},{"href":68003,"rel":68004},"https://devblogs.microsoft.com/xamarin/",[266],[68006],{"type":259,"value":68007},"Xamarin Blog",{"type":259,"value":16280},{"type":254,"tag":262,"props":68010,"children":68013},{"href":68011,"rel":68012},"https://devblogs.microsoft.com/devops/",[266],[68014],{"type":259,"value":68015},"Azure DevOps Blog",{"type":259,"value":68017}," (you can find the complete list ",{"type":254,"tag":262,"props":68019,"children":68022},{"href":68020,"rel":68021},"https://devblogs.microsoft.com/",[266],[68023],{"type":259,"value":1414},{"type":259,"value":2309},{"type":254,"tag":255,"props":68026,"children":68027},{},[68028],{"type":254,"tag":1161,"props":68029,"children":68033},{"alt":68030,"className":68031,"src":68032},"Microsoft Developer Blogs' banner.",[1165,1166],"/posts/images/techwatch_blogs_1.png",[],{"type":254,"tag":255,"props":68035,"children":68036},{},[68037],{"type":259,"value":68038},"Yet, I don't think that there is a must-read list of blogs that you should only read articles from, not even a list by tech stack. Because every blog author writes differently you have to find blogs that best suit what you like, both in the topics and in the format of the articles. Maybe you like to read short blog posts that just give you a high-level view of a techno or maybe on the contrary you like to read long blog posts that deep dive into a technology. It's really up to you to find what kind of articles you like to read, which blogs can make difficult frameworks/concepts to become crystal clear, and which blog authors have a writing style that appeals to you.",{"type":254,"tag":255,"props":68040,"children":68041},{},[68042],{"type":259,"value":68043},"In a nutshell read blogs that you will enjoy reading because there is no point in making yourself read things just to learn something: learning should always be a pleasure ✨.",{"type":254,"tag":455,"props":68045,"children":68047},{"id":68046},"use-an-rss-feed-reader",[68048],{"type":259,"value":68049},"Use an RSS feed reader",{"type":254,"tag":255,"props":68051,"children":68052},{},[68053],{"type":259,"value":68054},"Once you have your custom list of blogs that you want to read articles from, you should use an RSS feed reader. Of course, you can always bookmark the blogs and from time to time check if a new article has been published but honestly, that is a loss of time. Most blogs, if not all, have an RSS feed so you just have to add blogs your want to follow in your favorite RSS feed reader to keep up with new articles as they are published.",{"type":254,"tag":255,"props":68056,"children":68057},{},[68058,68060,68067],{"type":259,"value":68059},"I personally use ",{"type":254,"tag":262,"props":68061,"children":68064},{"href":68062,"rel":68063},"https://feedly.com/",[266],[68065],{"type":259,"value":68066},"Feedly",{"type":259,"value":68068}," but there are many other nice RSS feed readers. I let you do your comparison about the different RSS feed readers that exist.",{"type":254,"tag":255,"props":68070,"children":68071},{},[68072],{"type":254,"tag":1161,"props":68073,"children":68077},{"alt":68074,"className":68075,"src":68076},"Feedly screenshots with different RSS feeds displayed.",[1165,1166],"/posts/images/techwatch_feedly_1.png",[],{"type":254,"tag":255,"props":68079,"children":68080},{},[68081],{"type":259,"value":68082},"As you can see on the screenshot of my Feedly account I try to regroup blogs by topic like \"Database\", \"DevOps\", \"Xamarin\" when I can, even if it's not accurate because blogs are rarely about one subject only. There are surely better ways to organize all that or nice Feedly features that I should use but this has been working for me until now so I keep it that way for the moment. I am using the free version of Feedly and that's enough for me, even if I could benefit from some of their paid features like following Twitter feeds or hiding sponsored ads (not that much annoying as there are not that many ads but it would be better without).",{"type":254,"tag":255,"props":68084,"children":68085},{},[68086,68088,68094],{"type":259,"value":68087},"An RSS feed reader is a very useful way to aggregate a curated list of articles that you could be interested in but that doesn't mean you should read every single article that appears in your feed. Filtering articles to read is important as I guess your time is limited and even if you selected carefully which blogs to follow you won't be interested in all their articles. For instance, I recently started to use Pulumi for Infrastructure as Code so I added their ",{"type":254,"tag":262,"props":68089,"children":68092},{"href":68090,"rel":68091},"https://www.pulumi.com/blog/",[266],[68093],{"type":259,"value":32583},{"type":259,"value":68095}," in my blogs list to know about future features and updates to Pulumi. That does not mean I will read every post from their blog but at least I will be notified when they publish a new article and have less risk of missing a piece of news (like a new version). That's for me the principal advantage of an RSS feed reader: staying informed.",{"type":254,"tag":455,"props":68097,"children":68099},{"id":68098},"a-few-rss-feeds-i-like",[68100],{"type":259,"value":68101},"A few RSS feeds I like",{"type":254,"tag":255,"props":68103,"children":68104},{},[68105],{"type":259,"value":68106},"I won't tell you all the RSS feeds I am following because it would be quite long and boring but I still want to share a few of them.",{"type":254,"tag":255,"props":68108,"children":68109},{},[68110,68112,68119,68120,68127,68129,68136,68138,68143],{"type":259,"value":68111},"In the .NET world, apart from Microsoft official blogs, ",{"type":254,"tag":262,"props":68113,"children":68116},{"href":68114,"rel":68115},"https://www.infoq.com/fr/dotnet/articles/",[266],[68117],{"type":259,"value":68118},"InfoQ - .NET",{"type":259,"value":10347},{"type":254,"tag":262,"props":68121,"children":68124},{"href":68122,"rel":68123},"https://www.dotnetcurry.com/",[266],[68125],{"type":259,"value":68126},".NET Curry",{"type":259,"value":68128}," are two nice RSS feeds to follow. There are not individual blogs but websites about development with different writers and very interesting content. Another one is ",{"type":254,"tag":262,"props":68130,"children":68133},{"href":68131,"rel":68132},"https://discoverdot.net/",[266],[68134],{"type":259,"value":68135},"Discover .NET",{"type":259,"value":68137}," which is not a blog but a website built by Dave Glick (the author of ",{"type":254,"tag":262,"props":68139,"children":68141},{"href":51115,"rel":68140},[266],[68142],{"type":259,"value":51105},{"type":259,"value":68144},") that keeps track of .NET open source and community resources, hence it contains many links to .NET articles from other websites.",{"type":254,"tag":255,"props":68146,"children":68147},{},[68148],{"type":254,"tag":1161,"props":68149,"children":68153},{"alt":68150,"className":68151,"src":68152},"Discover .NET banner.",[1165,1166],"/posts/images/techwatch_discoverdotnet.png",[],{"type":254,"tag":255,"props":68155,"children":68156},{},[68157],{"type":259,"value":68158},"Among the individual blogs, here are some I particularly like:",{"type":254,"tag":332,"props":68160,"children":68161},{},[68162,68174,68186,68198,68210],{"type":254,"tag":336,"props":68163,"children":68164},{},[68165,68172],{"type":254,"tag":262,"props":68166,"children":68169},{"href":68167,"rel":68168},"https://www.stevejgordon.co.uk/",[266],[68170],{"type":259,"value":68171},"Steve Gordon's blog",{"type":259,"value":68173},": very well-written articles that deep dive into ASP.NET Core, .NET Core, C# ...",{"type":254,"tag":336,"props":68175,"children":68176},{},[68177,68184],{"type":254,"tag":262,"props":68178,"children":68181},{"href":68179,"rel":68180},"https://markheath.net/",[266],[68182],{"type":259,"value":68183},"Mark Heath's blog",{"type":259,"value":68185},": one of my favorite blog for Azure Functions (and especially Durable Functions) posts, other posts are great too",{"type":254,"tag":336,"props":68187,"children":68188},{},[68189,68196],{"type":254,"tag":262,"props":68190,"children":68193},{"href":68191,"rel":68192},"https://andrewlock.net/",[266],[68194],{"type":259,"value":68195},"Andrew Lock's blog",{"type":259,"value":68197},": another great blog about ASP.NET Core",{"type":254,"tag":336,"props":68199,"children":68200},{},[68201,68208],{"type":254,"tag":262,"props":68202,"children":68205},{"href":68203,"rel":68204},"https://codemilltech.com/",[266],[68206],{"type":259,"value":68207},"Matthew Soucoup's blog",{"type":259,"value":68209},": the place to go to understand Azure AD and Microsoft Identity among other articles about Xamarin, Azure ...",{"type":254,"tag":336,"props":68211,"children":68212},{},[68213,68220],{"type":254,"tag":262,"props":68214,"children":68217},{"href":68215,"rel":68216},"https://www.hanselman.com/blog/",[266],[68218],{"type":259,"value":68219},"Scott Hanselman's blog",{"type":259,"value":68221},": so much great content here whether it be about a new tool, framework, library, or thoughts about IT",{"type":254,"tag":255,"props":68223,"children":68224},{},[68225],{"type":259,"value":68226},"And finally, there are 3 blogs/websites that publish every day a collection of links of interesting articles about .NET and other things (they also appear in Discover .NET RSS feed):",{"type":254,"tag":332,"props":68228,"children":68229},{},[68230,68240,68250],{"type":254,"tag":336,"props":68231,"children":68232},{},[68233],{"type":254,"tag":262,"props":68234,"children":68237},{"href":68235,"rel":68236},"http://blog.cwa.me.uk/",[266],[68238],{"type":259,"value":68239},"The Morning Brew",{"type":254,"tag":336,"props":68241,"children":68242},{},[68243],{"type":254,"tag":262,"props":68244,"children":68247},{"href":68245,"rel":68246},"https://links.danrigby.com/",[266],[68248],{"type":259,"value":68249},".NET App Developer Links",{"type":254,"tag":336,"props":68251,"children":68252},{},[68253],{"type":254,"tag":262,"props":68254,"children":68257},{"href":68255,"rel":68256},"https://www.alvinashcraft.com/",[266],[68258],{"type":259,"value":68259},"Dew Drop",{"type":254,"tag":255,"props":68261,"children":68262},{},[68263,68265,68272,68273,68280],{"type":259,"value":68264},"Another way to read interesting blog posts is to find them on platforms like ",{"type":254,"tag":262,"props":68266,"children":68269},{"href":68267,"rel":68268},"https://medium.com/",[266],[68270],{"type":259,"value":68271},"Medium",{"type":259,"value":31561},{"type":254,"tag":262,"props":68274,"children":68277},{"href":68275,"rel":68276},"https://dev.to/",[266],[68278],{"type":259,"value":68279},"DEV",{"type":259,"value":68281}," which brings us to another way to do a technology watch: using blogging and micro blogging platforms.",{"type":254,"tag":282,"props":68283,"children":68285},{"id":68284},"blogging-and-microblogging-platforms",[68286],{"type":259,"value":68287},"Blogging and microblogging platforms",{"type":254,"tag":255,"props":68289,"children":68290},{},[68291],{"type":259,"value":68292},"Reading blog posts is great but it takes time and sometimes lacks interactivity especially when a blog does not contain a comments/discussions section above each article. Blogging should not be a one-way conversation but a community discussion.",{"type":254,"tag":455,"props":68294,"children":68296},{"id":68295},"give-a-try-to-blogging-platforms",[68297],{"type":259,"value":68298},"Give a try to blogging platforms",{"type":254,"tag":255,"props":68300,"children":68301},{},[68302,68304,68310,68312,68317],{"type":259,"value":68303},"What I want to talk about is not platforms that help you create your own blog/website like ",{"type":254,"tag":389,"props":68305,"children":68307},{"className":68306},[],[68308],{"type":259,"value":68309},"WordPress",{"type":259,"value":68311}," but social platforms where everyone can publish articles, read and comment articles, follow an author or a topic, and discuss with other users. There are many of these platforms, you may have heard of  ",{"type":254,"tag":389,"props":68313,"children":68315},{"className":68314},[],[68316],{"type":259,"value":68271},{"type":259,"value":68318}," which is pretty popular but which requires you to pay if you want unlimited access to articles (without a paywall once you have read too many articles) and whose content is not only about IT.",{"type":254,"tag":255,"props":68320,"children":68321},{},[68322],{"type":254,"tag":1161,"props":68323,"children":68327},{"alt":68324,"className":68325,"src":68326},"Dev.to banner.",[1165,1166],"/posts/images/techwatch_dev.png",[],{"type":254,"tag":255,"props":68329,"children":68330},{},[68331,68333,68338],{"type":259,"value":68332},"The blogging platform I use and like is ",{"type":254,"tag":262,"props":68334,"children":68336},{"href":68275,"rel":68335},[266],[68337],{"type":259,"value":68279},{"type":259,"value":68339}," which is an open source community for developers. Great content in there and a nice place to follow topics/people and interact with other developers. It's also quite easy to post content although as many people say: it's better to write on your own blog rather than to publish content on platforms that do not belong to you. If you want to publish content on a blogging platform I suggest you:",{"type":254,"tag":332,"props":68341,"children":68342},{},[68343,68348,68359,68372],{"type":254,"tag":336,"props":68344,"children":68345},{},[68346],{"type":259,"value":68347},"Write the articles on your personal blog on a domain you own",{"type":254,"tag":336,"props":68349,"children":68350},{},[68351,68353,68358],{"type":259,"value":68352},"Import some articles from your blog into that platform (really easy to do on ",{"type":254,"tag":389,"props":68354,"children":68356},{"className":68355},[],[68357],{"type":259,"value":68279},{"type":259,"value":2309},{"type":254,"tag":336,"props":68360,"children":68361},{},[68362,68364,68370],{"type":259,"value":68363},"Don't forget to set the ",{"type":254,"tag":389,"props":68365,"children":68367},{"className":68366},[],[68368],{"type":259,"value":68369},"canonical_url",{"type":259,"value":68371}," that will indicate to search engines that your articles are not duplicated content and were originally published on your blog",{"type":254,"tag":336,"props":68373,"children":68374},{},[68375],{"type":259,"value":68376},"Publish the imported articles on the platform",{"type":254,"tag":255,"props":68378,"children":68379},{},[68380],{"type":259,"value":68381},"That's what I do and this is a great way to reach a larger audience while attracting some traffic to your blog if readers want want to read more of your articles.",{"type":254,"tag":455,"props":68383,"children":68385},{"id":68384},"what-about-microblogging",[68386],{"type":259,"value":68387},"What about microblogging?",{"type":254,"tag":255,"props":68389,"children":68390},{},[68391],{"type":259,"value":68392},"According to Wikipedia:",{"type":254,"tag":31352,"props":68394,"children":68395},{},[68396],{"type":254,"tag":255,"props":68397,"children":68398},{},[68399],{"type":259,"value":68400},"Microblogging is an online broadcast medium that exists as a specific form of blogging. A microblog differs from a traditional blog in that its content is typically smaller in both actual and aggregated file size.",{"type":254,"tag":255,"props":68402,"children":68403},{},[68404],{"type":259,"value":68405},"Hence, Microblogging is not about long articles but small posts or short messages that aim at engaging with an audience. It solves the issue I was talking about earlier of lack of interactivity for traditional blogs. In a microblogging platform, you can directly comment and react to something someone you follow has just said. It's nice to quickly learn some tips and tricks in a programming language, hear about a techno that was just released, and exchange ideas with other developers.",{"type":254,"tag":255,"props":68407,"children":68408},{},[68409,68411,68418,68419,68426,68428,68435,68437,68442],{"type":259,"value":68410},"Lots of microblogging platforms exist like ",{"type":254,"tag":262,"props":68412,"children":68415},{"href":68413,"rel":68414},"https://twitter.com",[266],[68416],{"type":259,"value":68417},"Twitter",{"type":259,"value":16280},{"type":254,"tag":262,"props":68420,"children":68423},{"href":68421,"rel":68422},"https://www.tumblr.com/",[266],[68424],{"type":259,"value":68425},"Tumblr",{"type":259,"value":68427}," and even ",{"type":254,"tag":262,"props":68429,"children":68432},{"href":68430,"rel":68431},"https://www.linkedin.com/",[266],[68433],{"type":259,"value":68434},"LinkedIn",{"type":259,"value":68436},". I don't know if ",{"type":254,"tag":262,"props":68438,"children":68440},{"href":39780,"rel":68439},[266],[68441],{"type":259,"value":39784},{"type":259,"value":68443}," can be considered as a microblogging platform too as it is more a collection of forums but it's worth mentioning it because it's pretty popular among developers. As far as I am concerned the only one I use for my technology watch is Twitter so this is the only one I can speak about.",{"type":254,"tag":455,"props":68445,"children":68447},{"id":68446},"twitter-the-place-to-be-to-follow-it-content",[68448],{"type":259,"value":68449},"Twitter: the place to be to follow IT content",{"type":254,"tag":255,"props":68451,"children":68452},{},[68453,68455,68462],{"type":259,"value":68454},"Because Twitter is one of the most famous microblogging platforms, you probably know it and may even already be a user. What you might not realize is how useful such a platform can be to watch technology content. Lots of people and organizations are on Twitter so you can use it to follow a lot of things but I use it almost exclusively to follow IT content. If you are not yet on Twitter or another microblogging platform you can create a Twitter account just for your technology watch which is what I did: ",{"type":254,"tag":262,"props":68456,"children":68459},{"href":68457,"rel":68458},"https://twitter.com/techwatching",[266],[68460],{"type":259,"value":68461},"@techwatching",{"type":259,"value":594},{"type":254,"tag":255,"props":68464,"children":68465},{},[68466],{"type":254,"tag":1161,"props":68467,"children":68471},{"alt":68468,"className":68469,"src":68470},"Twitter website.",[1165,1166],"/posts/images/techwatch_twitter_1.png",[],{"type":254,"tag":255,"props":68473,"children":68474},{},[68475],{"type":259,"value":68476},"When I said previously to follow blogs of technologies (tools, programming languages, frameworks...) you use and people you like to read, the same goes for Twitter accounts to follow:",{"type":254,"tag":332,"props":68478,"children":68479},{},[68480,68494,68506,68527,68538],{"type":254,"tag":336,"props":68481,"children":68482},{},[68483,68485,68492],{"type":259,"value":68484},"if you regularly develop Azure Functions follow their ",{"type":254,"tag":262,"props":68486,"children":68489},{"href":68487,"rel":68488},"https://twitter.com/AzureFunctions",[266],[68490],{"type":259,"value":68491},"Twitter account",{"type":259,"value":68493}," to get the latest news and interesting links",{"type":254,"tag":336,"props":68495,"children":68496},{},[68497,68499],{"type":259,"value":68498},"if you are a .NET developer, follow the ",{"type":254,"tag":262,"props":68500,"children":68503},{"href":68501,"rel":68502},"https://twitter.com/dotnet",[266],[68504],{"type":259,"value":68505},".NET Twitter account",{"type":254,"tag":336,"props":68507,"children":68508},{},[68509,68511,68517,68519,68525],{"type":259,"value":68510},"if you are fond of the new cross-platform shell called ",{"type":254,"tag":262,"props":68512,"children":68515},{"href":68513,"rel":68514},"http://www.nushell.sh/",[266],[68516],{"type":259,"value":40203},{"type":259,"value":68518},", go check their ",{"type":254,"tag":262,"props":68520,"children":68523},{"href":68521,"rel":68522},"https://twitter.com/nu_shell",[266],[68524],{"type":259,"value":68491},{"type":259,"value":68526}," to see what's new (and seriously you should 😉)",{"type":254,"tag":336,"props":68528,"children":68529},{},[68530,68532],{"type":259,"value":68531},"if you like reading David Fowler's posts, read his tweets by following him on ",{"type":254,"tag":262,"props":68533,"children":68536},{"href":68534,"rel":68535},"https://twitter.com/davidfowl",[266],[68537],{"type":259,"value":68417},{"type":254,"tag":336,"props":68539,"children":68540},{},[68541],{"type":259,"value":60849},{"type":254,"tag":255,"props":68543,"children":68544},{},[68545],{"type":259,"value":68546},"These are some personal examples but there are so many people on Twitter from who we can learn. If like me you are mainly working on Microsoft technologies, you won't have trouble finding on Twitter Microsoft employees, Microsoft MVP, and Microsoft technologies users that have interesting things to say and share (and the same applies for any other stack). You only have to follow them and from time to time have a look at your feed to see what they are posting.",{"type":254,"tag":255,"props":68548,"children":68549},{},[68550],{"type":259,"value":68551},"One thing to note is when you follow someone on Twitter, you follow a whole person. I sometimes see people complaining that IT people they follow on Twitter are sharing content unrelated to IT whether it be hobbies, personal thoughts, or opinions about what happens in the world (politics, culture, society, ...). You have to realize that behind a Twitter account (not an organizational one) there is a real person with its tastes, beliefs, and emotions. A Twitter account is not a robot that is publishing IT content for you to do your technology watch so be kind and respect that. If you don't like or don't agree with some tweets, just keep scrolling, or if it bothers you unfollow the accounts but don't ask them to change who they are.",{"type":254,"tag":31130,"props":68553,"children":68555},{"className":68554},[31133,31134],[68556],{"type":254,"tag":31137,"props":68557,"children":68559},{"id":68558},"1269069589893115904",[],{"type":254,"tag":255,"props":68561,"children":68562},{},[68563],{"type":259,"value":68564},"Tweets are small so it's not like reading an entire blog post even if to be honest it can sometimes be time-consuming. Yet, this is something you can do discontinuously when you have a moment, the same way you would read Google or Bing News in public transportations. And you don't have to (and must not) read every single tweet from every Twitter account you follow, simply browse your Twitter feed, an stop for reading the tweets that caught your eyes. You can also categorize the accounts you follow using Twitter lists (one list by category/techno) and browse their corresponding feed but I find it easier to read tweets on one big feed.",{"type":254,"tag":255,"props":68566,"children":68567},{},[68568],{"type":259,"value":68569},"Like every social platform, Twitter is good at allowing people to interact with each other. People may not respond to you because they did not see your tweet/comment among all the others or because they did not have the time, yet in theory, you can publicly talk to anyone. This allows you to reach people you could not have otherwise, share ideas and sometimes get help from kind and skilled people on a specific topic. As long as you don't complain all the time and harass people with questions you are good to go 😉.",{"type":254,"tag":282,"props":68571,"children":68572},{"id":43649},[68573],{"type":259,"value":43652},{"type":254,"tag":255,"props":68575,"children":68576},{},[68577],{"type":259,"value":68578},"In this first article of this blog post series about technology watch, I talked about how to start your technology watch by reading blog articles and interacting with others on blogging and microblogging platforms. We saw how to choose the blogs and accounts to follow on websites like DEV or Twitter and we saw that using an RSS feed reader is important to keep up with latest blog articles.",{"type":254,"tag":255,"props":68580,"children":68581},{},[68582,68584,68590],{"type":259,"value":68583},"In the ",{"type":254,"tag":262,"props":68585,"children":68587},{"href":67248,"rel":68586},[266],[68588],{"type":259,"value":68589},"next article",{"type":259,"value":68591}," of this series, we will see where we can learn new things as part of our technology watch.",{"title":205,"searchDepth":635,"depth":635,"links":68593},[68594,68595,68600,68605],{"id":67930,"depth":635,"text":67933},{"id":67960,"depth":635,"text":67963,"children":68596},[68597,68598,68599],{"id":67971,"depth":679,"text":67974},{"id":68046,"depth":679,"text":68049},{"id":68098,"depth":679,"text":68101},{"id":68284,"depth":635,"text":68287,"children":68601},[68602,68603,68604],{"id":68295,"depth":679,"text":68298},{"id":68384,"depth":679,"text":68387},{"id":68446,"depth":679,"text":68449},{"id":43649,"depth":635,"text":43652},"content:1.posts:8.technology-watch-part1.md","1.posts/8.technology-watch-part1.md",{"_path":28,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":27,"description":68609,"lead":68610,"date":68611,"image":68612,"badge":68614,"tags":68615,"body":68617,"_type":1275,"_id":72565,"_source":231,"_file":72566,"_extension":1278},"If you have no interest in reading the blog post and just want the final script, you can find it on this GitHub repository.","Creating a PowerShell script to configure SSO for the tab of a Teams application.","2020-06-15T00:00:00.000Z",{"src":68613},"/images/shell_1.jpg",{"label":16349},[68616,38112,23935],"Microsoft Teams",{"type":251,"children":68618,"toc":72550},[68619,68631,68635,68647,68661,68667,68690,68695,68701,68715,68887,68892,68898,68903,68949,68961,68992,68998,69011,69066,69072,69085,69184,69190,69210,69561,69573,70022,70028,70033,70381,70386,70619,70625,70638,70839,70852,71493,71512,71932,71938,71951,71956,72150,72170,72530,72534,72546],{"type":254,"tag":255,"props":68620,"children":68621},{},[68622,68624,68630],{"type":259,"value":68623},"If you have no interest in reading the blog post and just want the final script, you can find it on this ",{"type":254,"tag":262,"props":68625,"children":68628},{"href":68626,"rel":68627},"https://github.com/TechWatching/TeamsDev/blob/master/infra/Scripts/ConfigureTeamsTabSSO.ps1",[266],[68629],{"type":259,"value":44981},{"type":259,"value":594},{"type":254,"tag":282,"props":68632,"children":68633},{"id":284},[68634],{"type":259,"value":287},{"type":254,"tag":255,"props":68636,"children":68637},{},[68638,68640,68645],{"type":259,"value":68639},"Several months ago, I supervised a student project aiming at developing a Teams application for my company. The application is mainly composed of a tab where Human Resources people can see information about arrivals and departures in the company. Once the project was finished and the first version of the application was available, I provisioned the application infrastructure on my company Azure tenant using ",{"type":254,"tag":262,"props":68641,"children":68643},{"href":10221,"rel":68642},[266],[68644],{"type":259,"value":10225},{"type":259,"value":68646}," which is a nice infrastructure as code platform.",{"type":254,"tag":255,"props":68648,"children":68649},{},[68650,68652,68659],{"type":259,"value":68651},"However, configuring Single Sign-On for the tab of the application did not seem possible with Pulumi as it internally uses Terraform Provider for AzureAD which at the time of writing doesn't have all functionalities necessary to configure this. The ",{"type":254,"tag":262,"props":68653,"children":68656},{"href":68654,"rel":68655},"http://aka.ms/teams-sso",[266],[68657],{"type":259,"value":68658},"documentation about SSO for Teams tab",{"type":259,"value":68660}," currently lists all the steps necessary to configure it from the Azure Portal, however, it mentions nothing about automating it, hence this blog post.",{"type":254,"tag":282,"props":68662,"children":68664},{"id":68663},"steps-to-create-the-powershell-script",[68665],{"type":259,"value":68666},"Steps to create the PowerShell script",{"type":254,"tag":255,"props":68668,"children":68669},{},[68670,68672,68679,68681,68688],{"type":259,"value":68671},"Usually, I prefer Azure CLI to PowerShell as I find it easier to find commands I need, but Azure CLI doesn't have yet the necessary commands. Most of the code comes from ",{"type":254,"tag":262,"props":68673,"children":68676},{"href":68674,"rel":68675},"https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/blob/master/3.-Web-api-call-Microsoft-graph-for-personal-accounts/AppCreationScripts/Configure.ps1",[266],[68677],{"type":259,"value":68678},"this script",{"type":259,"value":68680}," located in a repository of the ",{"type":254,"tag":262,"props":68682,"children":68685},{"href":68683,"rel":68684},"https://github.com/Azure-Samples",[266],[68686],{"type":259,"value":68687},"Azure Samples GitHub organization",{"type":259,"value":68689},". I took only what was necessary for Teams Tab SSO, adapted it to use Microsoft Graph objects/commands, and added missing commands.",{"type":254,"tag":255,"props":68691,"children":68692},{},[68693],{"type":259,"value":68694},"I am not an expert in PowerShell so there might be things to improve in the final script, but I hope the following steps will help you to understand how to configure SSO for your Teams Tab.",{"type":254,"tag":455,"props":68696,"children":68698},{"id":68697},"interacting-with-azure-active-directory",[68699],{"type":259,"value":68700},"Interacting with Azure Active Directory",{"type":254,"tag":255,"props":68702,"children":68703},{},[68704,68706,68713],{"type":259,"value":68705},"PowerShell has a module called ",{"type":254,"tag":262,"props":68707,"children":68710},{"href":68708,"rel":68709},"https://docs.microsoft.com/en-us/powershell/module/azuread/?view=azureadps-2.0",[266],[68711],{"type":259,"value":68712},"AzureAd",{"type":259,"value":68714}," that allow us to interact with Azure Active Directory.\nThe first step is to install this module if not already installed, import it and authenticate to Azure AD to be able to use Active Directory commands once authenticated.",{"type":254,"tag":612,"props":68716,"children":68718},{"className":10656,"code":68717,"language":10658,"meta":205,"style":205},"if ($null -eq (Get-Module -ListAvailable -Name \"AzureAD\")) { \n    Install-Module -Name \"AzureAD\" -Force\n}\n\nImport-Module AzureAD\n\nConnect-AzureAD -TenantId $tenantId\n",[68719],{"type":254,"tag":389,"props":68720,"children":68721},{"__ignoreMap":205},[68722,68791,68828,68835,68842,68855,68862],{"type":254,"tag":622,"props":68723,"children":68724},{"class":624,"line":625},[68725,68729,68734,68739,68743,68748,68752,68757,68761,68765,68769,68774,68778,68783,68787],{"type":254,"tag":622,"props":68726,"children":68727},{"style":1196},[68728],{"type":259,"value":52837},{"type":254,"tag":622,"props":68730,"children":68731},{"style":629},[68732],{"type":259,"value":68733}," ($null",{"type":254,"tag":622,"props":68735,"children":68736},{"style":629},[68737],{"type":259,"value":68738}," -eq",{"type":254,"tag":622,"props":68740,"children":68741},{"style":629},[68742],{"type":259,"value":3547},{"type":254,"tag":622,"props":68744,"children":68745},{"style":1735},[68746],{"type":259,"value":68747},"Get-Module",{"type":254,"tag":622,"props":68749,"children":68750},{"style":629},[68751],{"type":259,"value":10733},{"type":254,"tag":622,"props":68753,"children":68754},{"style":1202},[68755],{"type":259,"value":68756},"ListAvailable ",{"type":254,"tag":622,"props":68758,"children":68759},{"style":629},[68760],{"type":259,"value":17065},{"type":254,"tag":622,"props":68762,"children":68763},{"style":1202},[68764],{"type":259,"value":2395},{"type":254,"tag":622,"props":68766,"children":68767},{"style":629},[68768],{"type":259,"value":652},{"type":254,"tag":622,"props":68770,"children":68771},{"style":664},[68772],{"type":259,"value":68773},"AzureAD",{"type":254,"tag":622,"props":68775,"children":68776},{"style":629},[68777],{"type":259,"value":652},{"type":254,"tag":622,"props":68779,"children":68780},{"style":629},[68781],{"type":259,"value":68782},"))",{"type":254,"tag":622,"props":68784,"children":68785},{"style":629},[68786],{"type":259,"value":1990},{"type":254,"tag":622,"props":68788,"children":68789},{"style":1202},[68790],{"type":259,"value":2314},{"type":254,"tag":622,"props":68792,"children":68793},{"class":624,"line":635},[68794,68799,68803,68807,68811,68815,68819,68823],{"type":254,"tag":622,"props":68795,"children":68796},{"style":1735},[68797],{"type":259,"value":68798},"    Install-Module",{"type":254,"tag":622,"props":68800,"children":68801},{"style":629},[68802],{"type":259,"value":10733},{"type":254,"tag":622,"props":68804,"children":68805},{"style":1202},[68806],{"type":259,"value":2395},{"type":254,"tag":622,"props":68808,"children":68809},{"style":629},[68810],{"type":259,"value":652},{"type":254,"tag":622,"props":68812,"children":68813},{"style":664},[68814],{"type":259,"value":68773},{"type":254,"tag":622,"props":68816,"children":68817},{"style":629},[68818],{"type":259,"value":652},{"type":254,"tag":622,"props":68820,"children":68821},{"style":629},[68822],{"type":259,"value":10733},{"type":254,"tag":622,"props":68824,"children":68825},{"style":1202},[68826],{"type":259,"value":68827},"Force\n",{"type":254,"tag":622,"props":68829,"children":68830},{"class":624,"line":679},[68831],{"type":254,"tag":622,"props":68832,"children":68833},{"style":629},[68834],{"type":259,"value":1111},{"type":254,"tag":622,"props":68836,"children":68837},{"class":624,"line":705},[68838],{"type":254,"tag":622,"props":68839,"children":68840},{"emptyLinePlaceholder":1699},[68841],{"type":259,"value":1702},{"type":254,"tag":622,"props":68843,"children":68844},{"class":624,"line":732},[68845,68850],{"type":254,"tag":622,"props":68846,"children":68847},{"style":1735},[68848],{"type":259,"value":68849},"Import-Module",{"type":254,"tag":622,"props":68851,"children":68852},{"style":1202},[68853],{"type":259,"value":68854}," AzureAD\n",{"type":254,"tag":622,"props":68856,"children":68857},{"class":624,"line":759},[68858],{"type":254,"tag":622,"props":68859,"children":68860},{"emptyLinePlaceholder":1699},[68861],{"type":259,"value":1702},{"type":254,"tag":622,"props":68863,"children":68864},{"class":624,"line":798},[68865,68870,68874,68879,68883],{"type":254,"tag":622,"props":68866,"children":68867},{"style":1735},[68868],{"type":259,"value":68869},"Connect-AzureAD",{"type":254,"tag":622,"props":68871,"children":68872},{"style":629},[68873],{"type":259,"value":10733},{"type":254,"tag":622,"props":68875,"children":68876},{"style":1202},[68877],{"type":259,"value":68878},"TenantId ",{"type":254,"tag":622,"props":68880,"children":68881},{"style":629},[68882],{"type":259,"value":16922},{"type":254,"tag":622,"props":68884,"children":68885},{"style":1202},[68886],{"type":259,"value":18078},{"type":254,"tag":255,"props":68888,"children":68889},{},[68890],{"type":259,"value":68891},"This will prompt us to log in with our AD account. We will see later in the article how we can avoid that if we are using this script in an Azure Pipeline.",{"type":254,"tag":455,"props":68893,"children":68895},{"id":68894},"retrieving-the-application-registration",[68896],{"type":259,"value":68897},"Retrieving the application registration",{"type":254,"tag":255,"props":68899,"children":68900},{},[68901],{"type":259,"value":68902},"I already created my application registration in AD with Pulumi so I just have to retrieve it before configuring it.",{"type":254,"tag":612,"props":68904,"children":68906},{"className":10656,"code":68905,"language":10658,"meta":205,"style":205},"$app = Get-AzureADMSApplication -ObjectId $applicationObjectId\n",[68907],{"type":254,"tag":389,"props":68908,"children":68909},{"__ignoreMap":205},[68910],{"type":254,"tag":622,"props":68911,"children":68912},{"class":624,"line":625},[68913,68917,68922,68926,68931,68935,68940,68944],{"type":254,"tag":622,"props":68914,"children":68915},{"style":629},[68916],{"type":259,"value":16922},{"type":254,"tag":622,"props":68918,"children":68919},{"style":1202},[68920],{"type":259,"value":68921},"app ",{"type":254,"tag":622,"props":68923,"children":68924},{"style":629},[68925],{"type":259,"value":1654},{"type":254,"tag":622,"props":68927,"children":68928},{"style":1735},[68929],{"type":259,"value":68930}," Get-AzureADMSApplication",{"type":254,"tag":622,"props":68932,"children":68933},{"style":629},[68934],{"type":259,"value":10733},{"type":254,"tag":622,"props":68936,"children":68937},{"style":1202},[68938],{"type":259,"value":68939},"ObjectId ",{"type":254,"tag":622,"props":68941,"children":68942},{"style":629},[68943],{"type":259,"value":16922},{"type":254,"tag":622,"props":68945,"children":68946},{"style":1202},[68947],{"type":259,"value":68948},"applicationObjectId\n",{"type":254,"tag":255,"props":68950,"children":68951},{},[68952,68954,68960],{"type":259,"value":68953},"If you don't have an existing application registration you can create one with the ",{"type":254,"tag":389,"props":68955,"children":68957},{"className":68956},[],[68958],{"type":259,"value":68959},"New-AzureADMSApplication",{"type":259,"value":32093},{"type":254,"tag":427,"props":68962,"children":68964},{"icon":68963},"i-fluent-emoji-flat-gem-stone",[68965],{"type":254,"tag":255,"props":68966,"children":68967},{},[68968,68970,68976,68977,68983,68985,68990],{"type":259,"value":68969},"You may note that there are similar commands ",{"type":254,"tag":389,"props":68971,"children":68973},{"className":68972},[],[68974],{"type":259,"value":68975},"Get-AzureADApplication",{"type":259,"value":10347},{"type":254,"tag":389,"props":68978,"children":68980},{"className":68979},[],[68981],{"type":259,"value":68982},"New-AzureADApplication",{"type":259,"value":68984}," that exist. Both commands work fine but commands with ",{"type":254,"tag":19837,"props":68986,"children":68987},{},[68988],{"type":259,"value":68989},"MS",{"type":259,"value":68991}," in their name internally use Microsoft Graph which seems to be the modern way to interact with Azure AD.",{"type":254,"tag":455,"props":68993,"children":68995},{"id":68994},"creating-the-service-principal",[68996],{"type":259,"value":68997},"Creating the service principal",{"type":254,"tag":255,"props":68999,"children":69000},{},[69001,69003,69010],{"type":259,"value":69002},"When you register an application in Azure Portal it creates an Application object and a Service Principal in your tenant. But if you create the Application outside the Azure Portal (Azure CLI, PowerShell, Pulumi, ...), you will have to create the Service Principal as well. Just as a reminder the ",{"type":254,"tag":262,"props":69004,"children":69007},{"href":69005,"rel":69006},"https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals#application-and-service-principal-relationship",[266],[69008],{"type":259,"value":69009},"application object should be considered as the global representation of your application for use across all tenants, and the service principal as the local representation for use in a specific tenant",{"type":259,"value":594},{"type":254,"tag":612,"props":69012,"children":69014},{"className":10656,"code":69013,"language":10658,"meta":205,"style":205},"New-AzureADServicePrincipal -AppId $app.AppId -Tags {WindowsAzureActiveDirectoryIntegratedApp}\n",[69015],{"type":254,"tag":389,"props":69016,"children":69017},{"__ignoreMap":205},[69018],{"type":254,"tag":622,"props":69019,"children":69020},{"class":624,"line":625},[69021,69026,69030,69035,69039,69044,69048,69053,69057,69062],{"type":254,"tag":622,"props":69022,"children":69023},{"style":1735},[69024],{"type":259,"value":69025},"New-AzureADServicePrincipal",{"type":254,"tag":622,"props":69027,"children":69028},{"style":629},[69029],{"type":259,"value":10733},{"type":254,"tag":622,"props":69031,"children":69032},{"style":1202},[69033],{"type":259,"value":69034},"AppId ",{"type":254,"tag":622,"props":69036,"children":69037},{"style":629},[69038],{"type":259,"value":16922},{"type":254,"tag":622,"props":69040,"children":69041},{"style":1202},[69042],{"type":259,"value":69043},"app.AppId ",{"type":254,"tag":622,"props":69045,"children":69046},{"style":629},[69047],{"type":259,"value":17065},{"type":254,"tag":622,"props":69049,"children":69050},{"style":1202},[69051],{"type":259,"value":69052},"Tags ",{"type":254,"tag":622,"props":69054,"children":69055},{"style":629},[69056],{"type":259,"value":3234},{"type":254,"tag":622,"props":69058,"children":69059},{"style":1202},[69060],{"type":259,"value":69061},"WindowsAzureActiveDirectoryIntegratedApp",{"type":254,"tag":622,"props":69063,"children":69064},{"style":629},[69065],{"type":259,"value":1111},{"type":254,"tag":455,"props":69067,"children":69069},{"id":69068},"exposing-an-application-as-an-api",[69070],{"type":259,"value":69071},"Exposing an application as an API",{"type":254,"tag":255,"props":69073,"children":69074},{},[69075,69077,69083],{"type":259,"value":69076},"To expose an application as an API, it is necessary to set the identifier URI of the application. We will use a variable ",{"type":254,"tag":389,"props":69078,"children":69080},{"className":69079},[],[69081],{"type":259,"value":69082},"$customDomainName",{"type":259,"value":69084}," to specify the custom domain of the application. Indeed as stated by the documentation, for the moment Teams Tab SSO does not support applications that use the azurewebsites.net domain.",{"type":254,"tag":612,"props":69086,"children":69088},{"className":10656,"code":69087,"language":10658,"meta":205,"style":205},"$appId = $app.AppId\nSet-AzureADMSApplication -ObjectId $app.Id -IdentifierUris \"api://$customDomainName/$appId\"\n",[69089],{"type":254,"tag":389,"props":69090,"children":69091},{"__ignoreMap":205},[69092,69116],{"type":254,"tag":622,"props":69093,"children":69094},{"class":624,"line":625},[69095,69099,69103,69107,69111],{"type":254,"tag":622,"props":69096,"children":69097},{"style":629},[69098],{"type":259,"value":16922},{"type":254,"tag":622,"props":69100,"children":69101},{"style":1202},[69102],{"type":259,"value":17419},{"type":254,"tag":622,"props":69104,"children":69105},{"style":629},[69106],{"type":259,"value":1654},{"type":254,"tag":622,"props":69108,"children":69109},{"style":629},[69110],{"type":259,"value":39174},{"type":254,"tag":622,"props":69112,"children":69113},{"style":1202},[69114],{"type":259,"value":69115},"app.AppId\n",{"type":254,"tag":622,"props":69117,"children":69118},{"class":624,"line":635},[69119,69124,69128,69132,69136,69141,69145,69150,69154,69159,69163,69168,69172,69176,69180],{"type":254,"tag":622,"props":69120,"children":69121},{"style":1735},[69122],{"type":259,"value":69123},"Set-AzureADMSApplication",{"type":254,"tag":622,"props":69125,"children":69126},{"style":629},[69127],{"type":259,"value":10733},{"type":254,"tag":622,"props":69129,"children":69130},{"style":1202},[69131],{"type":259,"value":68939},{"type":254,"tag":622,"props":69133,"children":69134},{"style":629},[69135],{"type":259,"value":16922},{"type":254,"tag":622,"props":69137,"children":69138},{"style":1202},[69139],{"type":259,"value":69140},"app.Id ",{"type":254,"tag":622,"props":69142,"children":69143},{"style":629},[69144],{"type":259,"value":17065},{"type":254,"tag":622,"props":69146,"children":69147},{"style":1202},[69148],{"type":259,"value":69149},"IdentifierUris ",{"type":254,"tag":622,"props":69151,"children":69152},{"style":629},[69153],{"type":259,"value":652},{"type":254,"tag":622,"props":69155,"children":69156},{"style":664},[69157],{"type":259,"value":69158},"api://",{"type":254,"tag":622,"props":69160,"children":69161},{"style":629},[69162],{"type":259,"value":16922},{"type":254,"tag":622,"props":69164,"children":69165},{"style":1202},[69166],{"type":259,"value":69167},"customDomainName",{"type":254,"tag":622,"props":69169,"children":69170},{"style":664},[69171],{"type":259,"value":9768},{"type":254,"tag":622,"props":69173,"children":69174},{"style":629},[69175],{"type":259,"value":16922},{"type":254,"tag":622,"props":69177,"children":69178},{"style":1202},[69179],{"type":259,"value":17299},{"type":254,"tag":622,"props":69181,"children":69182},{"style":629},[69183],{"type":259,"value":9909},{"type":254,"tag":455,"props":69185,"children":69187},{"id":69186},"creating-the-access_as_user-scope",[69188],{"type":259,"value":69189},"Creating the access_as_user scope",{"type":254,"tag":255,"props":69191,"children":69192},{},[69193,69195,69201,69203,69208],{"type":259,"value":69194},"Teams Tab SSO works by making the Teams client (whether it be Teams mobile app, desktop app, or web app) ask for an Azure AD token with the scope ",{"type":254,"tag":389,"props":69196,"children":69198},{"className":69197},[],[69199],{"type":259,"value":69200},"access_as_user",{"type":259,"value":69202}," of the Tab application you developed. So we need to create a scope ",{"type":254,"tag":389,"props":69204,"children":69206},{"className":69205},[],[69207],{"type":259,"value":69200},{"type":259,"value":69209}," in the application.",{"type":254,"tag":612,"props":69211,"children":69213},{"className":10656,"code":69212,"language":10658,"meta":205,"style":205},"# Add all existing scopes first\n$scopes = New-Object System.Collections.Generic.List[Microsoft.Open.MsGraph.Model.PermissionScope]\n$app.Api.Oauth2PermissionScopes | foreach-object { $scopes.Add($_) }\n$scope = CreateScope -value \"access_as_user\"  `\n    -userConsentDisplayName \"Teams can access the user’s profile\"  `\n    -userConsentDescription \"Allows Teams to call the app’s web APIs as the current user.\"  `\n    -adminConsentDisplayName \"Teams can access your user profile and make requests on your behalf\"  `\n    -adminConsentDescription \"Enable Teams to call this app’s APIs with the same rights that you have\"\n$scopes.Add($scope)\n$app.Api.Oauth2PermissionScopes = $scopes\nSet-AzureADMSApplication -ObjectId $app.Id -Api $app.Api\n",[69214],{"type":254,"tag":389,"props":69215,"children":69216},{"__ignoreMap":205},[69217,69225,69264,69315,69361,69390,69419,69448,69473,69496,69520],{"type":254,"tag":622,"props":69218,"children":69219},{"class":624,"line":625},[69220],{"type":254,"tag":622,"props":69221,"children":69222},{"style":3195},[69223],{"type":259,"value":69224},"# Add all existing scopes first\n",{"type":254,"tag":622,"props":69226,"children":69227},{"class":624,"line":635},[69228,69232,69237,69241,69246,69251,69255,69260],{"type":254,"tag":622,"props":69229,"children":69230},{"style":629},[69231],{"type":259,"value":16922},{"type":254,"tag":622,"props":69233,"children":69234},{"style":1202},[69235],{"type":259,"value":69236},"scopes ",{"type":254,"tag":622,"props":69238,"children":69239},{"style":629},[69240],{"type":259,"value":1654},{"type":254,"tag":622,"props":69242,"children":69243},{"style":1735},[69244],{"type":259,"value":69245}," New-Object",{"type":254,"tag":622,"props":69247,"children":69248},{"style":1202},[69249],{"type":259,"value":69250}," System.Collections.Generic.List",{"type":254,"tag":622,"props":69252,"children":69253},{"style":629},[69254],{"type":259,"value":10993},{"type":254,"tag":622,"props":69256,"children":69257},{"style":644},[69258],{"type":259,"value":69259},"Microsoft.Open.MsGraph.Model.PermissionScope",{"type":254,"tag":622,"props":69261,"children":69262},{"style":629},[69263],{"type":259,"value":11003},{"type":254,"tag":622,"props":69265,"children":69266},{"class":624,"line":679},[69267,69271,69276,69280,69285,69289,69293,69298,69303,69307,69311],{"type":254,"tag":622,"props":69268,"children":69269},{"style":629},[69270],{"type":259,"value":16922},{"type":254,"tag":622,"props":69272,"children":69273},{"style":1202},[69274],{"type":259,"value":69275},"app.Api.Oauth2PermissionScopes ",{"type":254,"tag":622,"props":69277,"children":69278},{"style":629},[69279],{"type":259,"value":17875},{"type":254,"tag":622,"props":69281,"children":69282},{"style":1735},[69283],{"type":259,"value":69284}," foreach-object",{"type":254,"tag":622,"props":69286,"children":69287},{"style":629},[69288],{"type":259,"value":1990},{"type":254,"tag":622,"props":69290,"children":69291},{"style":629},[69292],{"type":259,"value":39174},{"type":254,"tag":622,"props":69294,"children":69295},{"style":1202},[69296],{"type":259,"value":69297},"scopes.Add",{"type":254,"tag":622,"props":69299,"children":69300},{"style":629},[69301],{"type":259,"value":69302},"($",{"type":254,"tag":622,"props":69304,"children":69305},{"style":1202},[69306],{"type":259,"value":8945},{"type":254,"tag":622,"props":69308,"children":69309},{"style":629},[69310],{"type":259,"value":2309},{"type":254,"tag":622,"props":69312,"children":69313},{"style":629},[69314],{"type":259,"value":2014},{"type":254,"tag":622,"props":69316,"children":69317},{"class":624,"line":705},[69318,69322,69326,69330,69335,69339,69344,69348,69352,69356],{"type":254,"tag":622,"props":69319,"children":69320},{"style":629},[69321],{"type":259,"value":16922},{"type":254,"tag":622,"props":69323,"children":69324},{"style":1202},[69325],{"type":259,"value":17569},{"type":254,"tag":622,"props":69327,"children":69328},{"style":629},[69329],{"type":259,"value":1654},{"type":254,"tag":622,"props":69331,"children":69332},{"style":1202},[69333],{"type":259,"value":69334}," CreateScope ",{"type":254,"tag":622,"props":69336,"children":69337},{"style":629},[69338],{"type":259,"value":17065},{"type":254,"tag":622,"props":69340,"children":69341},{"style":1202},[69342],{"type":259,"value":69343},"value ",{"type":254,"tag":622,"props":69345,"children":69346},{"style":629},[69347],{"type":259,"value":652},{"type":254,"tag":622,"props":69349,"children":69350},{"style":664},[69351],{"type":259,"value":69200},{"type":254,"tag":622,"props":69353,"children":69354},{"style":629},[69355],{"type":259,"value":652},{"type":254,"tag":622,"props":69357,"children":69358},{"style":629},[69359],{"type":259,"value":69360},"  `\n",{"type":254,"tag":622,"props":69362,"children":69363},{"class":624,"line":732},[69364,69368,69373,69377,69382,69386],{"type":254,"tag":622,"props":69365,"children":69366},{"style":629},[69367],{"type":259,"value":51754},{"type":254,"tag":622,"props":69369,"children":69370},{"style":1202},[69371],{"type":259,"value":69372},"userConsentDisplayName ",{"type":254,"tag":622,"props":69374,"children":69375},{"style":629},[69376],{"type":259,"value":652},{"type":254,"tag":622,"props":69378,"children":69379},{"style":664},[69380],{"type":259,"value":69381},"Teams can access the user’s profile",{"type":254,"tag":622,"props":69383,"children":69384},{"style":629},[69385],{"type":259,"value":652},{"type":254,"tag":622,"props":69387,"children":69388},{"style":629},[69389],{"type":259,"value":69360},{"type":254,"tag":622,"props":69391,"children":69392},{"class":624,"line":759},[69393,69397,69402,69406,69411,69415],{"type":254,"tag":622,"props":69394,"children":69395},{"style":629},[69396],{"type":259,"value":51754},{"type":254,"tag":622,"props":69398,"children":69399},{"style":1202},[69400],{"type":259,"value":69401},"userConsentDescription ",{"type":254,"tag":622,"props":69403,"children":69404},{"style":629},[69405],{"type":259,"value":652},{"type":254,"tag":622,"props":69407,"children":69408},{"style":664},[69409],{"type":259,"value":69410},"Allows Teams to call the app’s web APIs as the current user.",{"type":254,"tag":622,"props":69412,"children":69413},{"style":629},[69414],{"type":259,"value":652},{"type":254,"tag":622,"props":69416,"children":69417},{"style":629},[69418],{"type":259,"value":69360},{"type":254,"tag":622,"props":69420,"children":69421},{"class":624,"line":798},[69422,69426,69431,69435,69440,69444],{"type":254,"tag":622,"props":69423,"children":69424},{"style":629},[69425],{"type":259,"value":51754},{"type":254,"tag":622,"props":69427,"children":69428},{"style":1202},[69429],{"type":259,"value":69430},"adminConsentDisplayName ",{"type":254,"tag":622,"props":69432,"children":69433},{"style":629},[69434],{"type":259,"value":652},{"type":254,"tag":622,"props":69436,"children":69437},{"style":664},[69438],{"type":259,"value":69439},"Teams can access your user profile and make requests on your behalf",{"type":254,"tag":622,"props":69441,"children":69442},{"style":629},[69443],{"type":259,"value":652},{"type":254,"tag":622,"props":69445,"children":69446},{"style":629},[69447],{"type":259,"value":69360},{"type":254,"tag":622,"props":69449,"children":69450},{"class":624,"line":836},[69451,69455,69460,69464,69469],{"type":254,"tag":622,"props":69452,"children":69453},{"style":629},[69454],{"type":259,"value":51754},{"type":254,"tag":622,"props":69456,"children":69457},{"style":1202},[69458],{"type":259,"value":69459},"adminConsentDescription ",{"type":254,"tag":622,"props":69461,"children":69462},{"style":629},[69463],{"type":259,"value":652},{"type":254,"tag":622,"props":69465,"children":69466},{"style":664},[69467],{"type":259,"value":69468},"Enable Teams to call this app’s APIs with the same rights that you have",{"type":254,"tag":622,"props":69470,"children":69471},{"style":629},[69472],{"type":259,"value":9909},{"type":254,"tag":622,"props":69474,"children":69475},{"class":624,"line":862},[69476,69480,69484,69488,69492],{"type":254,"tag":622,"props":69477,"children":69478},{"style":629},[69479],{"type":259,"value":16922},{"type":254,"tag":622,"props":69481,"children":69482},{"style":1202},[69483],{"type":259,"value":69297},{"type":254,"tag":622,"props":69485,"children":69486},{"style":629},[69487],{"type":259,"value":69302},{"type":254,"tag":622,"props":69489,"children":69490},{"style":1202},[69491],{"type":259,"value":62192},{"type":254,"tag":622,"props":69493,"children":69494},{"style":629},[69495],{"type":259,"value":1685},{"type":254,"tag":622,"props":69497,"children":69498},{"class":624,"line":900},[69499,69503,69507,69511,69515],{"type":254,"tag":622,"props":69500,"children":69501},{"style":629},[69502],{"type":259,"value":16922},{"type":254,"tag":622,"props":69504,"children":69505},{"style":1202},[69506],{"type":259,"value":69275},{"type":254,"tag":622,"props":69508,"children":69509},{"style":629},[69510],{"type":259,"value":1654},{"type":254,"tag":622,"props":69512,"children":69513},{"style":629},[69514],{"type":259,"value":39174},{"type":254,"tag":622,"props":69516,"children":69517},{"style":1202},[69518],{"type":259,"value":69519},"scopes\n",{"type":254,"tag":622,"props":69521,"children":69522},{"class":624,"line":938},[69523,69527,69531,69535,69539,69543,69547,69552,69556],{"type":254,"tag":622,"props":69524,"children":69525},{"style":1735},[69526],{"type":259,"value":69123},{"type":254,"tag":622,"props":69528,"children":69529},{"style":629},[69530],{"type":259,"value":10733},{"type":254,"tag":622,"props":69532,"children":69533},{"style":1202},[69534],{"type":259,"value":68939},{"type":254,"tag":622,"props":69536,"children":69537},{"style":629},[69538],{"type":259,"value":16922},{"type":254,"tag":622,"props":69540,"children":69541},{"style":1202},[69542],{"type":259,"value":69140},{"type":254,"tag":622,"props":69544,"children":69545},{"style":629},[69546],{"type":259,"value":17065},{"type":254,"tag":622,"props":69548,"children":69549},{"style":1202},[69550],{"type":259,"value":69551},"Api ",{"type":254,"tag":622,"props":69553,"children":69554},{"style":629},[69555],{"type":259,"value":16922},{"type":254,"tag":622,"props":69557,"children":69558},{"style":1202},[69559],{"type":259,"value":69560},"app.Api\n",{"type":254,"tag":255,"props":69562,"children":69563},{},[69564,69566,69571],{"type":259,"value":69565},"This piece of PowerShell just ensures existing scopes won't be deleted when adding the scope ",{"type":254,"tag":389,"props":69567,"children":69569},{"className":69568},[],[69570],{"type":259,"value":69200},{"type":259,"value":69572},". Display names and descriptions of the new scope are the ones recommended in the documentation. This code calls a PowerShell function that simply creates the scope object.",{"type":254,"tag":612,"props":69574,"children":69576},{"className":10656,"code":69575,"language":10658,"meta":205,"style":205},"\u003C#.Description\n   This function creates a new Azure AD scope (OAuth2Permission) with default and provided values\n#>  \nfunction CreateScope(\n    [string] $value,\n    [string] $userConsentDisplayName,\n    [string] $userConsentDescription,\n    [string] $adminConsentDisplayName,\n    [string] $adminConsentDescription)\n{\n    $scope = New-Object Microsoft.Open.MsGraph.Model.PermissionScope\n    $scope.Id = New-Guid\n    $scope.Value = $value\n    $scope.UserConsentDisplayName = $userConsentDisplayName\n    $scope.UserConsentDescription = $userConsentDescription\n    $scope.AdminConsentDisplayName = $adminConsentDisplayName\n    $scope.AdminConsentDescription = $adminConsentDescription\n    $scope.IsEnabled = $true\n    $scope.Type = \"User\"\n    return $scope\n}\n",[69577],{"type":254,"tag":389,"props":69578,"children":69579},{"__ignoreMap":205},[69580,69597,69605,69617,69633,69660,69688,69716,69744,69772,69779,69804,69825,69850,69875,69900,69925,69950,69971,69999,70015],{"type":254,"tag":622,"props":69581,"children":69582},{"class":624,"line":625},[69583,69588,69592],{"type":254,"tag":622,"props":69584,"children":69585},{"style":3195},[69586],{"type":259,"value":69587},"\u003C#",{"type":254,"tag":622,"props":69589,"children":69590},{"style":3195},[69591],{"type":259,"value":594},{"type":254,"tag":622,"props":69593,"children":69594},{"style":1196},[69595],{"type":259,"value":69596},"Description\n",{"type":254,"tag":622,"props":69598,"children":69599},{"class":624,"line":635},[69600],{"type":254,"tag":622,"props":69601,"children":69602},{"style":3195},[69603],{"type":259,"value":69604},"   This function creates a new Azure AD scope (OAuth2Permission) with default and provided values\n",{"type":254,"tag":622,"props":69606,"children":69607},{"class":624,"line":679},[69608,69613],{"type":254,"tag":622,"props":69609,"children":69610},{"style":3195},[69611],{"type":259,"value":69612},"#>",{"type":254,"tag":622,"props":69614,"children":69615},{"style":1202},[69616],{"type":259,"value":33411},{"type":254,"tag":622,"props":69618,"children":69619},{"class":624,"line":705},[69620,69624,69629],{"type":254,"tag":622,"props":69621,"children":69622},{"style":644},[69623],{"type":259,"value":21389},{"type":254,"tag":622,"props":69625,"children":69626},{"style":1735},[69627],{"type":259,"value":69628}," CreateScope",{"type":254,"tag":622,"props":69630,"children":69631},{"style":629},[69632],{"type":259,"value":1452},{"type":254,"tag":622,"props":69634,"children":69635},{"class":624,"line":732},[69636,69640,69644,69648,69652,69656],{"type":254,"tag":622,"props":69637,"children":69638},{"style":629},[69639],{"type":259,"value":9494},{"type":254,"tag":622,"props":69641,"children":69642},{"style":644},[69643],{"type":259,"value":2851},{"type":254,"tag":622,"props":69645,"children":69646},{"style":629},[69647],{"type":259,"value":20251},{"type":254,"tag":622,"props":69649,"children":69650},{"style":629},[69651],{"type":259,"value":39174},{"type":254,"tag":622,"props":69653,"children":69654},{"style":1202},[69655],{"type":259,"value":40770},{"type":254,"tag":622,"props":69657,"children":69658},{"style":629},[69659],{"type":259,"value":676},{"type":254,"tag":622,"props":69661,"children":69662},{"class":624,"line":759},[69663,69667,69671,69675,69679,69684],{"type":254,"tag":622,"props":69664,"children":69665},{"style":629},[69666],{"type":259,"value":9494},{"type":254,"tag":622,"props":69668,"children":69669},{"style":644},[69670],{"type":259,"value":2851},{"type":254,"tag":622,"props":69672,"children":69673},{"style":629},[69674],{"type":259,"value":20251},{"type":254,"tag":622,"props":69676,"children":69677},{"style":629},[69678],{"type":259,"value":39174},{"type":254,"tag":622,"props":69680,"children":69681},{"style":1202},[69682],{"type":259,"value":69683},"userConsentDisplayName",{"type":254,"tag":622,"props":69685,"children":69686},{"style":629},[69687],{"type":259,"value":676},{"type":254,"tag":622,"props":69689,"children":69690},{"class":624,"line":798},[69691,69695,69699,69703,69707,69712],{"type":254,"tag":622,"props":69692,"children":69693},{"style":629},[69694],{"type":259,"value":9494},{"type":254,"tag":622,"props":69696,"children":69697},{"style":644},[69698],{"type":259,"value":2851},{"type":254,"tag":622,"props":69700,"children":69701},{"style":629},[69702],{"type":259,"value":20251},{"type":254,"tag":622,"props":69704,"children":69705},{"style":629},[69706],{"type":259,"value":39174},{"type":254,"tag":622,"props":69708,"children":69709},{"style":1202},[69710],{"type":259,"value":69711},"userConsentDescription",{"type":254,"tag":622,"props":69713,"children":69714},{"style":629},[69715],{"type":259,"value":676},{"type":254,"tag":622,"props":69717,"children":69718},{"class":624,"line":836},[69719,69723,69727,69731,69735,69740],{"type":254,"tag":622,"props":69720,"children":69721},{"style":629},[69722],{"type":259,"value":9494},{"type":254,"tag":622,"props":69724,"children":69725},{"style":644},[69726],{"type":259,"value":2851},{"type":254,"tag":622,"props":69728,"children":69729},{"style":629},[69730],{"type":259,"value":20251},{"type":254,"tag":622,"props":69732,"children":69733},{"style":629},[69734],{"type":259,"value":39174},{"type":254,"tag":622,"props":69736,"children":69737},{"style":1202},[69738],{"type":259,"value":69739},"adminConsentDisplayName",{"type":254,"tag":622,"props":69741,"children":69742},{"style":629},[69743],{"type":259,"value":676},{"type":254,"tag":622,"props":69745,"children":69746},{"class":624,"line":862},[69747,69751,69755,69759,69763,69768],{"type":254,"tag":622,"props":69748,"children":69749},{"style":629},[69750],{"type":259,"value":9494},{"type":254,"tag":622,"props":69752,"children":69753},{"style":644},[69754],{"type":259,"value":2851},{"type":254,"tag":622,"props":69756,"children":69757},{"style":629},[69758],{"type":259,"value":20251},{"type":254,"tag":622,"props":69760,"children":69761},{"style":629},[69762],{"type":259,"value":39174},{"type":254,"tag":622,"props":69764,"children":69765},{"style":1202},[69766],{"type":259,"value":69767},"adminConsentDescription",{"type":254,"tag":622,"props":69769,"children":69770},{"style":629},[69771],{"type":259,"value":1685},{"type":254,"tag":622,"props":69773,"children":69774},{"class":624,"line":900},[69775],{"type":254,"tag":622,"props":69776,"children":69777},{"style":629},[69778],{"type":259,"value":632},{"type":254,"tag":622,"props":69780,"children":69781},{"class":624,"line":938},[69782,69787,69791,69795,69799],{"type":254,"tag":622,"props":69783,"children":69784},{"style":629},[69785],{"type":259,"value":69786},"    $",{"type":254,"tag":622,"props":69788,"children":69789},{"style":1202},[69790],{"type":259,"value":17569},{"type":254,"tag":622,"props":69792,"children":69793},{"style":629},[69794],{"type":259,"value":1654},{"type":254,"tag":622,"props":69796,"children":69797},{"style":1735},[69798],{"type":259,"value":69245},{"type":254,"tag":622,"props":69800,"children":69801},{"style":1202},[69802],{"type":259,"value":69803}," Microsoft.Open.MsGraph.Model.PermissionScope\n",{"type":254,"tag":622,"props":69805,"children":69806},{"class":624,"line":976},[69807,69811,69816,69820],{"type":254,"tag":622,"props":69808,"children":69809},{"style":629},[69810],{"type":259,"value":69786},{"type":254,"tag":622,"props":69812,"children":69813},{"style":1202},[69814],{"type":259,"value":69815},"scope.Id ",{"type":254,"tag":622,"props":69817,"children":69818},{"style":629},[69819],{"type":259,"value":1654},{"type":254,"tag":622,"props":69821,"children":69822},{"style":1735},[69823],{"type":259,"value":69824}," New-Guid\n",{"type":254,"tag":622,"props":69826,"children":69827},{"class":624,"line":1014},[69828,69832,69837,69841,69845],{"type":254,"tag":622,"props":69829,"children":69830},{"style":629},[69831],{"type":259,"value":69786},{"type":254,"tag":622,"props":69833,"children":69834},{"style":1202},[69835],{"type":259,"value":69836},"scope.Value ",{"type":254,"tag":622,"props":69838,"children":69839},{"style":629},[69840],{"type":259,"value":1654},{"type":254,"tag":622,"props":69842,"children":69843},{"style":629},[69844],{"type":259,"value":39174},{"type":254,"tag":622,"props":69846,"children":69847},{"style":1202},[69848],{"type":259,"value":69849},"value\n",{"type":254,"tag":622,"props":69851,"children":69852},{"class":624,"line":1052},[69853,69857,69862,69866,69870],{"type":254,"tag":622,"props":69854,"children":69855},{"style":629},[69856],{"type":259,"value":69786},{"type":254,"tag":622,"props":69858,"children":69859},{"style":1202},[69860],{"type":259,"value":69861},"scope.UserConsentDisplayName ",{"type":254,"tag":622,"props":69863,"children":69864},{"style":629},[69865],{"type":259,"value":1654},{"type":254,"tag":622,"props":69867,"children":69868},{"style":629},[69869],{"type":259,"value":39174},{"type":254,"tag":622,"props":69871,"children":69872},{"style":1202},[69873],{"type":259,"value":69874},"userConsentDisplayName\n",{"type":254,"tag":622,"props":69876,"children":69877},{"class":624,"line":1078},[69878,69882,69887,69891,69895],{"type":254,"tag":622,"props":69879,"children":69880},{"style":629},[69881],{"type":259,"value":69786},{"type":254,"tag":622,"props":69883,"children":69884},{"style":1202},[69885],{"type":259,"value":69886},"scope.UserConsentDescription ",{"type":254,"tag":622,"props":69888,"children":69889},{"style":629},[69890],{"type":259,"value":1654},{"type":254,"tag":622,"props":69892,"children":69893},{"style":629},[69894],{"type":259,"value":39174},{"type":254,"tag":622,"props":69896,"children":69897},{"style":1202},[69898],{"type":259,"value":69899},"userConsentDescription\n",{"type":254,"tag":622,"props":69901,"children":69902},{"class":624,"line":1087},[69903,69907,69912,69916,69920],{"type":254,"tag":622,"props":69904,"children":69905},{"style":629},[69906],{"type":259,"value":69786},{"type":254,"tag":622,"props":69908,"children":69909},{"style":1202},[69910],{"type":259,"value":69911},"scope.AdminConsentDisplayName ",{"type":254,"tag":622,"props":69913,"children":69914},{"style":629},[69915],{"type":259,"value":1654},{"type":254,"tag":622,"props":69917,"children":69918},{"style":629},[69919],{"type":259,"value":39174},{"type":254,"tag":622,"props":69921,"children":69922},{"style":1202},[69923],{"type":259,"value":69924},"adminConsentDisplayName\n",{"type":254,"tag":622,"props":69926,"children":69927},{"class":624,"line":1096},[69928,69932,69937,69941,69945],{"type":254,"tag":622,"props":69929,"children":69930},{"style":629},[69931],{"type":259,"value":69786},{"type":254,"tag":622,"props":69933,"children":69934},{"style":1202},[69935],{"type":259,"value":69936},"scope.AdminConsentDescription ",{"type":254,"tag":622,"props":69938,"children":69939},{"style":629},[69940],{"type":259,"value":1654},{"type":254,"tag":622,"props":69942,"children":69943},{"style":629},[69944],{"type":259,"value":39174},{"type":254,"tag":622,"props":69946,"children":69947},{"style":1202},[69948],{"type":259,"value":69949},"adminConsentDescription\n",{"type":254,"tag":622,"props":69951,"children":69952},{"class":624,"line":1105},[69953,69957,69962,69966],{"type":254,"tag":622,"props":69954,"children":69955},{"style":629},[69956],{"type":259,"value":69786},{"type":254,"tag":622,"props":69958,"children":69959},{"style":1202},[69960],{"type":259,"value":69961},"scope.IsEnabled ",{"type":254,"tag":622,"props":69963,"children":69964},{"style":629},[69965],{"type":259,"value":1654},{"type":254,"tag":622,"props":69967,"children":69968},{"style":629},[69969],{"type":259,"value":69970}," $true\n",{"type":254,"tag":622,"props":69972,"children":69973},{"class":624,"line":4131},[69974,69978,69983,69987,69991,69995],{"type":254,"tag":622,"props":69975,"children":69976},{"style":629},[69977],{"type":259,"value":69786},{"type":254,"tag":622,"props":69979,"children":69980},{"style":1202},[69981],{"type":259,"value":69982},"scope.Type ",{"type":254,"tag":622,"props":69984,"children":69985},{"style":629},[69986],{"type":259,"value":1654},{"type":254,"tag":622,"props":69988,"children":69989},{"style":629},[69990],{"type":259,"value":661},{"type":254,"tag":622,"props":69992,"children":69993},{"style":664},[69994],{"type":259,"value":44521},{"type":254,"tag":622,"props":69996,"children":69997},{"style":629},[69998],{"type":259,"value":9909},{"type":254,"tag":622,"props":70000,"children":70001},{"class":624,"line":4183},[70002,70006,70010],{"type":254,"tag":622,"props":70003,"children":70004},{"style":1196},[70005],{"type":259,"value":2358},{"type":254,"tag":622,"props":70007,"children":70008},{"style":629},[70009],{"type":259,"value":39174},{"type":254,"tag":622,"props":70011,"children":70012},{"style":1202},[70013],{"type":259,"value":70014},"scope\n",{"type":254,"tag":622,"props":70016,"children":70017},{"class":624,"line":4208},[70018],{"type":254,"tag":622,"props":70019,"children":70020},{"style":629},[70021],{"type":259,"value":1111},{"type":254,"tag":455,"props":70023,"children":70025},{"id":70024},"preauthorize-teams-clients",[70026],{"type":259,"value":70027},"Preauthorize Teams clients.",{"type":254,"tag":255,"props":70029,"children":70030},{},[70031],{"type":259,"value":70032},"As the Teams clients will ask for a token with the previously created scope, they must be authorized to have access to this permission. That is what does the following script:",{"type":254,"tag":612,"props":70034,"children":70036},{"className":10656,"code":70035,"language":10658,"meta":205,"style":205},"# Authorize Teams mobile/desktop client and Teams web client to access API\n$preAuthorizedApplications = New-Object 'System.Collections.Generic.List[Microsoft.Open.MSGraph.ModePreAuthorizedApplication]'\n$teamsRichClienPreauthorization = CreatePreAuthorizedApplication `\n    -applicationIdToPreAuthorize '1fec8e78-bce4-4aaf-ab1b-5451cc387264' `\n    -scopeId $scope.Id\n$teamsWebClienPreauthorization = CreatePreAuthorizedApplication `\n    -applicationIdToPreAuthorize '5e3ce6c0-2b1f-4285-8d4b-75ee78787346' `\n    -scopeId $scope.Id\n$preAuthorizedApplications.Add($teamsRichClienPreauthorization)\n$preAuthorizedApplications.Add($teamsWebClienPreauthorization)   \n$app = Get-AzureADMSApplication -ObjectId $applicationObjectId\n$app.Api.PreAuthorizedApplications = $preAuthorizedApplications\nSet-AzureADMSApplication -ObjectId $app.Id -Api $app.Api\n",[70037],{"type":254,"tag":389,"props":70038,"children":70039},{"__ignoreMap":205},[70040,70048,70081,70107,70137,70158,70182,70210,70229,70254,70282,70317,70342],{"type":254,"tag":622,"props":70041,"children":70042},{"class":624,"line":625},[70043],{"type":254,"tag":622,"props":70044,"children":70045},{"style":3195},[70046],{"type":259,"value":70047},"# Authorize Teams mobile/desktop client and Teams web client to access API\n",{"type":254,"tag":622,"props":70049,"children":70050},{"class":624,"line":635},[70051,70055,70060,70064,70068,70072,70077],{"type":254,"tag":622,"props":70052,"children":70053},{"style":629},[70054],{"type":259,"value":16922},{"type":254,"tag":622,"props":70056,"children":70057},{"style":1202},[70058],{"type":259,"value":70059},"preAuthorizedApplications ",{"type":254,"tag":622,"props":70061,"children":70062},{"style":629},[70063],{"type":259,"value":1654},{"type":254,"tag":622,"props":70065,"children":70066},{"style":1735},[70067],{"type":259,"value":69245},{"type":254,"tag":622,"props":70069,"children":70070},{"style":629},[70071],{"type":259,"value":16556},{"type":254,"tag":622,"props":70073,"children":70074},{"style":664},[70075],{"type":259,"value":70076},"System.Collections.Generic.List[Microsoft.Open.MSGraph.ModePreAuthorizedApplication]",{"type":254,"tag":622,"props":70078,"children":70079},{"style":629},[70080],{"type":259,"value":16566},{"type":254,"tag":622,"props":70082,"children":70083},{"class":624,"line":679},[70084,70088,70093,70097,70102],{"type":254,"tag":622,"props":70085,"children":70086},{"style":629},[70087],{"type":259,"value":16922},{"type":254,"tag":622,"props":70089,"children":70090},{"style":1202},[70091],{"type":259,"value":70092},"teamsRichClienPreauthorization ",{"type":254,"tag":622,"props":70094,"children":70095},{"style":629},[70096],{"type":259,"value":1654},{"type":254,"tag":622,"props":70098,"children":70099},{"style":1202},[70100],{"type":259,"value":70101}," CreatePreAuthorizedApplication ",{"type":254,"tag":622,"props":70103,"children":70104},{"style":629},[70105],{"type":259,"value":70106},"`\n",{"type":254,"tag":622,"props":70108,"children":70109},{"class":624,"line":705},[70110,70114,70119,70123,70128,70132],{"type":254,"tag":622,"props":70111,"children":70112},{"style":629},[70113],{"type":259,"value":51754},{"type":254,"tag":622,"props":70115,"children":70116},{"style":1202},[70117],{"type":259,"value":70118},"applicationIdToPreAuthorize ",{"type":254,"tag":622,"props":70120,"children":70121},{"style":629},[70122],{"type":259,"value":46169},{"type":254,"tag":622,"props":70124,"children":70125},{"style":664},[70126],{"type":259,"value":70127},"1fec8e78-bce4-4aaf-ab1b-5451cc387264",{"type":254,"tag":622,"props":70129,"children":70130},{"style":629},[70131],{"type":259,"value":46169},{"type":254,"tag":622,"props":70133,"children":70134},{"style":629},[70135],{"type":259,"value":70136}," `\n",{"type":254,"tag":622,"props":70138,"children":70139},{"class":624,"line":732},[70140,70144,70149,70153],{"type":254,"tag":622,"props":70141,"children":70142},{"style":629},[70143],{"type":259,"value":51754},{"type":254,"tag":622,"props":70145,"children":70146},{"style":1202},[70147],{"type":259,"value":70148},"scopeId ",{"type":254,"tag":622,"props":70150,"children":70151},{"style":629},[70152],{"type":259,"value":16922},{"type":254,"tag":622,"props":70154,"children":70155},{"style":1202},[70156],{"type":259,"value":70157},"scope.Id\n",{"type":254,"tag":622,"props":70159,"children":70160},{"class":624,"line":759},[70161,70165,70170,70174,70178],{"type":254,"tag":622,"props":70162,"children":70163},{"style":629},[70164],{"type":259,"value":16922},{"type":254,"tag":622,"props":70166,"children":70167},{"style":1202},[70168],{"type":259,"value":70169},"teamsWebClienPreauthorization ",{"type":254,"tag":622,"props":70171,"children":70172},{"style":629},[70173],{"type":259,"value":1654},{"type":254,"tag":622,"props":70175,"children":70176},{"style":1202},[70177],{"type":259,"value":70101},{"type":254,"tag":622,"props":70179,"children":70180},{"style":629},[70181],{"type":259,"value":70106},{"type":254,"tag":622,"props":70183,"children":70184},{"class":624,"line":798},[70185,70189,70193,70197,70202,70206],{"type":254,"tag":622,"props":70186,"children":70187},{"style":629},[70188],{"type":259,"value":51754},{"type":254,"tag":622,"props":70190,"children":70191},{"style":1202},[70192],{"type":259,"value":70118},{"type":254,"tag":622,"props":70194,"children":70195},{"style":629},[70196],{"type":259,"value":46169},{"type":254,"tag":622,"props":70198,"children":70199},{"style":664},[70200],{"type":259,"value":70201},"5e3ce6c0-2b1f-4285-8d4b-75ee78787346",{"type":254,"tag":622,"props":70203,"children":70204},{"style":629},[70205],{"type":259,"value":46169},{"type":254,"tag":622,"props":70207,"children":70208},{"style":629},[70209],{"type":259,"value":70136},{"type":254,"tag":622,"props":70211,"children":70212},{"class":624,"line":836},[70213,70217,70221,70225],{"type":254,"tag":622,"props":70214,"children":70215},{"style":629},[70216],{"type":259,"value":51754},{"type":254,"tag":622,"props":70218,"children":70219},{"style":1202},[70220],{"type":259,"value":70148},{"type":254,"tag":622,"props":70222,"children":70223},{"style":629},[70224],{"type":259,"value":16922},{"type":254,"tag":622,"props":70226,"children":70227},{"style":1202},[70228],{"type":259,"value":70157},{"type":254,"tag":622,"props":70230,"children":70231},{"class":624,"line":862},[70232,70236,70241,70245,70250],{"type":254,"tag":622,"props":70233,"children":70234},{"style":629},[70235],{"type":259,"value":16922},{"type":254,"tag":622,"props":70237,"children":70238},{"style":1202},[70239],{"type":259,"value":70240},"preAuthorizedApplications.Add",{"type":254,"tag":622,"props":70242,"children":70243},{"style":629},[70244],{"type":259,"value":69302},{"type":254,"tag":622,"props":70246,"children":70247},{"style":1202},[70248],{"type":259,"value":70249},"teamsRichClienPreauthorization",{"type":254,"tag":622,"props":70251,"children":70252},{"style":629},[70253],{"type":259,"value":1685},{"type":254,"tag":622,"props":70255,"children":70256},{"class":624,"line":900},[70257,70261,70265,70269,70274,70278],{"type":254,"tag":622,"props":70258,"children":70259},{"style":629},[70260],{"type":259,"value":16922},{"type":254,"tag":622,"props":70262,"children":70263},{"style":1202},[70264],{"type":259,"value":70240},{"type":254,"tag":622,"props":70266,"children":70267},{"style":629},[70268],{"type":259,"value":69302},{"type":254,"tag":622,"props":70270,"children":70271},{"style":1202},[70272],{"type":259,"value":70273},"teamsWebClienPreauthorization",{"type":254,"tag":622,"props":70275,"children":70276},{"style":629},[70277],{"type":259,"value":2309},{"type":254,"tag":622,"props":70279,"children":70280},{"style":1202},[70281],{"type":259,"value":35234},{"type":254,"tag":622,"props":70283,"children":70284},{"class":624,"line":938},[70285,70289,70293,70297,70301,70305,70309,70313],{"type":254,"tag":622,"props":70286,"children":70287},{"style":629},[70288],{"type":259,"value":16922},{"type":254,"tag":622,"props":70290,"children":70291},{"style":1202},[70292],{"type":259,"value":68921},{"type":254,"tag":622,"props":70294,"children":70295},{"style":629},[70296],{"type":259,"value":1654},{"type":254,"tag":622,"props":70298,"children":70299},{"style":1735},[70300],{"type":259,"value":68930},{"type":254,"tag":622,"props":70302,"children":70303},{"style":629},[70304],{"type":259,"value":10733},{"type":254,"tag":622,"props":70306,"children":70307},{"style":1202},[70308],{"type":259,"value":68939},{"type":254,"tag":622,"props":70310,"children":70311},{"style":629},[70312],{"type":259,"value":16922},{"type":254,"tag":622,"props":70314,"children":70315},{"style":1202},[70316],{"type":259,"value":68948},{"type":254,"tag":622,"props":70318,"children":70319},{"class":624,"line":976},[70320,70324,70329,70333,70337],{"type":254,"tag":622,"props":70321,"children":70322},{"style":629},[70323],{"type":259,"value":16922},{"type":254,"tag":622,"props":70325,"children":70326},{"style":1202},[70327],{"type":259,"value":70328},"app.Api.PreAuthorizedApplications ",{"type":254,"tag":622,"props":70330,"children":70331},{"style":629},[70332],{"type":259,"value":1654},{"type":254,"tag":622,"props":70334,"children":70335},{"style":629},[70336],{"type":259,"value":39174},{"type":254,"tag":622,"props":70338,"children":70339},{"style":1202},[70340],{"type":259,"value":70341},"preAuthorizedApplications\n",{"type":254,"tag":622,"props":70343,"children":70344},{"class":624,"line":1014},[70345,70349,70353,70357,70361,70365,70369,70373,70377],{"type":254,"tag":622,"props":70346,"children":70347},{"style":1735},[70348],{"type":259,"value":69123},{"type":254,"tag":622,"props":70350,"children":70351},{"style":629},[70352],{"type":259,"value":10733},{"type":254,"tag":622,"props":70354,"children":70355},{"style":1202},[70356],{"type":259,"value":68939},{"type":254,"tag":622,"props":70358,"children":70359},{"style":629},[70360],{"type":259,"value":16922},{"type":254,"tag":622,"props":70362,"children":70363},{"style":1202},[70364],{"type":259,"value":69140},{"type":254,"tag":622,"props":70366,"children":70367},{"style":629},[70368],{"type":259,"value":17065},{"type":254,"tag":622,"props":70370,"children":70371},{"style":1202},[70372],{"type":259,"value":69551},{"type":254,"tag":622,"props":70374,"children":70375},{"style":629},[70376],{"type":259,"value":16922},{"type":254,"tag":622,"props":70378,"children":70379},{"style":1202},[70380],{"type":259,"value":69560},{"type":254,"tag":255,"props":70382,"children":70383},{},[70384],{"type":259,"value":70385},"This code calls a PowerShell function that simply creates the PreAuthorizedApplication object.",{"type":254,"tag":612,"props":70387,"children":70389},{"className":10656,"code":70388,"language":10658,"meta":205,"style":205},"\u003C#.Description\n   This function creates a new PreAuthorized application on a specified scope\n#>  \nfunction CreatePreAuthorizedApplication(\n    [string] $applicationIdToPreAuthorize,\n    [string] $scopeId)\n{\n    $preAuthorizedApplication = New-Object 'Microsoft.Open.MSGraph.Model.PreAuthorizedApplication'\n    $preAuthorizedApplication.AppId = $applicationIdToPreAuthorize\n    $preAuthorizedApplication.DelegatedPermissionIds = @($scopeId)\n    return $preAuthorizedApplication\n}\n",[70390],{"type":254,"tag":389,"props":70391,"children":70392},{"__ignoreMap":205},[70393,70408,70416,70427,70443,70471,70499,70506,70539,70564,70596,70612],{"type":254,"tag":622,"props":70394,"children":70395},{"class":624,"line":625},[70396,70400,70404],{"type":254,"tag":622,"props":70397,"children":70398},{"style":3195},[70399],{"type":259,"value":69587},{"type":254,"tag":622,"props":70401,"children":70402},{"style":3195},[70403],{"type":259,"value":594},{"type":254,"tag":622,"props":70405,"children":70406},{"style":1196},[70407],{"type":259,"value":69596},{"type":254,"tag":622,"props":70409,"children":70410},{"class":624,"line":635},[70411],{"type":254,"tag":622,"props":70412,"children":70413},{"style":3195},[70414],{"type":259,"value":70415},"   This function creates a new PreAuthorized application on a specified scope\n",{"type":254,"tag":622,"props":70417,"children":70418},{"class":624,"line":679},[70419,70423],{"type":254,"tag":622,"props":70420,"children":70421},{"style":3195},[70422],{"type":259,"value":69612},{"type":254,"tag":622,"props":70424,"children":70425},{"style":1202},[70426],{"type":259,"value":33411},{"type":254,"tag":622,"props":70428,"children":70429},{"class":624,"line":705},[70430,70434,70439],{"type":254,"tag":622,"props":70431,"children":70432},{"style":644},[70433],{"type":259,"value":21389},{"type":254,"tag":622,"props":70435,"children":70436},{"style":1735},[70437],{"type":259,"value":70438}," CreatePreAuthorizedApplication",{"type":254,"tag":622,"props":70440,"children":70441},{"style":629},[70442],{"type":259,"value":1452},{"type":254,"tag":622,"props":70444,"children":70445},{"class":624,"line":732},[70446,70450,70454,70458,70462,70467],{"type":254,"tag":622,"props":70447,"children":70448},{"style":629},[70449],{"type":259,"value":9494},{"type":254,"tag":622,"props":70451,"children":70452},{"style":644},[70453],{"type":259,"value":2851},{"type":254,"tag":622,"props":70455,"children":70456},{"style":629},[70457],{"type":259,"value":20251},{"type":254,"tag":622,"props":70459,"children":70460},{"style":629},[70461],{"type":259,"value":39174},{"type":254,"tag":622,"props":70463,"children":70464},{"style":1202},[70465],{"type":259,"value":70466},"applicationIdToPreAuthorize",{"type":254,"tag":622,"props":70468,"children":70469},{"style":629},[70470],{"type":259,"value":676},{"type":254,"tag":622,"props":70472,"children":70473},{"class":624,"line":759},[70474,70478,70482,70486,70490,70495],{"type":254,"tag":622,"props":70475,"children":70476},{"style":629},[70477],{"type":259,"value":9494},{"type":254,"tag":622,"props":70479,"children":70480},{"style":644},[70481],{"type":259,"value":2851},{"type":254,"tag":622,"props":70483,"children":70484},{"style":629},[70485],{"type":259,"value":20251},{"type":254,"tag":622,"props":70487,"children":70488},{"style":629},[70489],{"type":259,"value":39174},{"type":254,"tag":622,"props":70491,"children":70492},{"style":1202},[70493],{"type":259,"value":70494},"scopeId",{"type":254,"tag":622,"props":70496,"children":70497},{"style":629},[70498],{"type":259,"value":1685},{"type":254,"tag":622,"props":70500,"children":70501},{"class":624,"line":798},[70502],{"type":254,"tag":622,"props":70503,"children":70504},{"style":629},[70505],{"type":259,"value":632},{"type":254,"tag":622,"props":70507,"children":70508},{"class":624,"line":836},[70509,70513,70518,70522,70526,70530,70535],{"type":254,"tag":622,"props":70510,"children":70511},{"style":629},[70512],{"type":259,"value":69786},{"type":254,"tag":622,"props":70514,"children":70515},{"style":1202},[70516],{"type":259,"value":70517},"preAuthorizedApplication ",{"type":254,"tag":622,"props":70519,"children":70520},{"style":629},[70521],{"type":259,"value":1654},{"type":254,"tag":622,"props":70523,"children":70524},{"style":1735},[70525],{"type":259,"value":69245},{"type":254,"tag":622,"props":70527,"children":70528},{"style":629},[70529],{"type":259,"value":16556},{"type":254,"tag":622,"props":70531,"children":70532},{"style":664},[70533],{"type":259,"value":70534},"Microsoft.Open.MSGraph.Model.PreAuthorizedApplication",{"type":254,"tag":622,"props":70536,"children":70537},{"style":629},[70538],{"type":259,"value":16566},{"type":254,"tag":622,"props":70540,"children":70541},{"class":624,"line":862},[70542,70546,70551,70555,70559],{"type":254,"tag":622,"props":70543,"children":70544},{"style":629},[70545],{"type":259,"value":69786},{"type":254,"tag":622,"props":70547,"children":70548},{"style":1202},[70549],{"type":259,"value":70550},"preAuthorizedApplication.AppId ",{"type":254,"tag":622,"props":70552,"children":70553},{"style":629},[70554],{"type":259,"value":1654},{"type":254,"tag":622,"props":70556,"children":70557},{"style":629},[70558],{"type":259,"value":39174},{"type":254,"tag":622,"props":70560,"children":70561},{"style":1202},[70562],{"type":259,"value":70563},"applicationIdToPreAuthorize\n",{"type":254,"tag":622,"props":70565,"children":70566},{"class":624,"line":900},[70567,70571,70576,70580,70584,70588,70592],{"type":254,"tag":622,"props":70568,"children":70569},{"style":629},[70570],{"type":259,"value":69786},{"type":254,"tag":622,"props":70572,"children":70573},{"style":1202},[70574],{"type":259,"value":70575},"preAuthorizedApplication.DelegatedPermissionIds ",{"type":254,"tag":622,"props":70577,"children":70578},{"style":629},[70579],{"type":259,"value":1654},{"type":254,"tag":622,"props":70581,"children":70582},{"style":741},[70583],{"type":259,"value":17621},{"type":254,"tag":622,"props":70585,"children":70586},{"style":629},[70587],{"type":259,"value":69302},{"type":254,"tag":622,"props":70589,"children":70590},{"style":1202},[70591],{"type":259,"value":70494},{"type":254,"tag":622,"props":70593,"children":70594},{"style":629},[70595],{"type":259,"value":1685},{"type":254,"tag":622,"props":70597,"children":70598},{"class":624,"line":938},[70599,70603,70607],{"type":254,"tag":622,"props":70600,"children":70601},{"style":1196},[70602],{"type":259,"value":2358},{"type":254,"tag":622,"props":70604,"children":70605},{"style":629},[70606],{"type":259,"value":39174},{"type":254,"tag":622,"props":70608,"children":70609},{"style":1202},[70610],{"type":259,"value":70611},"preAuthorizedApplication\n",{"type":254,"tag":622,"props":70613,"children":70614},{"class":624,"line":976},[70615],{"type":254,"tag":622,"props":70616,"children":70617},{"style":629},[70618],{"type":259,"value":1111},{"type":254,"tag":455,"props":70620,"children":70622},{"id":70621},"grant-user-level-graph-api-permissions",[70623],{"type":259,"value":70624},"Grant user-level Graph API permissions",{"type":254,"tag":255,"props":70626,"children":70627},{},[70628,70630,70637],{"type":259,"value":70629},"The next step consists in specifying the permissions the application will need for the AAD endpoint: email, offline_access, openid, profile (",{"type":254,"tag":262,"props":70631,"children":70634},{"href":70632,"rel":70633},"https://docs.microsoft.com/fr-fr/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes",[266],[70635],{"type":259,"value":70636},"OpenID connect scopes",{"type":259,"value":2280},{"type":254,"tag":612,"props":70639,"children":70641},{"className":10656,"code":70640,"language":10658,"meta":205,"style":205},"# Add API permissions needed\n$requiredResourcesAccess = New-Object System.Collections.Generic.List[Microsoft.Open.MsGraph.Model.RequiredResourceAccess]\n$requiredPermissions = GetRequiredPermissions `\n    -applicationDisplayName 'Microsoft Graph' `\n    -requiredDelegatedPermissions \"User.Read|email|offline_access|openid|profile\"\n$requiredResourcesAccess.Add($requiredPermissions)   \nSet-AzureADMSApplication -ObjectId $app.Id -RequiredResourceAccess $requiredPermissions\n",[70642],{"type":254,"tag":389,"props":70643,"children":70644},{"__ignoreMap":205},[70645,70653,70690,70715,70744,70769,70798],{"type":254,"tag":622,"props":70646,"children":70647},{"class":624,"line":625},[70648],{"type":254,"tag":622,"props":70649,"children":70650},{"style":3195},[70651],{"type":259,"value":70652},"# Add API permissions needed\n",{"type":254,"tag":622,"props":70654,"children":70655},{"class":624,"line":635},[70656,70660,70665,70669,70673,70677,70681,70686],{"type":254,"tag":622,"props":70657,"children":70658},{"style":629},[70659],{"type":259,"value":16922},{"type":254,"tag":622,"props":70661,"children":70662},{"style":1202},[70663],{"type":259,"value":70664},"requiredResourcesAccess ",{"type":254,"tag":622,"props":70666,"children":70667},{"style":629},[70668],{"type":259,"value":1654},{"type":254,"tag":622,"props":70670,"children":70671},{"style":1735},[70672],{"type":259,"value":69245},{"type":254,"tag":622,"props":70674,"children":70675},{"style":1202},[70676],{"type":259,"value":69250},{"type":254,"tag":622,"props":70678,"children":70679},{"style":629},[70680],{"type":259,"value":10993},{"type":254,"tag":622,"props":70682,"children":70683},{"style":644},[70684],{"type":259,"value":70685},"Microsoft.Open.MsGraph.Model.RequiredResourceAccess",{"type":254,"tag":622,"props":70687,"children":70688},{"style":629},[70689],{"type":259,"value":11003},{"type":254,"tag":622,"props":70691,"children":70692},{"class":624,"line":679},[70693,70697,70702,70706,70711],{"type":254,"tag":622,"props":70694,"children":70695},{"style":629},[70696],{"type":259,"value":16922},{"type":254,"tag":622,"props":70698,"children":70699},{"style":1202},[70700],{"type":259,"value":70701},"requiredPermissions ",{"type":254,"tag":622,"props":70703,"children":70704},{"style":629},[70705],{"type":259,"value":1654},{"type":254,"tag":622,"props":70707,"children":70708},{"style":1202},[70709],{"type":259,"value":70710}," GetRequiredPermissions ",{"type":254,"tag":622,"props":70712,"children":70713},{"style":629},[70714],{"type":259,"value":70106},{"type":254,"tag":622,"props":70716,"children":70717},{"class":624,"line":705},[70718,70722,70727,70731,70736,70740],{"type":254,"tag":622,"props":70719,"children":70720},{"style":629},[70721],{"type":259,"value":51754},{"type":254,"tag":622,"props":70723,"children":70724},{"style":1202},[70725],{"type":259,"value":70726},"applicationDisplayName ",{"type":254,"tag":622,"props":70728,"children":70729},{"style":629},[70730],{"type":259,"value":46169},{"type":254,"tag":622,"props":70732,"children":70733},{"style":664},[70734],{"type":259,"value":70735},"Microsoft Graph",{"type":254,"tag":622,"props":70737,"children":70738},{"style":629},[70739],{"type":259,"value":46169},{"type":254,"tag":622,"props":70741,"children":70742},{"style":629},[70743],{"type":259,"value":70136},{"type":254,"tag":622,"props":70745,"children":70746},{"class":624,"line":732},[70747,70751,70756,70760,70765],{"type":254,"tag":622,"props":70748,"children":70749},{"style":629},[70750],{"type":259,"value":51754},{"type":254,"tag":622,"props":70752,"children":70753},{"style":1202},[70754],{"type":259,"value":70755},"requiredDelegatedPermissions ",{"type":254,"tag":622,"props":70757,"children":70758},{"style":629},[70759],{"type":259,"value":652},{"type":254,"tag":622,"props":70761,"children":70762},{"style":664},[70763],{"type":259,"value":70764},"User.Read|email|offline_access|openid|profile",{"type":254,"tag":622,"props":70766,"children":70767},{"style":629},[70768],{"type":259,"value":9909},{"type":254,"tag":622,"props":70770,"children":70771},{"class":624,"line":759},[70772,70776,70781,70785,70790,70794],{"type":254,"tag":622,"props":70773,"children":70774},{"style":629},[70775],{"type":259,"value":16922},{"type":254,"tag":622,"props":70777,"children":70778},{"style":1202},[70779],{"type":259,"value":70780},"requiredResourcesAccess.Add",{"type":254,"tag":622,"props":70782,"children":70783},{"style":629},[70784],{"type":259,"value":69302},{"type":254,"tag":622,"props":70786,"children":70787},{"style":1202},[70788],{"type":259,"value":70789},"requiredPermissions",{"type":254,"tag":622,"props":70791,"children":70792},{"style":629},[70793],{"type":259,"value":2309},{"type":254,"tag":622,"props":70795,"children":70796},{"style":1202},[70797],{"type":259,"value":35234},{"type":254,"tag":622,"props":70799,"children":70800},{"class":624,"line":798},[70801,70805,70809,70813,70817,70821,70825,70830,70834],{"type":254,"tag":622,"props":70802,"children":70803},{"style":1735},[70804],{"type":259,"value":69123},{"type":254,"tag":622,"props":70806,"children":70807},{"style":629},[70808],{"type":259,"value":10733},{"type":254,"tag":622,"props":70810,"children":70811},{"style":1202},[70812],{"type":259,"value":68939},{"type":254,"tag":622,"props":70814,"children":70815},{"style":629},[70816],{"type":259,"value":16922},{"type":254,"tag":622,"props":70818,"children":70819},{"style":1202},[70820],{"type":259,"value":69140},{"type":254,"tag":622,"props":70822,"children":70823},{"style":629},[70824],{"type":259,"value":17065},{"type":254,"tag":622,"props":70826,"children":70827},{"style":1202},[70828],{"type":259,"value":70829},"RequiredResourceAccess ",{"type":254,"tag":622,"props":70831,"children":70832},{"style":629},[70833],{"type":259,"value":16922},{"type":254,"tag":622,"props":70835,"children":70836},{"style":1202},[70837],{"type":259,"value":70838},"requiredPermissions\n",{"type":254,"tag":255,"props":70840,"children":70841},{},[70842,70844,70850],{"type":259,"value":70843},"This code calls a PowerShell function ",{"type":254,"tag":389,"props":70845,"children":70847},{"className":70846},[],[70848],{"type":259,"value":70849},"GetRequiredPermissions",{"type":259,"value":70851}," that add the delegated or application permissions specified in parameter. Here we only ask for delegated permissions of Microsoft Graph needed to retrieve an OpenId Connect token but this function is generic and could be used to require scopes or roles of other APIs.",{"type":254,"tag":612,"props":70853,"children":70855},{"className":10656,"code":70854,"language":10658,"meta":205,"style":205},"# Example: GetRequiredPermissions \"Microsoft Graph\"  \"Graph.Read|User.Read\"\n# See also: http://stackoverflow.com/questions/42164581/how-to-configure-a-new-azure-ad-application-through-powershell\nfunction GetRequiredPermissions(\n    [string] $applicationDisplayName,\n    [string] $requiredDelegatedPermissions,\n    [string]$requiredApplicationPermissions,\n    $servicePrincipal)\n{\n    # If we are passed the service principal we use it directly, otherwise we find it from the display name (which might not be unique)\n    if ($servicePrincipal)\n    {\n        $sp = $servicePrincipal\n    }\n    else\n    {\n        $sp = Get-AzureADServicePrincipal -Filter \"DisplayName eq '$applicationDisplayName'\"\n    }\n\n    $requiredAccess = New-Object Microsoft.Open.MsGraph.Model.RequiredResourceAccess\n    $requiredAccess.ResourceAppId = $sp.AppId \n    $requiredAccess.ResourceAccess = New-Object System.Collections.Generic.List[Microsoft.Open.MsGraph.Model.ResourceAccess]\n\n    # $sp.Oauth2Permissions | Select Id,AdminConsentDisplayName,Value: To see the list of all the Delegated permissions for the application:\n    if ($requiredDelegatedPermissions)\n    {\n        AddResourcePermission $requiredAccess -exposedPermissions $sp.Oauth2Permissions -requiredAccesses $requiredDelegatedPermissions -permissionType \"Scope\"\n    }\n    \n    # $sp.AppRoles | Select Id,AdminConsentDisplayName,Value: To see the list of all the Application permissions for the application\n    if ($requiredApplicationPermissions)\n    {\n        AddResourcePermission $requiredAccess -exposedPermissions $sp.AppRoles -requiredAccesses $requiredApplicationPermissions -permissionType \"Role\"\n    }\n    return $requiredAccess\n}\n",[70856],{"type":254,"tag":389,"props":70857,"children":70858},{"__ignoreMap":205},[70859,70867,70875,70891,70919,70947,70971,70987,70994,71002,71022,71029,71055,71062,71070,71077,71131,71138,71145,71170,71195,71232,71239,71247,71266,71273,71345,71352,71359,71367,71386,71393,71463,71470,71486],{"type":254,"tag":622,"props":70860,"children":70861},{"class":624,"line":625},[70862],{"type":254,"tag":622,"props":70863,"children":70864},{"style":3195},[70865],{"type":259,"value":70866},"# Example: GetRequiredPermissions \"Microsoft Graph\"  \"Graph.Read|User.Read\"\n",{"type":254,"tag":622,"props":70868,"children":70869},{"class":624,"line":635},[70870],{"type":254,"tag":622,"props":70871,"children":70872},{"style":3195},[70873],{"type":259,"value":70874},"# See also: http://stackoverflow.com/questions/42164581/how-to-configure-a-new-azure-ad-application-through-powershell\n",{"type":254,"tag":622,"props":70876,"children":70877},{"class":624,"line":679},[70878,70882,70887],{"type":254,"tag":622,"props":70879,"children":70880},{"style":644},[70881],{"type":259,"value":21389},{"type":254,"tag":622,"props":70883,"children":70884},{"style":1735},[70885],{"type":259,"value":70886}," GetRequiredPermissions",{"type":254,"tag":622,"props":70888,"children":70889},{"style":629},[70890],{"type":259,"value":1452},{"type":254,"tag":622,"props":70892,"children":70893},{"class":624,"line":705},[70894,70898,70902,70906,70910,70915],{"type":254,"tag":622,"props":70895,"children":70896},{"style":629},[70897],{"type":259,"value":9494},{"type":254,"tag":622,"props":70899,"children":70900},{"style":644},[70901],{"type":259,"value":2851},{"type":254,"tag":622,"props":70903,"children":70904},{"style":629},[70905],{"type":259,"value":20251},{"type":254,"tag":622,"props":70907,"children":70908},{"style":629},[70909],{"type":259,"value":39174},{"type":254,"tag":622,"props":70911,"children":70912},{"style":1202},[70913],{"type":259,"value":70914},"applicationDisplayName",{"type":254,"tag":622,"props":70916,"children":70917},{"style":629},[70918],{"type":259,"value":676},{"type":254,"tag":622,"props":70920,"children":70921},{"class":624,"line":732},[70922,70926,70930,70934,70938,70943],{"type":254,"tag":622,"props":70923,"children":70924},{"style":629},[70925],{"type":259,"value":9494},{"type":254,"tag":622,"props":70927,"children":70928},{"style":644},[70929],{"type":259,"value":2851},{"type":254,"tag":622,"props":70931,"children":70932},{"style":629},[70933],{"type":259,"value":20251},{"type":254,"tag":622,"props":70935,"children":70936},{"style":629},[70937],{"type":259,"value":39174},{"type":254,"tag":622,"props":70939,"children":70940},{"style":1202},[70941],{"type":259,"value":70942},"requiredDelegatedPermissions",{"type":254,"tag":622,"props":70944,"children":70945},{"style":629},[70946],{"type":259,"value":676},{"type":254,"tag":622,"props":70948,"children":70949},{"class":624,"line":759},[70950,70954,70958,70962,70967],{"type":254,"tag":622,"props":70951,"children":70952},{"style":629},[70953],{"type":259,"value":9494},{"type":254,"tag":622,"props":70955,"children":70956},{"style":644},[70957],{"type":259,"value":2851},{"type":254,"tag":622,"props":70959,"children":70960},{"style":629},[70961],{"type":259,"value":39123},{"type":254,"tag":622,"props":70963,"children":70964},{"style":1202},[70965],{"type":259,"value":70966},"requiredApplicationPermissions",{"type":254,"tag":622,"props":70968,"children":70969},{"style":629},[70970],{"type":259,"value":676},{"type":254,"tag":622,"props":70972,"children":70973},{"class":624,"line":798},[70974,70978,70983],{"type":254,"tag":622,"props":70975,"children":70976},{"style":629},[70977],{"type":259,"value":69786},{"type":254,"tag":622,"props":70979,"children":70980},{"style":1202},[70981],{"type":259,"value":70982},"servicePrincipal",{"type":254,"tag":622,"props":70984,"children":70985},{"style":629},[70986],{"type":259,"value":1685},{"type":254,"tag":622,"props":70988,"children":70989},{"class":624,"line":836},[70990],{"type":254,"tag":622,"props":70991,"children":70992},{"style":629},[70993],{"type":259,"value":632},{"type":254,"tag":622,"props":70995,"children":70996},{"class":624,"line":862},[70997],{"type":254,"tag":622,"props":70998,"children":70999},{"style":3195},[71000],{"type":259,"value":71001},"    # If we are passed the service principal we use it directly, otherwise we find it from the display name (which might not be unique)\n",{"type":254,"tag":622,"props":71003,"children":71004},{"class":624,"line":900},[71005,71009,71014,71018],{"type":254,"tag":622,"props":71006,"children":71007},{"style":1196},[71008],{"type":259,"value":3542},{"type":254,"tag":622,"props":71010,"children":71011},{"style":629},[71012],{"type":259,"value":71013}," ($",{"type":254,"tag":622,"props":71015,"children":71016},{"style":1202},[71017],{"type":259,"value":70982},{"type":254,"tag":622,"props":71019,"children":71020},{"style":629},[71021],{"type":259,"value":1685},{"type":254,"tag":622,"props":71023,"children":71024},{"class":624,"line":938},[71025],{"type":254,"tag":622,"props":71026,"children":71027},{"style":629},[71028],{"type":259,"value":3574},{"type":254,"tag":622,"props":71030,"children":71031},{"class":624,"line":976},[71032,71037,71042,71046,71050],{"type":254,"tag":622,"props":71033,"children":71034},{"style":629},[71035],{"type":259,"value":71036},"        $",{"type":254,"tag":622,"props":71038,"children":71039},{"style":1202},[71040],{"type":259,"value":71041},"sp ",{"type":254,"tag":622,"props":71043,"children":71044},{"style":629},[71045],{"type":259,"value":1654},{"type":254,"tag":622,"props":71047,"children":71048},{"style":629},[71049],{"type":259,"value":39174},{"type":254,"tag":622,"props":71051,"children":71052},{"style":1202},[71053],{"type":259,"value":71054},"servicePrincipal\n",{"type":254,"tag":622,"props":71056,"children":71057},{"class":624,"line":1014},[71058],{"type":254,"tag":622,"props":71059,"children":71060},{"style":629},[71061],{"type":259,"value":1093},{"type":254,"tag":622,"props":71063,"children":71064},{"class":624,"line":1052},[71065],{"type":254,"tag":622,"props":71066,"children":71067},{"style":1196},[71068],{"type":259,"value":71069},"    else\n",{"type":254,"tag":622,"props":71071,"children":71072},{"class":624,"line":1078},[71073],{"type":254,"tag":622,"props":71074,"children":71075},{"style":629},[71076],{"type":259,"value":3574},{"type":254,"tag":622,"props":71078,"children":71079},{"class":624,"line":1087},[71080,71084,71088,71092,71097,71101,71106,71110,71115,71119,71123,71127],{"type":254,"tag":622,"props":71081,"children":71082},{"style":629},[71083],{"type":259,"value":71036},{"type":254,"tag":622,"props":71085,"children":71086},{"style":1202},[71087],{"type":259,"value":71041},{"type":254,"tag":622,"props":71089,"children":71090},{"style":629},[71091],{"type":259,"value":1654},{"type":254,"tag":622,"props":71093,"children":71094},{"style":1735},[71095],{"type":259,"value":71096}," Get-AzureADServicePrincipal",{"type":254,"tag":622,"props":71098,"children":71099},{"style":629},[71100],{"type":259,"value":10733},{"type":254,"tag":622,"props":71102,"children":71103},{"style":1202},[71104],{"type":259,"value":71105},"Filter ",{"type":254,"tag":622,"props":71107,"children":71108},{"style":629},[71109],{"type":259,"value":652},{"type":254,"tag":622,"props":71111,"children":71112},{"style":664},[71113],{"type":259,"value":71114},"DisplayName eq '",{"type":254,"tag":622,"props":71116,"children":71117},{"style":629},[71118],{"type":259,"value":16922},{"type":254,"tag":622,"props":71120,"children":71121},{"style":1202},[71122],{"type":259,"value":70914},{"type":254,"tag":622,"props":71124,"children":71125},{"style":664},[71126],{"type":259,"value":46169},{"type":254,"tag":622,"props":71128,"children":71129},{"style":629},[71130],{"type":259,"value":9909},{"type":254,"tag":622,"props":71132,"children":71133},{"class":624,"line":1096},[71134],{"type":254,"tag":622,"props":71135,"children":71136},{"style":629},[71137],{"type":259,"value":1093},{"type":254,"tag":622,"props":71139,"children":71140},{"class":624,"line":1105},[71141],{"type":254,"tag":622,"props":71142,"children":71143},{"emptyLinePlaceholder":1699},[71144],{"type":259,"value":1702},{"type":254,"tag":622,"props":71146,"children":71147},{"class":624,"line":4131},[71148,71152,71157,71161,71165],{"type":254,"tag":622,"props":71149,"children":71150},{"style":629},[71151],{"type":259,"value":69786},{"type":254,"tag":622,"props":71153,"children":71154},{"style":1202},[71155],{"type":259,"value":71156},"requiredAccess ",{"type":254,"tag":622,"props":71158,"children":71159},{"style":629},[71160],{"type":259,"value":1654},{"type":254,"tag":622,"props":71162,"children":71163},{"style":1735},[71164],{"type":259,"value":69245},{"type":254,"tag":622,"props":71166,"children":71167},{"style":1202},[71168],{"type":259,"value":71169}," Microsoft.Open.MsGraph.Model.RequiredResourceAccess\n",{"type":254,"tag":622,"props":71171,"children":71172},{"class":624,"line":4183},[71173,71177,71182,71186,71190],{"type":254,"tag":622,"props":71174,"children":71175},{"style":629},[71176],{"type":259,"value":69786},{"type":254,"tag":622,"props":71178,"children":71179},{"style":1202},[71180],{"type":259,"value":71181},"requiredAccess.ResourceAppId ",{"type":254,"tag":622,"props":71183,"children":71184},{"style":629},[71185],{"type":259,"value":1654},{"type":254,"tag":622,"props":71187,"children":71188},{"style":629},[71189],{"type":259,"value":39174},{"type":254,"tag":622,"props":71191,"children":71192},{"style":1202},[71193],{"type":259,"value":71194},"sp.AppId \n",{"type":254,"tag":622,"props":71196,"children":71197},{"class":624,"line":4208},[71198,71202,71207,71211,71215,71219,71223,71228],{"type":254,"tag":622,"props":71199,"children":71200},{"style":629},[71201],{"type":259,"value":69786},{"type":254,"tag":622,"props":71203,"children":71204},{"style":1202},[71205],{"type":259,"value":71206},"requiredAccess.ResourceAccess ",{"type":254,"tag":622,"props":71208,"children":71209},{"style":629},[71210],{"type":259,"value":1654},{"type":254,"tag":622,"props":71212,"children":71213},{"style":1735},[71214],{"type":259,"value":69245},{"type":254,"tag":622,"props":71216,"children":71217},{"style":1202},[71218],{"type":259,"value":69250},{"type":254,"tag":622,"props":71220,"children":71221},{"style":629},[71222],{"type":259,"value":10993},{"type":254,"tag":622,"props":71224,"children":71225},{"style":644},[71226],{"type":259,"value":71227},"Microsoft.Open.MsGraph.Model.ResourceAccess",{"type":254,"tag":622,"props":71229,"children":71230},{"style":629},[71231],{"type":259,"value":11003},{"type":254,"tag":622,"props":71233,"children":71234},{"class":624,"line":4216},[71235],{"type":254,"tag":622,"props":71236,"children":71237},{"emptyLinePlaceholder":1699},[71238],{"type":259,"value":1702},{"type":254,"tag":622,"props":71240,"children":71241},{"class":624,"line":4224},[71242],{"type":254,"tag":622,"props":71243,"children":71244},{"style":3195},[71245],{"type":259,"value":71246},"    # $sp.Oauth2Permissions | Select Id,AdminConsentDisplayName,Value: To see the list of all the Delegated permissions for the application:\n",{"type":254,"tag":622,"props":71248,"children":71249},{"class":624,"line":4268},[71250,71254,71258,71262],{"type":254,"tag":622,"props":71251,"children":71252},{"style":1196},[71253],{"type":259,"value":3542},{"type":254,"tag":622,"props":71255,"children":71256},{"style":629},[71257],{"type":259,"value":71013},{"type":254,"tag":622,"props":71259,"children":71260},{"style":1202},[71261],{"type":259,"value":70942},{"type":254,"tag":622,"props":71263,"children":71264},{"style":629},[71265],{"type":259,"value":1685},{"type":254,"tag":622,"props":71267,"children":71268},{"class":624,"line":4276},[71269],{"type":254,"tag":622,"props":71270,"children":71271},{"style":629},[71272],{"type":259,"value":3574},{"type":254,"tag":622,"props":71274,"children":71275},{"class":624,"line":4332},[71276,71281,71285,71289,71293,71298,71302,71307,71311,71316,71320,71324,71328,71333,71337,71341],{"type":254,"tag":622,"props":71277,"children":71278},{"style":1202},[71279],{"type":259,"value":71280},"        AddResourcePermission ",{"type":254,"tag":622,"props":71282,"children":71283},{"style":629},[71284],{"type":259,"value":16922},{"type":254,"tag":622,"props":71286,"children":71287},{"style":1202},[71288],{"type":259,"value":71156},{"type":254,"tag":622,"props":71290,"children":71291},{"style":629},[71292],{"type":259,"value":17065},{"type":254,"tag":622,"props":71294,"children":71295},{"style":1202},[71296],{"type":259,"value":71297},"exposedPermissions ",{"type":254,"tag":622,"props":71299,"children":71300},{"style":629},[71301],{"type":259,"value":16922},{"type":254,"tag":622,"props":71303,"children":71304},{"style":1202},[71305],{"type":259,"value":71306},"sp.Oauth2Permissions ",{"type":254,"tag":622,"props":71308,"children":71309},{"style":629},[71310],{"type":259,"value":17065},{"type":254,"tag":622,"props":71312,"children":71313},{"style":1202},[71314],{"type":259,"value":71315},"requiredAccesses ",{"type":254,"tag":622,"props":71317,"children":71318},{"style":629},[71319],{"type":259,"value":16922},{"type":254,"tag":622,"props":71321,"children":71322},{"style":1202},[71323],{"type":259,"value":70755},{"type":254,"tag":622,"props":71325,"children":71326},{"style":629},[71327],{"type":259,"value":17065},{"type":254,"tag":622,"props":71329,"children":71330},{"style":1202},[71331],{"type":259,"value":71332},"permissionType ",{"type":254,"tag":622,"props":71334,"children":71335},{"style":629},[71336],{"type":259,"value":652},{"type":254,"tag":622,"props":71338,"children":71339},{"style":664},[71340],{"type":259,"value":910},{"type":254,"tag":622,"props":71342,"children":71343},{"style":629},[71344],{"type":259,"value":9909},{"type":254,"tag":622,"props":71346,"children":71347},{"class":624,"line":4376},[71348],{"type":254,"tag":622,"props":71349,"children":71350},{"style":629},[71351],{"type":259,"value":1093},{"type":254,"tag":622,"props":71353,"children":71354},{"class":624,"line":4384},[71355],{"type":254,"tag":622,"props":71356,"children":71357},{"style":1202},[71358],{"type":259,"value":7336},{"type":254,"tag":622,"props":71360,"children":71361},{"class":624,"line":4416},[71362],{"type":254,"tag":622,"props":71363,"children":71364},{"style":3195},[71365],{"type":259,"value":71366},"    # $sp.AppRoles | Select Id,AdminConsentDisplayName,Value: To see the list of all the Application permissions for the application\n",{"type":254,"tag":622,"props":71368,"children":71369},{"class":624,"line":4456},[71370,71374,71378,71382],{"type":254,"tag":622,"props":71371,"children":71372},{"style":1196},[71373],{"type":259,"value":3542},{"type":254,"tag":622,"props":71375,"children":71376},{"style":629},[71377],{"type":259,"value":71013},{"type":254,"tag":622,"props":71379,"children":71380},{"style":1202},[71381],{"type":259,"value":70966},{"type":254,"tag":622,"props":71383,"children":71384},{"style":629},[71385],{"type":259,"value":1685},{"type":254,"tag":622,"props":71387,"children":71388},{"class":624,"line":4488},[71389],{"type":254,"tag":622,"props":71390,"children":71391},{"style":629},[71392],{"type":259,"value":3574},{"type":254,"tag":622,"props":71394,"children":71395},{"class":624,"line":4496},[71396,71400,71404,71408,71412,71416,71420,71425,71429,71433,71437,71442,71446,71450,71454,71459],{"type":254,"tag":622,"props":71397,"children":71398},{"style":1202},[71399],{"type":259,"value":71280},{"type":254,"tag":622,"props":71401,"children":71402},{"style":629},[71403],{"type":259,"value":16922},{"type":254,"tag":622,"props":71405,"children":71406},{"style":1202},[71407],{"type":259,"value":71156},{"type":254,"tag":622,"props":71409,"children":71410},{"style":629},[71411],{"type":259,"value":17065},{"type":254,"tag":622,"props":71413,"children":71414},{"style":1202},[71415],{"type":259,"value":71297},{"type":254,"tag":622,"props":71417,"children":71418},{"style":629},[71419],{"type":259,"value":16922},{"type":254,"tag":622,"props":71421,"children":71422},{"style":1202},[71423],{"type":259,"value":71424},"sp.AppRoles ",{"type":254,"tag":622,"props":71426,"children":71427},{"style":629},[71428],{"type":259,"value":17065},{"type":254,"tag":622,"props":71430,"children":71431},{"style":1202},[71432],{"type":259,"value":71315},{"type":254,"tag":622,"props":71434,"children":71435},{"style":629},[71436],{"type":259,"value":16922},{"type":254,"tag":622,"props":71438,"children":71439},{"style":1202},[71440],{"type":259,"value":71441},"requiredApplicationPermissions ",{"type":254,"tag":622,"props":71443,"children":71444},{"style":629},[71445],{"type":259,"value":17065},{"type":254,"tag":622,"props":71447,"children":71448},{"style":1202},[71449],{"type":259,"value":71332},{"type":254,"tag":622,"props":71451,"children":71452},{"style":629},[71453],{"type":259,"value":652},{"type":254,"tag":622,"props":71455,"children":71456},{"style":664},[71457],{"type":259,"value":71458},"Role",{"type":254,"tag":622,"props":71460,"children":71461},{"style":629},[71462],{"type":259,"value":9909},{"type":254,"tag":622,"props":71464,"children":71465},{"class":624,"line":4564},[71466],{"type":254,"tag":622,"props":71467,"children":71468},{"style":629},[71469],{"type":259,"value":1093},{"type":254,"tag":622,"props":71471,"children":71472},{"class":624,"line":4572},[71473,71477,71481],{"type":254,"tag":622,"props":71474,"children":71475},{"style":1196},[71476],{"type":259,"value":2358},{"type":254,"tag":622,"props":71478,"children":71479},{"style":629},[71480],{"type":259,"value":39174},{"type":254,"tag":622,"props":71482,"children":71483},{"style":1202},[71484],{"type":259,"value":71485},"requiredAccess\n",{"type":254,"tag":622,"props":71487,"children":71488},{"class":624,"line":4644},[71489],{"type":254,"tag":622,"props":71490,"children":71491},{"style":629},[71492],{"type":259,"value":1111},{"type":254,"tag":255,"props":71494,"children":71495},{},[71496,71497,71502,71504,71510],{"type":259,"value":10970},{"type":254,"tag":389,"props":71498,"children":71500},{"className":71499},[],[71501],{"type":259,"value":70849},{"type":259,"value":71503}," function calls a ",{"type":254,"tag":389,"props":71505,"children":71507},{"className":71506},[],[71508],{"type":259,"value":71509},"AddResourcePermission",{"type":259,"value":71511}," function that creates permissions (ResourceAccess objects).",{"type":254,"tag":612,"props":71513,"children":71515},{"className":10656,"code":71514,"language":10658,"meta":205,"style":205},"# Adds the requiredAccesses (expressed as a pipe separated string) to the requiredAccess structure\n# The exposed permissions are in the $exposedPermissions collection, and the type of permission (Scope | Role) is \n# described in $permissionType\nfunction AddResourcePermission(\n    $requiredAccess,\n    $exposedPermissions,\n    [string]$requiredAccesses,\n    [string]$permissionType)\n{\n        foreach($permission in $requiredAccesses.Trim().Split(\"|\"))\n        {\n            foreach($exposedPermission in $exposedPermissions)\n            {\n                if ($exposedPermission.Value -eq $permission)\n                {\n                    $resourceAccess = New-Object Microsoft.Open.MsGraph.Model.ResourceAccess\n                    $resourceAccess.Type = $permissionType # Scope = Delegated permissions | Role = Application permissions\n                    $resourceAccess.Id = $exposedPermission.Id # Read directory data\n                    $requiredAccess.ResourceAccess.Add($resourceAccess)\n                }\n            }\n        }\n}\n",[71516],{"type":254,"tag":389,"props":71517,"children":71518},{"__ignoreMap":205},[71519,71527,71535,71543,71559,71575,71591,71615,71639,71646,71705,71712,71745,71752,71787,71794,71820,71849,71879,71904,71911,71918,71925],{"type":254,"tag":622,"props":71520,"children":71521},{"class":624,"line":625},[71522],{"type":254,"tag":622,"props":71523,"children":71524},{"style":3195},[71525],{"type":259,"value":71526},"# Adds the requiredAccesses (expressed as a pipe separated string) to the requiredAccess structure\n",{"type":254,"tag":622,"props":71528,"children":71529},{"class":624,"line":635},[71530],{"type":254,"tag":622,"props":71531,"children":71532},{"style":3195},[71533],{"type":259,"value":71534},"# The exposed permissions are in the $exposedPermissions collection, and the type of permission (Scope | Role) is \n",{"type":254,"tag":622,"props":71536,"children":71537},{"class":624,"line":679},[71538],{"type":254,"tag":622,"props":71539,"children":71540},{"style":3195},[71541],{"type":259,"value":71542},"# described in $permissionType\n",{"type":254,"tag":622,"props":71544,"children":71545},{"class":624,"line":705},[71546,71550,71555],{"type":254,"tag":622,"props":71547,"children":71548},{"style":644},[71549],{"type":259,"value":21389},{"type":254,"tag":622,"props":71551,"children":71552},{"style":1735},[71553],{"type":259,"value":71554}," AddResourcePermission",{"type":254,"tag":622,"props":71556,"children":71557},{"style":629},[71558],{"type":259,"value":1452},{"type":254,"tag":622,"props":71560,"children":71561},{"class":624,"line":732},[71562,71566,71571],{"type":254,"tag":622,"props":71563,"children":71564},{"style":629},[71565],{"type":259,"value":69786},{"type":254,"tag":622,"props":71567,"children":71568},{"style":1202},[71569],{"type":259,"value":71570},"requiredAccess",{"type":254,"tag":622,"props":71572,"children":71573},{"style":629},[71574],{"type":259,"value":676},{"type":254,"tag":622,"props":71576,"children":71577},{"class":624,"line":759},[71578,71582,71587],{"type":254,"tag":622,"props":71579,"children":71580},{"style":629},[71581],{"type":259,"value":69786},{"type":254,"tag":622,"props":71583,"children":71584},{"style":1202},[71585],{"type":259,"value":71586},"exposedPermissions",{"type":254,"tag":622,"props":71588,"children":71589},{"style":629},[71590],{"type":259,"value":676},{"type":254,"tag":622,"props":71592,"children":71593},{"class":624,"line":798},[71594,71598,71602,71606,71611],{"type":254,"tag":622,"props":71595,"children":71596},{"style":629},[71597],{"type":259,"value":9494},{"type":254,"tag":622,"props":71599,"children":71600},{"style":644},[71601],{"type":259,"value":2851},{"type":254,"tag":622,"props":71603,"children":71604},{"style":629},[71605],{"type":259,"value":39123},{"type":254,"tag":622,"props":71607,"children":71608},{"style":1202},[71609],{"type":259,"value":71610},"requiredAccesses",{"type":254,"tag":622,"props":71612,"children":71613},{"style":629},[71614],{"type":259,"value":676},{"type":254,"tag":622,"props":71616,"children":71617},{"class":624,"line":836},[71618,71622,71626,71630,71635],{"type":254,"tag":622,"props":71619,"children":71620},{"style":629},[71621],{"type":259,"value":9494},{"type":254,"tag":622,"props":71623,"children":71624},{"style":644},[71625],{"type":259,"value":2851},{"type":254,"tag":622,"props":71627,"children":71628},{"style":629},[71629],{"type":259,"value":39123},{"type":254,"tag":622,"props":71631,"children":71632},{"style":1202},[71633],{"type":259,"value":71634},"permissionType",{"type":254,"tag":622,"props":71636,"children":71637},{"style":629},[71638],{"type":259,"value":1685},{"type":254,"tag":622,"props":71640,"children":71641},{"class":624,"line":862},[71642],{"type":254,"tag":622,"props":71643,"children":71644},{"style":629},[71645],{"type":259,"value":632},{"type":254,"tag":622,"props":71647,"children":71648},{"class":624,"line":900},[71649,71654,71658,71663,71667,71671,71676,71680,71685,71689,71693,71697,71701],{"type":254,"tag":622,"props":71650,"children":71651},{"style":1196},[71652],{"type":259,"value":71653},"        foreach",{"type":254,"tag":622,"props":71655,"children":71656},{"style":629},[71657],{"type":259,"value":69302},{"type":254,"tag":622,"props":71659,"children":71660},{"style":1202},[71661],{"type":259,"value":71662},"permission ",{"type":254,"tag":622,"props":71664,"children":71665},{"style":1196},[71666],{"type":259,"value":49173},{"type":254,"tag":622,"props":71668,"children":71669},{"style":629},[71670],{"type":259,"value":39174},{"type":254,"tag":622,"props":71672,"children":71673},{"style":1202},[71674],{"type":259,"value":71675},"requiredAccesses.Trim",{"type":254,"tag":622,"props":71677,"children":71678},{"style":629},[71679],{"type":259,"value":2724},{"type":254,"tag":622,"props":71681,"children":71682},{"style":1202},[71683],{"type":259,"value":71684},".Split",{"type":254,"tag":622,"props":71686,"children":71687},{"style":629},[71688],{"type":259,"value":1668},{"type":254,"tag":622,"props":71690,"children":71691},{"style":629},[71692],{"type":259,"value":652},{"type":254,"tag":622,"props":71694,"children":71695},{"style":664},[71696],{"type":259,"value":17875},{"type":254,"tag":622,"props":71698,"children":71699},{"style":629},[71700],{"type":259,"value":652},{"type":254,"tag":622,"props":71702,"children":71703},{"style":629},[71704],{"type":259,"value":7086},{"type":254,"tag":622,"props":71706,"children":71707},{"class":624,"line":938},[71708],{"type":254,"tag":622,"props":71709,"children":71710},{"style":629},[71711],{"type":259,"value":7094},{"type":254,"tag":622,"props":71713,"children":71714},{"class":624,"line":976},[71715,71720,71724,71729,71733,71737,71741],{"type":254,"tag":622,"props":71716,"children":71717},{"style":1196},[71718],{"type":259,"value":71719},"            foreach",{"type":254,"tag":622,"props":71721,"children":71722},{"style":629},[71723],{"type":259,"value":69302},{"type":254,"tag":622,"props":71725,"children":71726},{"style":1202},[71727],{"type":259,"value":71728},"exposedPermission ",{"type":254,"tag":622,"props":71730,"children":71731},{"style":1196},[71732],{"type":259,"value":49173},{"type":254,"tag":622,"props":71734,"children":71735},{"style":629},[71736],{"type":259,"value":39174},{"type":254,"tag":622,"props":71738,"children":71739},{"style":1202},[71740],{"type":259,"value":71586},{"type":254,"tag":622,"props":71742,"children":71743},{"style":629},[71744],{"type":259,"value":1685},{"type":254,"tag":622,"props":71746,"children":71747},{"class":624,"line":1014},[71748],{"type":254,"tag":622,"props":71749,"children":71750},{"style":629},[71751],{"type":259,"value":11957},{"type":254,"tag":622,"props":71753,"children":71754},{"class":624,"line":1052},[71755,71760,71764,71769,71774,71778,71783],{"type":254,"tag":622,"props":71756,"children":71757},{"style":1196},[71758],{"type":259,"value":71759},"                if",{"type":254,"tag":622,"props":71761,"children":71762},{"style":629},[71763],{"type":259,"value":71013},{"type":254,"tag":622,"props":71765,"children":71766},{"style":1202},[71767],{"type":259,"value":71768},"exposedPermission.Value ",{"type":254,"tag":622,"props":71770,"children":71771},{"style":629},[71772],{"type":259,"value":71773},"-eq",{"type":254,"tag":622,"props":71775,"children":71776},{"style":629},[71777],{"type":259,"value":39174},{"type":254,"tag":622,"props":71779,"children":71780},{"style":1202},[71781],{"type":259,"value":71782},"permission",{"type":254,"tag":622,"props":71784,"children":71785},{"style":629},[71786],{"type":259,"value":1685},{"type":254,"tag":622,"props":71788,"children":71789},{"class":624,"line":1078},[71790],{"type":254,"tag":622,"props":71791,"children":71792},{"style":629},[71793],{"type":259,"value":12482},{"type":254,"tag":622,"props":71795,"children":71796},{"class":624,"line":1087},[71797,71802,71807,71811,71815],{"type":254,"tag":622,"props":71798,"children":71799},{"style":629},[71800],{"type":259,"value":71801},"                    $",{"type":254,"tag":622,"props":71803,"children":71804},{"style":1202},[71805],{"type":259,"value":71806},"resourceAccess ",{"type":254,"tag":622,"props":71808,"children":71809},{"style":629},[71810],{"type":259,"value":1654},{"type":254,"tag":622,"props":71812,"children":71813},{"style":1735},[71814],{"type":259,"value":69245},{"type":254,"tag":622,"props":71816,"children":71817},{"style":1202},[71818],{"type":259,"value":71819}," Microsoft.Open.MsGraph.Model.ResourceAccess\n",{"type":254,"tag":622,"props":71821,"children":71822},{"class":624,"line":1096},[71823,71827,71832,71836,71840,71844],{"type":254,"tag":622,"props":71824,"children":71825},{"style":629},[71826],{"type":259,"value":71801},{"type":254,"tag":622,"props":71828,"children":71829},{"style":1202},[71830],{"type":259,"value":71831},"resourceAccess.Type ",{"type":254,"tag":622,"props":71833,"children":71834},{"style":629},[71835],{"type":259,"value":1654},{"type":254,"tag":622,"props":71837,"children":71838},{"style":629},[71839],{"type":259,"value":39174},{"type":254,"tag":622,"props":71841,"children":71842},{"style":1202},[71843],{"type":259,"value":71332},{"type":254,"tag":622,"props":71845,"children":71846},{"style":3195},[71847],{"type":259,"value":71848},"# Scope = Delegated permissions | Role = Application permissions\n",{"type":254,"tag":622,"props":71850,"children":71851},{"class":624,"line":1105},[71852,71856,71861,71865,71869,71874],{"type":254,"tag":622,"props":71853,"children":71854},{"style":629},[71855],{"type":259,"value":71801},{"type":254,"tag":622,"props":71857,"children":71858},{"style":1202},[71859],{"type":259,"value":71860},"resourceAccess.Id ",{"type":254,"tag":622,"props":71862,"children":71863},{"style":629},[71864],{"type":259,"value":1654},{"type":254,"tag":622,"props":71866,"children":71867},{"style":629},[71868],{"type":259,"value":39174},{"type":254,"tag":622,"props":71870,"children":71871},{"style":1202},[71872],{"type":259,"value":71873},"exposedPermission.Id ",{"type":254,"tag":622,"props":71875,"children":71876},{"style":3195},[71877],{"type":259,"value":71878},"# Read directory data\n",{"type":254,"tag":622,"props":71880,"children":71881},{"class":624,"line":4131},[71882,71886,71891,71895,71900],{"type":254,"tag":622,"props":71883,"children":71884},{"style":629},[71885],{"type":259,"value":71801},{"type":254,"tag":622,"props":71887,"children":71888},{"style":1202},[71889],{"type":259,"value":71890},"requiredAccess.ResourceAccess.Add",{"type":254,"tag":622,"props":71892,"children":71893},{"style":629},[71894],{"type":259,"value":69302},{"type":254,"tag":622,"props":71896,"children":71897},{"style":1202},[71898],{"type":259,"value":71899},"resourceAccess",{"type":254,"tag":622,"props":71901,"children":71902},{"style":629},[71903],{"type":259,"value":1685},{"type":254,"tag":622,"props":71905,"children":71906},{"class":624,"line":4183},[71907],{"type":254,"tag":622,"props":71908,"children":71909},{"style":629},[71910],{"type":259,"value":63073},{"type":254,"tag":622,"props":71912,"children":71913},{"class":624,"line":4208},[71914],{"type":254,"tag":622,"props":71915,"children":71916},{"style":629},[71917],{"type":259,"value":57346},{"type":254,"tag":622,"props":71919,"children":71920},{"class":624,"line":4216},[71921],{"type":254,"tag":622,"props":71922,"children":71923},{"style":629},[71924],{"type":259,"value":7144},{"type":254,"tag":622,"props":71926,"children":71927},{"class":624,"line":4224},[71928],{"type":254,"tag":622,"props":71929,"children":71930},{"style":629},[71931],{"type":259,"value":1111},{"type":254,"tag":282,"props":71933,"children":71935},{"id":71934},"using-the-script-in-an-azure-pipeline",[71936],{"type":259,"value":71937},"Using the script in an Azure Pipeline",{"type":254,"tag":255,"props":71939,"children":71940},{},[71941,71943,71950],{"type":259,"value":71942},"To execute this script in the Azure pipeline that deploys and configures the rest of the application infrastructure we can use an ",{"type":254,"tag":262,"props":71944,"children":71947},{"href":71945,"rel":71946},"https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/deploy/azure-powershell?view=azure-devops",[266],[71948],{"type":259,"value":71949},"Azure PowerShell task",{"type":259,"value":594},{"type":254,"tag":255,"props":71952,"children":71953},{},[71954],{"type":259,"value":71955},"The task of the Azure Pipeline will look like this:",{"type":254,"tag":612,"props":71957,"children":71959},{"className":16381,"code":71958,"language":230,"meta":205,"style":205},"- task: AzurePowerShell@5\n  displayName: 'Configure Teams tab SSO'\n  inputs:\n    azureSubscription: 'My Azure Service Connection'\n    ScriptType: 'FilePath'\n    ScriptPath: 'infra/AdditionalScripts/ConfigureTeamsTabSSO.ps1'\n    ScriptArguments: \n      -applicationObjectId $(AzureAdObjectId) `\n      -customDomainName $(CustomDomainName)\n    azurePowerShellVersion: 'LatestVersion'\n",[71960],{"type":254,"tag":389,"props":71961,"children":71962},{"__ignoreMap":205},[71963,71983,72007,72018,72043,72068,72093,72109,72117,72125],{"type":254,"tag":622,"props":71964,"children":71965},{"class":624,"line":625},[71966,71970,71974,71978],{"type":254,"tag":622,"props":71967,"children":71968},{"style":629},[71969],{"type":259,"value":17065},{"type":254,"tag":622,"props":71971,"children":71972},{"style":768},[71973],{"type":259,"value":22604},{"type":254,"tag":622,"props":71975,"children":71976},{"style":629},[71977],{"type":259,"value":474},{"type":254,"tag":622,"props":71979,"children":71980},{"style":664},[71981],{"type":259,"value":71982}," AzurePowerShell@5\n",{"type":254,"tag":622,"props":71984,"children":71985},{"class":624,"line":635},[71986,71990,71994,71998,72003],{"type":254,"tag":622,"props":71987,"children":71988},{"style":768},[71989],{"type":259,"value":25132},{"type":254,"tag":622,"props":71991,"children":71992},{"style":629},[71993],{"type":259,"value":474},{"type":254,"tag":622,"props":71995,"children":71996},{"style":629},[71997],{"type":259,"value":16556},{"type":254,"tag":622,"props":71999,"children":72000},{"style":664},[72001],{"type":259,"value":72002},"Configure Teams tab SSO",{"type":254,"tag":622,"props":72004,"children":72005},{"style":629},[72006],{"type":259,"value":16566},{"type":254,"tag":622,"props":72008,"children":72009},{"class":624,"line":679},[72010,72014],{"type":254,"tag":622,"props":72011,"children":72012},{"style":768},[72013],{"type":259,"value":65518},{"type":254,"tag":622,"props":72015,"children":72016},{"style":629},[72017],{"type":259,"value":16416},{"type":254,"tag":622,"props":72019,"children":72020},{"class":624,"line":705},[72021,72026,72030,72034,72039],{"type":254,"tag":622,"props":72022,"children":72023},{"style":768},[72024],{"type":259,"value":72025},"    azureSubscription",{"type":254,"tag":622,"props":72027,"children":72028},{"style":629},[72029],{"type":259,"value":474},{"type":254,"tag":622,"props":72031,"children":72032},{"style":629},[72033],{"type":259,"value":16556},{"type":254,"tag":622,"props":72035,"children":72036},{"style":664},[72037],{"type":259,"value":72038},"My Azure Service Connection",{"type":254,"tag":622,"props":72040,"children":72041},{"style":629},[72042],{"type":259,"value":16566},{"type":254,"tag":622,"props":72044,"children":72045},{"class":624,"line":732},[72046,72051,72055,72059,72064],{"type":254,"tag":622,"props":72047,"children":72048},{"style":768},[72049],{"type":259,"value":72050},"    ScriptType",{"type":254,"tag":622,"props":72052,"children":72053},{"style":629},[72054],{"type":259,"value":474},{"type":254,"tag":622,"props":72056,"children":72057},{"style":629},[72058],{"type":259,"value":16556},{"type":254,"tag":622,"props":72060,"children":72061},{"style":664},[72062],{"type":259,"value":72063},"FilePath",{"type":254,"tag":622,"props":72065,"children":72066},{"style":629},[72067],{"type":259,"value":16566},{"type":254,"tag":622,"props":72069,"children":72070},{"class":624,"line":759},[72071,72076,72080,72084,72089],{"type":254,"tag":622,"props":72072,"children":72073},{"style":768},[72074],{"type":259,"value":72075},"    ScriptPath",{"type":254,"tag":622,"props":72077,"children":72078},{"style":629},[72079],{"type":259,"value":474},{"type":254,"tag":622,"props":72081,"children":72082},{"style":629},[72083],{"type":259,"value":16556},{"type":254,"tag":622,"props":72085,"children":72086},{"style":664},[72087],{"type":259,"value":72088},"infra/AdditionalScripts/ConfigureTeamsTabSSO.ps1",{"type":254,"tag":622,"props":72090,"children":72091},{"style":629},[72092],{"type":259,"value":16566},{"type":254,"tag":622,"props":72094,"children":72095},{"class":624,"line":798},[72096,72101,72105],{"type":254,"tag":622,"props":72097,"children":72098},{"style":768},[72099],{"type":259,"value":72100},"    ScriptArguments",{"type":254,"tag":622,"props":72102,"children":72103},{"style":629},[72104],{"type":259,"value":474},{"type":254,"tag":622,"props":72106,"children":72107},{"style":1202},[72108],{"type":259,"value":2314},{"type":254,"tag":622,"props":72110,"children":72111},{"class":624,"line":836},[72112],{"type":254,"tag":622,"props":72113,"children":72114},{"style":664},[72115],{"type":259,"value":72116},"      -applicationObjectId $(AzureAdObjectId) `\n",{"type":254,"tag":622,"props":72118,"children":72119},{"class":624,"line":862},[72120],{"type":254,"tag":622,"props":72121,"children":72122},{"style":664},[72123],{"type":259,"value":72124},"      -customDomainName $(CustomDomainName)\n",{"type":254,"tag":622,"props":72126,"children":72127},{"class":624,"line":900},[72128,72133,72137,72141,72146],{"type":254,"tag":622,"props":72129,"children":72130},{"style":768},[72131],{"type":259,"value":72132},"    azurePowerShellVersion",{"type":254,"tag":622,"props":72134,"children":72135},{"style":629},[72136],{"type":259,"value":474},{"type":254,"tag":622,"props":72138,"children":72139},{"style":629},[72140],{"type":259,"value":16556},{"type":254,"tag":622,"props":72142,"children":72143},{"style":664},[72144],{"type":259,"value":72145},"LatestVersion",{"type":254,"tag":622,"props":72147,"children":72148},{"style":629},[72149],{"type":259,"value":16566},{"type":254,"tag":255,"props":72151,"children":72152},{},[72153,72155,72160,72162,72169],{"type":259,"value":72154},"The advantage is that this task will connect to Azure with an Azure Service Connection that has enough rights to execute the Azure AD commands in this script. However, it involves passing to the ",{"type":254,"tag":389,"props":72156,"children":72158},{"className":72157},[],[72159],{"type":259,"value":68869},{"type":259,"value":72161}," command the access token of the Service Principal associated with the Azure Service Connection. This can easily be done as I found out in ",{"type":254,"tag":262,"props":72163,"children":72166},{"href":72164,"rel":72165},"https://stackoverflow.com/questions/60185213/automate-connect-azuread-using-powershell-in-azure-devops",[266],[72167],{"type":259,"value":72168},"a StackOverflow post",{"type":259,"value":594},{"type":254,"tag":612,"props":72171,"children":72173},{"className":10656,"code":72172,"language":10658,"meta":205,"style":205},"$context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext\n$graphToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, \"https://graph.microsoft.com\").AccessToken\n$aadToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, \"https://graph.windows.net\").AccessToken\nConnect-AzureAD -AadAccessToken $aadToken -MsAccessToken $graphToken -AccountId $context.Account.Id -TenantId $context.tenant.id\n",[72174],{"type":254,"tag":389,"props":72175,"children":72176},{"__ignoreMap":205},[72177,72211,72337,72454],{"type":254,"tag":622,"props":72178,"children":72179},{"class":624,"line":625},[72180,72184,72189,72193,72197,72202,72206],{"type":254,"tag":622,"props":72181,"children":72182},{"style":629},[72183],{"type":259,"value":16922},{"type":254,"tag":622,"props":72185,"children":72186},{"style":1202},[72187],{"type":259,"value":72188},"context ",{"type":254,"tag":622,"props":72190,"children":72191},{"style":629},[72192],{"type":259,"value":1654},{"type":254,"tag":622,"props":72194,"children":72195},{"style":629},[72196],{"type":259,"value":13676},{"type":254,"tag":622,"props":72198,"children":72199},{"style":644},[72200],{"type":259,"value":72201},"Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider",{"type":254,"tag":622,"props":72203,"children":72204},{"style":629},[72205],{"type":259,"value":20251},{"type":254,"tag":622,"props":72207,"children":72208},{"style":1202},[72209],{"type":259,"value":72210},"::Instance.Profile.DefaultContext\n",{"type":254,"tag":622,"props":72212,"children":72213},{"class":624,"line":635},[72214,72218,72223,72227,72231,72236,72240,72245,72249,72254,72258,72262,72267,72271,72275,72280,72284,72289,72293,72298,72302,72307,72311,72315,72319,72324,72328,72332],{"type":254,"tag":622,"props":72215,"children":72216},{"style":629},[72217],{"type":259,"value":16922},{"type":254,"tag":622,"props":72219,"children":72220},{"style":1202},[72221],{"type":259,"value":72222},"graphToken ",{"type":254,"tag":622,"props":72224,"children":72225},{"style":629},[72226],{"type":259,"value":1654},{"type":254,"tag":622,"props":72228,"children":72229},{"style":629},[72230],{"type":259,"value":13676},{"type":254,"tag":622,"props":72232,"children":72233},{"style":644},[72234],{"type":259,"value":72235},"Microsoft.Azure.Commands.Common.Authentication.AzureSession",{"type":254,"tag":622,"props":72237,"children":72238},{"style":629},[72239],{"type":259,"value":20251},{"type":254,"tag":622,"props":72241,"children":72242},{"style":1202},[72243],{"type":259,"value":72244},"::Instance.AuthenticationFactory.Authenticate",{"type":254,"tag":622,"props":72246,"children":72247},{"style":629},[72248],{"type":259,"value":69302},{"type":254,"tag":622,"props":72250,"children":72251},{"style":1202},[72252],{"type":259,"value":72253},"context.Account",{"type":254,"tag":622,"props":72255,"children":72256},{"style":629},[72257],{"type":259,"value":3149},{"type":254,"tag":622,"props":72259,"children":72260},{"style":629},[72261],{"type":259,"value":39174},{"type":254,"tag":622,"props":72263,"children":72264},{"style":1202},[72265],{"type":259,"value":72266},"context.Environment",{"type":254,"tag":622,"props":72268,"children":72269},{"style":629},[72270],{"type":259,"value":3149},{"type":254,"tag":622,"props":72272,"children":72273},{"style":629},[72274],{"type":259,"value":39174},{"type":254,"tag":622,"props":72276,"children":72277},{"style":1202},[72278],{"type":259,"value":72279},"context.Tenant.Id.ToString",{"type":254,"tag":622,"props":72281,"children":72282},{"style":629},[72283],{"type":259,"value":2290},{"type":254,"tag":622,"props":72285,"children":72286},{"style":629},[72287],{"type":259,"value":72288}," $null,",{"type":254,"tag":622,"props":72290,"children":72291},{"style":629},[72292],{"type":259,"value":13676},{"type":254,"tag":622,"props":72294,"children":72295},{"style":644},[72296],{"type":259,"value":72297},"Microsoft.Azure.Commands.Common.Authentication.ShowDialog",{"type":254,"tag":622,"props":72299,"children":72300},{"style":629},[72301],{"type":259,"value":20251},{"type":254,"tag":622,"props":72303,"children":72304},{"style":1202},[72305],{"type":259,"value":72306},"::Never",{"type":254,"tag":622,"props":72308,"children":72309},{"style":629},[72310],{"type":259,"value":3149},{"type":254,"tag":622,"props":72312,"children":72313},{"style":629},[72314],{"type":259,"value":72288},{"type":254,"tag":622,"props":72316,"children":72317},{"style":629},[72318],{"type":259,"value":661},{"type":254,"tag":622,"props":72320,"children":72321},{"style":664},[72322],{"type":259,"value":72323},"https://graph.microsoft.com",{"type":254,"tag":622,"props":72325,"children":72326},{"style":629},[72327],{"type":259,"value":652},{"type":254,"tag":622,"props":72329,"children":72330},{"style":629},[72331],{"type":259,"value":2309},{"type":254,"tag":622,"props":72333,"children":72334},{"style":1202},[72335],{"type":259,"value":72336},".AccessToken\n",{"type":254,"tag":622,"props":72338,"children":72339},{"class":624,"line":679},[72340,72344,72349,72353,72357,72361,72365,72369,72373,72377,72381,72385,72389,72393,72397,72401,72405,72409,72413,72417,72421,72425,72429,72433,72437,72442,72446,72450],{"type":254,"tag":622,"props":72341,"children":72342},{"style":629},[72343],{"type":259,"value":16922},{"type":254,"tag":622,"props":72345,"children":72346},{"style":1202},[72347],{"type":259,"value":72348},"aadToken ",{"type":254,"tag":622,"props":72350,"children":72351},{"style":629},[72352],{"type":259,"value":1654},{"type":254,"tag":622,"props":72354,"children":72355},{"style":629},[72356],{"type":259,"value":13676},{"type":254,"tag":622,"props":72358,"children":72359},{"style":644},[72360],{"type":259,"value":72235},{"type":254,"tag":622,"props":72362,"children":72363},{"style":629},[72364],{"type":259,"value":20251},{"type":254,"tag":622,"props":72366,"children":72367},{"style":1202},[72368],{"type":259,"value":72244},{"type":254,"tag":622,"props":72370,"children":72371},{"style":629},[72372],{"type":259,"value":69302},{"type":254,"tag":622,"props":72374,"children":72375},{"style":1202},[72376],{"type":259,"value":72253},{"type":254,"tag":622,"props":72378,"children":72379},{"style":629},[72380],{"type":259,"value":3149},{"type":254,"tag":622,"props":72382,"children":72383},{"style":629},[72384],{"type":259,"value":39174},{"type":254,"tag":622,"props":72386,"children":72387},{"style":1202},[72388],{"type":259,"value":72266},{"type":254,"tag":622,"props":72390,"children":72391},{"style":629},[72392],{"type":259,"value":3149},{"type":254,"tag":622,"props":72394,"children":72395},{"style":629},[72396],{"type":259,"value":39174},{"type":254,"tag":622,"props":72398,"children":72399},{"style":1202},[72400],{"type":259,"value":72279},{"type":254,"tag":622,"props":72402,"children":72403},{"style":629},[72404],{"type":259,"value":2290},{"type":254,"tag":622,"props":72406,"children":72407},{"style":629},[72408],{"type":259,"value":72288},{"type":254,"tag":622,"props":72410,"children":72411},{"style":629},[72412],{"type":259,"value":13676},{"type":254,"tag":622,"props":72414,"children":72415},{"style":644},[72416],{"type":259,"value":72297},{"type":254,"tag":622,"props":72418,"children":72419},{"style":629},[72420],{"type":259,"value":20251},{"type":254,"tag":622,"props":72422,"children":72423},{"style":1202},[72424],{"type":259,"value":72306},{"type":254,"tag":622,"props":72426,"children":72427},{"style":629},[72428],{"type":259,"value":3149},{"type":254,"tag":622,"props":72430,"children":72431},{"style":629},[72432],{"type":259,"value":72288},{"type":254,"tag":622,"props":72434,"children":72435},{"style":629},[72436],{"type":259,"value":661},{"type":254,"tag":622,"props":72438,"children":72439},{"style":664},[72440],{"type":259,"value":72441},"https://graph.windows.net",{"type":254,"tag":622,"props":72443,"children":72444},{"style":629},[72445],{"type":259,"value":652},{"type":254,"tag":622,"props":72447,"children":72448},{"style":629},[72449],{"type":259,"value":2309},{"type":254,"tag":622,"props":72451,"children":72452},{"style":1202},[72453],{"type":259,"value":72336},{"type":254,"tag":622,"props":72455,"children":72456},{"class":624,"line":705},[72457,72461,72465,72470,72474,72478,72482,72487,72491,72495,72499,72504,72508,72513,72517,72521,72525],{"type":254,"tag":622,"props":72458,"children":72459},{"style":1735},[72460],{"type":259,"value":68869},{"type":254,"tag":622,"props":72462,"children":72463},{"style":629},[72464],{"type":259,"value":10733},{"type":254,"tag":622,"props":72466,"children":72467},{"style":1202},[72468],{"type":259,"value":72469},"AadAccessToken ",{"type":254,"tag":622,"props":72471,"children":72472},{"style":629},[72473],{"type":259,"value":16922},{"type":254,"tag":622,"props":72475,"children":72476},{"style":1202},[72477],{"type":259,"value":72348},{"type":254,"tag":622,"props":72479,"children":72480},{"style":629},[72481],{"type":259,"value":17065},{"type":254,"tag":622,"props":72483,"children":72484},{"style":1202},[72485],{"type":259,"value":72486},"MsAccessToken ",{"type":254,"tag":622,"props":72488,"children":72489},{"style":629},[72490],{"type":259,"value":16922},{"type":254,"tag":622,"props":72492,"children":72493},{"style":1202},[72494],{"type":259,"value":72222},{"type":254,"tag":622,"props":72496,"children":72497},{"style":629},[72498],{"type":259,"value":17065},{"type":254,"tag":622,"props":72500,"children":72501},{"style":1202},[72502],{"type":259,"value":72503},"AccountId ",{"type":254,"tag":622,"props":72505,"children":72506},{"style":629},[72507],{"type":259,"value":16922},{"type":254,"tag":622,"props":72509,"children":72510},{"style":1202},[72511],{"type":259,"value":72512},"context.Account.Id ",{"type":254,"tag":622,"props":72514,"children":72515},{"style":629},[72516],{"type":259,"value":17065},{"type":254,"tag":622,"props":72518,"children":72519},{"style":1202},[72520],{"type":259,"value":68878},{"type":254,"tag":622,"props":72522,"children":72523},{"style":629},[72524],{"type":259,"value":16922},{"type":254,"tag":622,"props":72526,"children":72527},{"style":1202},[72528],{"type":259,"value":72529},"context.tenant.id\n",{"type":254,"tag":455,"props":72531,"children":72532},{"id":43649},[72533],{"type":259,"value":43652},{"type":254,"tag":255,"props":72535,"children":72536},{},[72537,72539,72544],{"type":259,"value":72538},"In this post, I wanted to show the different steps to configure Teams Tab SSO in PowerShell. The final script can be found ",{"type":254,"tag":262,"props":72540,"children":72542},{"href":68626,"rel":72541},[266],[72543],{"type":259,"value":1414},{"type":259,"value":72545}," and is directly used in an Azure pipeline to automate this configuration. Although it does the job, I hope doing such Azure AD configurations will be supported soon in Pulumi as it would have been easier to set it up instead of coming up with a big PowerShell script like this which is not idempotent.",{"type":254,"tag":1260,"props":72547,"children":72548},{},[72549],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":72551},[72552,72553,72562],{"id":284,"depth":635,"text":287},{"id":68663,"depth":635,"text":68666,"children":72554},[72555,72556,72557,72558,72559,72560,72561],{"id":68697,"depth":679,"text":68700},{"id":68894,"depth":679,"text":68897},{"id":68994,"depth":679,"text":68997},{"id":69068,"depth":679,"text":69071},{"id":69186,"depth":679,"text":69189},{"id":70024,"depth":679,"text":70027},{"id":70621,"depth":679,"text":70624},{"id":71934,"depth":635,"text":71937,"children":72563},[72564],{"id":43649,"depth":679,"text":43652},"content:1.posts:7.teams-sso-powershell.md","1.posts/7.teams-sso-powershell.md",{"_path":25,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":24,"description":72568,"lead":72569,"date":72570,"image":72571,"badge":72573,"tags":72574,"body":72576,"_type":1275,"_id":73140,"_source":231,"_file":73141,"_extension":1278},"When working on a git repository, I often have to manually delete old local branches that I don't use anymore. That's not a huge waste of time but still, that's something I have to do quite often so I decided to automate that.","Playing with Nushell to create a useful git alias to delete unused local git branches.","2020-04-06T00:00:00.000Z",{"src":72572},"/images/branches_1.jpg",{"label":9260},[245,16850,72575,40203],"shell",{"type":251,"children":72577,"toc":73134},[72578,72582,72588,72593,72605,72636,72641,72654,72660,72679,72684,72694,72715,72724,72737,72767,72773,72793,72802,72823,72832,72843,72852,72857,73027,73033,73052,73082,73103,73116,73125,73130],{"type":254,"tag":255,"props":72579,"children":72580},{},[72581],{"type":259,"value":72568},{"type":254,"tag":282,"props":72583,"children":72585},{"id":72584},"why-do-i-end-up-having-outdated-local-branches-on-my-git-repositories",[72586],{"type":259,"value":72587},"Why do I end up having outdated local branches on my git repositories?",{"type":254,"tag":255,"props":72589,"children":72590},{},[72591],{"type":259,"value":72592},"First, let's talk about how I end up having many useless local git branches. That's something quite usual and directly linked with the way I work with git but chances are that you are having the same issue.",{"type":254,"tag":255,"props":72594,"children":72595},{},[72596,72598,72603],{"type":259,"value":72597},"At work I am working in a small team of developers, we host our git repositories in ",{"type":254,"tag":262,"props":72599,"children":72601},{"href":51150,"rel":72600},[266],[72602],{"type":259,"value":9264},{"type":259,"value":72604}," and we try to respect the following practices in our daily development:",{"type":254,"tag":332,"props":72606,"children":72607},{},[72608,72613,72618],{"type":254,"tag":336,"props":72609,"children":72610},{},[72611],{"type":259,"value":72612},"having a main branch (master) on which nobody can commit directly",{"type":254,"tag":336,"props":72614,"children":72615},{},[72616],{"type":259,"value":72617},"always create a short-lived branch (also called feature branch) when developing a new feature of the application",{"type":254,"tag":336,"props":72619,"children":72620},{},[72621,72623],{"type":259,"value":72622},"only merge a feature branch on the main branch through an Azure DevOps pull request\n",{"type":254,"tag":332,"props":72624,"children":72625},{},[72626,72631],{"type":254,"tag":336,"props":72627,"children":72628},{},[72629],{"type":259,"value":72630},"the PR triggers a pipeline that ensures the code build correctly, follow some conventions (with a Sonar analysis for instance) and that unit tests pass",{"type":254,"tag":336,"props":72632,"children":72633},{},[72634],{"type":259,"value":72635},"the PR can only be completed after a code review of at least one member of the team",{"type":254,"tag":255,"props":72637,"children":72638},{},[72639],{"type":259,"value":72640},"These practices allow us to keep good quality in our code base, not to mess with our git repositories, and ensure the main branch always builds.",{"type":254,"tag":255,"props":72642,"children":72643},{},[72644,72646,72652],{"type":259,"value":72645},"However, each week we are creating a lot of branches that need to be deleted as once merged we no longer need to have them. When a pull request is approved and we decide to complete it, Azure DevOps takes care of automatically merging the associated feature branch into master and deleting it from the repository. Once that's done, I can do a ",{"type":254,"tag":389,"props":72647,"children":72649},{"className":72648},[],[72650],{"type":259,"value":72651},"git fetch --prune",{"type":259,"value":72653}," on my laptop to have the feature branch removed from the remote of my local repository (by the way, I recommend you to directly set the fetch command to prune by default in your git config 👌). Nevertheless, this does not delete the local version of the feature branch thus our problem: over time if we do not think of deleting all these outdated branches, they become too many and we don't even know which branch should be kept or not.",{"type":254,"tag":282,"props":72655,"children":72657},{"id":72656},"git-commands-to-identify-and-delete-outdated-branches",[72658],{"type":259,"value":72659},"Git commands to identify and delete outdated branches.",{"type":254,"tag":255,"props":72661,"children":72662},{},[72663,72665,72670,72672,72678],{"type":259,"value":72664},"As my outdated branches are already removed from my remote (thanks to ",{"type":254,"tag":389,"props":72666,"children":72668},{"className":72667},[],[72669],{"type":259,"value":72651},{"type":259,"value":72671},") it should not be too complicated to use some git commands to guess which branches are not useful anymore. But as it's Azure DevOps that took care of merging them (sometimes with a squash) I cannot use the ",{"type":254,"tag":389,"props":72673,"children":72675},{"className":72674},[],[72676],{"type":259,"value":72677},"git branch --merged",{"type":259,"value":32093},{"type":254,"tag":255,"props":72680,"children":72681},{},[72682],{"type":259,"value":72683},"If I take my blog repository as an example I have a bunch of branches: some that could be useful (articles I have started to write but did not finish yet and I don't know if I will one day 😋) and some that are already merged into my master branch through a PR.",{"type":254,"tag":255,"props":72685,"children":72686},{},[72687],{"type":254,"tag":1161,"props":72688,"children":72693},{"alt":72689,"className":72690,"src":72691,"width":72692},"List all git branches in the terminal.",[1165,1166],"/posts/images/cleaningbranches_shell_1.png",800,[],{"type":254,"tag":255,"props":72695,"children":72696},{},[72697,72699,72705,72707,72713],{"type":259,"value":72698},"The command ",{"type":254,"tag":389,"props":72700,"children":72702},{"className":72701},[],[72703],{"type":259,"value":72704},"git branch -vl",{"type":259,"value":72706}," (which lists in a verbose way the local git branches) gives us an interesting view as it shows the branches for which the remote has been deleted specifying a ",{"type":254,"tag":389,"props":72708,"children":72710},{"className":72709},[],[72711],{"type":259,"value":72712},"[gone]",{"type":259,"value":72714}," for them. These branches correspond to the outdated branches we want to delete.",{"type":254,"tag":255,"props":72716,"children":72717},{},[72718],{"type":254,"tag":1161,"props":72719,"children":72723},{"alt":72720,"className":72721,"src":72722,"width":19735},"List all git branches with verbose tag in terminal.",[1165,1166],"/posts/images/cleaningbranches_shell_2.png",[],{"type":254,"tag":255,"props":72725,"children":72726},{},[72727,72729,72735],{"type":259,"value":72728},"We know how to identify the outdated branches but we need a command to delete them which is the ",{"type":254,"tag":389,"props":72730,"children":72732},{"className":72731},[],[72733],{"type":259,"value":72734},"git branch -D",{"type":259,"value":72736}," command. Now we only need a script to associate the output and input of these two commands to automate the deletion.",{"type":254,"tag":255,"props":72738,"children":72739},{},[72740,72742,72748,72750,72756,72758,72765],{"type":259,"value":72741},"You can find on Stackoverflow some posts like ",{"type":254,"tag":262,"props":72743,"children":72746},{"href":72744,"rel":72745},"https://stackoverflow.com/questions/7726949/remove-tracking-branches-no-longer-on-remote",[266],[72747],{"type":259,"value":67793},{"type":259,"value":72749}," that show different solutions using bash that work perfectly but I thought it would be interesting to try to script that using another shell. Indeed I recently started to use a shell called ",{"type":254,"tag":262,"props":72751,"children":72754},{"href":72752,"rel":72753},"https://github.com/nushell/nushell",[266],[72755],{"type":259,"value":40203},{"type":259,"value":72757}," which is a pretty powerful yet simple cross-platform shell. It is still in preview at the time of writing but if you have not heard of it I suggest you read the ",{"type":254,"tag":262,"props":72759,"children":72762},{"href":72760,"rel":72761},"https://www.jonathanturner.org/2019/08/introducing-nushell.html",[266],[72763],{"type":259,"value":72764},"introduction post",{"type":259,"value":72766}," of Jonathan Turner.",{"type":254,"tag":282,"props":72768,"children":72770},{"id":72769},"lets-script-that-with-nushell",[72771],{"type":259,"value":72772},"Let's script that with nushell!",{"type":254,"tag":255,"props":72774,"children":72775},{},[72776,72778,72783,72785,72791],{"type":259,"value":72777},"Enough of talking, let's script.\nTo start with, we can use the nu lines command to create a table from the lines of the ",{"type":254,"tag":389,"props":72779,"children":72781},{"className":72780},[],[72782],{"type":259,"value":72704},{"type":259,"value":72784}," output (we added an extra ",{"type":254,"tag":389,"props":72786,"children":72788},{"className":72787},[],[72789],{"type":259,"value":72790},"*/*",{"type":259,"value":72792}," argument as we are only interested in posts branches).",{"type":254,"tag":255,"props":72794,"children":72795},{},[72796],{"type":254,"tag":1161,"props":72797,"children":72801},{"alt":72798,"className":72799,"src":72800,"width":19735},"List git branches in table in the terminal.",[1165,1166],"/posts/images/cleaningbranches_shell_3.png",[],{"type":254,"tag":255,"props":72803,"children":72804},{},[72805,72807,72813,72815,72821],{"type":259,"value":72806},"Then we can split the different lines into columns that we can name with the ",{"type":254,"tag":389,"props":72808,"children":72810},{"className":72809},[],[72811],{"type":259,"value":72812},"split column",{"type":259,"value":72814}," command. We use spaces to correctly split a line and the option ",{"type":254,"tag":389,"props":72816,"children":72818},{"className":72817},[],[72819],{"type":259,"value":72820},"--collapse-empty",{"type":259,"value":72822}," to remove the empty columns.",{"type":254,"tag":255,"props":72824,"children":72825},{},[72826],{"type":254,"tag":1161,"props":72827,"children":72831},{"alt":72828,"className":72829,"src":72830,"width":19735},"List git branches in table with columns by property in the terminal.",[1165,1166],"/posts/images/cleaningbranches_shell_4.png",[],{"type":254,"tag":255,"props":72833,"children":72834},{},[72835,72837,72842],{"type":259,"value":72836},"We then just have to filter the table to get only the lines with the Status ",{"type":254,"tag":389,"props":72838,"children":72840},{"className":72839},[],[72841],{"type":259,"value":72712},{"type":259,"value":594},{"type":254,"tag":255,"props":72844,"children":72845},{},[72846],{"type":254,"tag":1161,"props":72847,"children":72851},{"alt":72848,"className":72849,"src":72850,"width":19735},"Filter on git branches gone in the terminal.",[1165,1166],"/posts/images/cleaningbranches_shell_5.png",[],{"type":254,"tag":255,"props":72853,"children":72854},{},[72855],{"type":259,"value":72856},"And the final script:",{"type":254,"tag":612,"props":72858,"children":72860},{"className":16836,"code":72859,"language":16838,"meta":205,"style":205},"git branch -vl '*/*' | lines | split column \" \" BranchName Hash Status --collapse-empty | where Status == '[gone]' | each { |it| git branch -D $it.BranchName }\n",[72861],{"type":254,"tag":389,"props":72862,"children":72863},{"__ignoreMap":205},[72864],{"type":254,"tag":622,"props":72865,"children":72866},{"class":624,"line":625},[72867,72871,72876,72881,72885,72889,72893,72897,72902,72906,72911,72916,72920,72924,72929,72934,72939,72944,72948,72953,72957,72962,72966,72970,72974,72978,72983,72987,72991,72995,72999,73004,73008,73013,73018,73023],{"type":254,"tag":622,"props":72868,"children":72869},{"style":714},[72870],{"type":259,"value":16850},{"type":254,"tag":622,"props":72872,"children":72873},{"style":664},[72874],{"type":259,"value":72875}," branch",{"type":254,"tag":622,"props":72877,"children":72878},{"style":664},[72879],{"type":259,"value":72880}," -vl",{"type":254,"tag":622,"props":72882,"children":72883},{"style":629},[72884],{"type":259,"value":16556},{"type":254,"tag":622,"props":72886,"children":72887},{"style":664},[72888],{"type":259,"value":72790},{"type":254,"tag":622,"props":72890,"children":72891},{"style":629},[72892],{"type":259,"value":46169},{"type":254,"tag":622,"props":72894,"children":72895},{"style":629},[72896],{"type":259,"value":41040},{"type":254,"tag":622,"props":72898,"children":72899},{"style":714},[72900],{"type":259,"value":72901}," lines",{"type":254,"tag":622,"props":72903,"children":72904},{"style":629},[72905],{"type":259,"value":41040},{"type":254,"tag":622,"props":72907,"children":72908},{"style":714},[72909],{"type":259,"value":72910}," split",{"type":254,"tag":622,"props":72912,"children":72913},{"style":664},[72914],{"type":259,"value":72915}," column",{"type":254,"tag":622,"props":72917,"children":72918},{"style":629},[72919],{"type":259,"value":661},{"type":254,"tag":622,"props":72921,"children":72922},{"style":629},[72923],{"type":259,"value":661},{"type":254,"tag":622,"props":72925,"children":72926},{"style":664},[72927],{"type":259,"value":72928}," BranchName",{"type":254,"tag":622,"props":72930,"children":72931},{"style":664},[72932],{"type":259,"value":72933}," Hash",{"type":254,"tag":622,"props":72935,"children":72936},{"style":664},[72937],{"type":259,"value":72938}," Status",{"type":254,"tag":622,"props":72940,"children":72941},{"style":664},[72942],{"type":259,"value":72943}," --collapse-empty",{"type":254,"tag":622,"props":72945,"children":72946},{"style":629},[72947],{"type":259,"value":41040},{"type":254,"tag":622,"props":72949,"children":72950},{"style":714},[72951],{"type":259,"value":72952}," where",{"type":254,"tag":622,"props":72954,"children":72955},{"style":664},[72956],{"type":259,"value":72938},{"type":254,"tag":622,"props":72958,"children":72959},{"style":664},[72960],{"type":259,"value":72961}," ==",{"type":254,"tag":622,"props":72963,"children":72964},{"style":629},[72965],{"type":259,"value":16556},{"type":254,"tag":622,"props":72967,"children":72968},{"style":664},[72969],{"type":259,"value":72712},{"type":254,"tag":622,"props":72971,"children":72972},{"style":629},[72973],{"type":259,"value":46169},{"type":254,"tag":622,"props":72975,"children":72976},{"style":629},[72977],{"type":259,"value":41040},{"type":254,"tag":622,"props":72979,"children":72980},{"style":714},[72981],{"type":259,"value":72982}," each",{"type":254,"tag":622,"props":72984,"children":72985},{"style":664},[72986],{"type":259,"value":1990},{"type":254,"tag":622,"props":72988,"children":72989},{"style":629},[72990],{"type":259,"value":41040},{"type":254,"tag":622,"props":72992,"children":72993},{"style":714},[72994],{"type":259,"value":50887},{"type":254,"tag":622,"props":72996,"children":72997},{"style":629},[72998],{"type":259,"value":17875},{"type":254,"tag":622,"props":73000,"children":73001},{"style":714},[73002],{"type":259,"value":73003}," git",{"type":254,"tag":622,"props":73005,"children":73006},{"style":664},[73007],{"type":259,"value":72875},{"type":254,"tag":622,"props":73009,"children":73010},{"style":664},[73011],{"type":259,"value":73012}," -D",{"type":254,"tag":622,"props":73014,"children":73015},{"style":1202},[73016],{"type":259,"value":73017}," $it",{"type":254,"tag":622,"props":73019,"children":73020},{"style":664},[73021],{"type":259,"value":73022},".BranchName",{"type":254,"tag":622,"props":73024,"children":73025},{"style":664},[73026],{"type":259,"value":2014},{"type":254,"tag":282,"props":73028,"children":73030},{"id":73029},"make-it-a-git-alias",[73031],{"type":259,"value":73032},"Make it a git alias.",{"type":254,"tag":255,"props":73034,"children":73035},{},[73036,73038,73044,73046,73051],{"type":259,"value":73037},"We can integrate this script into our git commands by creating a git alias. Let's say I want to create the alias ",{"type":254,"tag":389,"props":73039,"children":73041},{"className":73040},[],[73042],{"type":259,"value":73043},"bcl",{"type":259,"value":73045}," for branch clean up, we only need to add the following to our ",{"type":254,"tag":389,"props":73047,"children":73049},{"className":73048},[],[73050],{"type":259,"value":38304},{"type":259,"value":474},{"type":254,"tag":612,"props":73053,"children":73055},{"className":16381,"code":73054,"language":230,"meta":205,"style":205},"[alias]\n    bcl = !nu \\\"D:\\\\gitalias_bcl.nu\\\"\n",[73056],{"type":254,"tag":389,"props":73057,"children":73058},{"__ignoreMap":205},[73059,73074],{"type":254,"tag":622,"props":73060,"children":73061},{"class":624,"line":625},[73062,73066,73070],{"type":254,"tag":622,"props":73063,"children":73064},{"style":629},[73065],{"type":259,"value":10993},{"type":254,"tag":622,"props":73067,"children":73068},{"style":664},[73069],{"type":259,"value":42323},{"type":254,"tag":622,"props":73071,"children":73072},{"style":629},[73073],{"type":259,"value":11003},{"type":254,"tag":622,"props":73075,"children":73076},{"class":624,"line":635},[73077],{"type":254,"tag":622,"props":73078,"children":73079},{"style":664},[73080],{"type":259,"value":73081},"    bcl = !nu \\\"D:\\\\gitalias_bcl.nu\\\"\n",{"type":254,"tag":255,"props":73083,"children":73084},{},[73085,73087,73093,73095,73101],{"type":259,"value":73086},"where ",{"type":254,"tag":389,"props":73088,"children":73090},{"className":73089},[],[73091],{"type":259,"value":73092},"gitalias_bcl.nu",{"type":259,"value":73094}," is the nu script file we created earlier (it's located here in the ",{"type":254,"tag":389,"props":73096,"children":73098},{"className":73097},[],[73099],{"type":259,"value":73100},"D://",{"type":259,"value":73102}," drive but can be created anywhere).",{"type":254,"tag":255,"props":73104,"children":73105},{},[73106,73108,73114],{"type":259,"value":73107},"Now we can simply do a ",{"type":254,"tag":389,"props":73109,"children":73111},{"className":73110},[],[73112],{"type":259,"value":73113},"git bcl",{"type":259,"value":73115}," to clean our outdated local git branches.",{"type":254,"tag":255,"props":73117,"children":73118},{},[73119],{"type":254,"tag":1161,"props":73120,"children":73124},{"alt":73121,"className":73122,"src":73123,"width":72692},"List oudated git branches  in the terminal.",[1165,1166],"/posts/images/cleaningbranches_shell_6.png",[],{"type":254,"tag":255,"props":73126,"children":73127},{},[73128],{"type":259,"value":73129},"That's it, nothing revolutionary but that was the opportunity to automate the boring task of deleting outdated local branches while playing with nushell.",{"type":254,"tag":1260,"props":73131,"children":73132},{},[73133],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":73135},[73136,73137,73138,73139],{"id":72584,"depth":635,"text":72587},{"id":72656,"depth":635,"text":72659},{"id":72769,"depth":635,"text":72772},{"id":73029,"depth":635,"text":73032},"content:1.posts:6.cleaning-git-branches.md","1.posts/6.cleaning-git-branches.md",{"_path":22,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":21,"description":73143,"lead":73144,"date":73145,"image":73146,"badge":73148,"tags":73149,"body":73150,"_type":1275,"_id":76965,"_source":231,"_file":76966,"_extension":1278},"In our daily job, we often have to query secure REST APIs that require our HTTP requests to have a valid access token in their Authorization header.\nOf course, many APIs come with an SDK that makes the job easier for us as it directly takes care of retrieving a token and sending the authenticated HTTP requests.\nHowever, it is not always the case and knowing how to implement that using HttpClient, IMemoryCache, and DelegatingHandler can become pretty useful.","Using a DelegatingHandler","2020-01-29T00:00:00.000Z",{"src":73147},"/images/keyboard_1.jpg",{"label":1286},[246,37463,10627,1288],{"type":251,"children":73151,"toc":76958},[73152,73156,73160,73165,73174,73208,73213,73251,73355,73367,73372,73600,73617,73736,73748,73754,73780,74880,74885,74920,74926,74967,75614,75634,75640,75679,75712,75724,76091,76096,76620,76633,76945,76949,76954],{"type":254,"tag":255,"props":73153,"children":73154},{},[73155],{"type":259,"value":73143},{"type":254,"tag":282,"props":73157,"children":73158},{"id":284},[73159],{"type":259,"value":287},{"type":254,"tag":255,"props":73161,"children":73162},{},[73163],{"type":259,"value":73164},"Let's imagine we have a very simple API that contains the following routes :",{"type":254,"tag":255,"props":73166,"children":73167},{},[73168],{"type":254,"tag":1161,"props":73169,"children":73173},{"alt":73170,"className":73171,"src":73172},"Open API specifications of the Users API",[1165,1166],"/posts/images/delegatinghandler_api_1.png",[],{"type":254,"tag":255,"props":73175,"children":73176},{},[73177,73178,73184,73186,73192,73194,73200,73201,73207],{"type":259,"value":10970},{"type":254,"tag":389,"props":73179,"children":73181},{"className":73180},[],[73182],{"type":259,"value":73183},"POST /login",{"type":259,"value":73185}," route returns an ",{"type":254,"tag":389,"props":73187,"children":73189},{"className":73188},[],[73190],{"type":259,"value":73191},"AuthResponse",{"type":259,"value":73193}," that contains the necessary Bearer token to call the 2 protected routes ",{"type":254,"tag":389,"props":73195,"children":73197},{"className":73196},[],[73198],{"type":259,"value":73199},"GET /users",{"type":259,"value":10347},{"type":254,"tag":389,"props":73202,"children":73204},{"className":73203},[],[73205],{"type":259,"value":73206},"PUT /users/{username}",{"type":259,"value":594},{"type":254,"tag":255,"props":73209,"children":73210},{},[73211],{"type":259,"value":73212},"We want to implement an IUserService that has 2 methods:",{"type":254,"tag":332,"props":73214,"children":73215},{},[73216,73234],{"type":254,"tag":336,"props":73217,"children":73218},{},[73219,73225,73227,73232],{"type":254,"tag":389,"props":73220,"children":73222},{"className":73221},[],[73223],{"type":259,"value":73224},"GetAllUsers",{"type":259,"value":73226}," to retrieve the list of users that will use the ",{"type":254,"tag":389,"props":73228,"children":73230},{"className":73229},[],[73231],{"type":259,"value":73199},{"type":259,"value":73233}," route",{"type":254,"tag":336,"props":73235,"children":73236},{},[73237,73243,73245,73250],{"type":254,"tag":389,"props":73238,"children":73240},{"className":73239},[],[73241],{"type":259,"value":73242},"UpdateUser",{"type":259,"value":73244}," to update a user that will use the ",{"type":254,"tag":389,"props":73246,"children":73248},{"className":73247},[],[73249],{"type":259,"value":73206},{"type":259,"value":73233},{"type":254,"tag":612,"props":73252,"children":73254},{"className":1423,"code":73253,"language":1425,"meta":205,"style":205},"public interface IUserService\n{\n    Task\u003CIReadOnlyCollection\u003CUser>> GetAllUsers();\n    Task UpdateUser(User userToUpdate);\n}\n",[73255],{"type":254,"tag":389,"props":73256,"children":73257},{"__ignoreMap":205},[73258,73274,73281,73319,73348],{"type":254,"tag":622,"props":73259,"children":73260},{"class":624,"line":625},[73261,73265,73269],{"type":254,"tag":622,"props":73262,"children":73263},{"style":644},[73264],{"type":259,"value":1437},{"type":254,"tag":622,"props":73266,"children":73267},{"style":714},[73268],{"type":259,"value":15084},{"type":254,"tag":622,"props":73270,"children":73271},{"style":714},[73272],{"type":259,"value":73273}," IUserService\n",{"type":254,"tag":622,"props":73275,"children":73276},{"class":624,"line":635},[73277],{"type":254,"tag":622,"props":73278,"children":73279},{"style":629},[73280],{"type":259,"value":632},{"type":254,"tag":622,"props":73282,"children":73283},{"class":624,"line":679},[73284,73288,73292,73297,73301,73305,73310,73315],{"type":254,"tag":622,"props":73285,"children":73286},{"style":714},[73287],{"type":259,"value":15137},{"type":254,"tag":622,"props":73289,"children":73290},{"style":629},[73291],{"type":259,"value":1743},{"type":254,"tag":622,"props":73293,"children":73294},{"style":714},[73295],{"type":259,"value":73296},"IReadOnlyCollection",{"type":254,"tag":622,"props":73298,"children":73299},{"style":629},[73300],{"type":259,"value":1743},{"type":254,"tag":622,"props":73302,"children":73303},{"style":714},[73304],{"type":259,"value":44521},{"type":254,"tag":622,"props":73306,"children":73307},{"style":629},[73308],{"type":259,"value":73309},">>",{"type":254,"tag":622,"props":73311,"children":73312},{"style":1735},[73313],{"type":259,"value":73314}," GetAllUsers",{"type":254,"tag":622,"props":73316,"children":73317},{"style":629},[73318],{"type":259,"value":2798},{"type":254,"tag":622,"props":73320,"children":73321},{"class":624,"line":705},[73322,73326,73331,73335,73339,73344],{"type":254,"tag":622,"props":73323,"children":73324},{"style":714},[73325],{"type":259,"value":15137},{"type":254,"tag":622,"props":73327,"children":73328},{"style":1735},[73329],{"type":259,"value":73330}," UpdateUser",{"type":254,"tag":622,"props":73332,"children":73333},{"style":629},[73334],{"type":259,"value":1668},{"type":254,"tag":622,"props":73336,"children":73337},{"style":714},[73338],{"type":259,"value":44521},{"type":254,"tag":622,"props":73340,"children":73341},{"style":714},[73342],{"type":259,"value":73343}," userToUpdate",{"type":254,"tag":622,"props":73345,"children":73346},{"style":629},[73347],{"type":259,"value":1554},{"type":254,"tag":622,"props":73349,"children":73350},{"class":624,"line":732},[73351],{"type":254,"tag":622,"props":73352,"children":73353},{"style":629},[73354],{"type":259,"value":1111},{"type":254,"tag":255,"props":73356,"children":73357},{},[73358,73360,73365],{"type":259,"value":73359},"Each of these methods needs to retrieve a valid token from the ",{"type":254,"tag":389,"props":73361,"children":73363},{"className":73362},[],[73364],{"type":259,"value":73183},{"type":259,"value":73366}," route and set the Authorization header with this token in the HTTP request to each of the protected routes.",{"type":254,"tag":255,"props":73368,"children":73369},{},[73370],{"type":259,"value":73371},"The following code shows how to retrieve the token:",{"type":254,"tag":612,"props":73373,"children":73375},{"className":1423,"code":73374,"language":1425,"meta":205,"style":205},"var body = new { login = \"login\", password = \"password\" };\nvar response = await _httpClient.PostAsync(\"login\", new StringContent(JsonConvert.SerializeObject(body)));\nvar authResponse = await response.Content.ReadAsAsync\u003CAuthResponse>();\n",[73376],{"type":254,"tag":389,"props":73377,"children":73378},{"__ignoreMap":205},[73379,73453,73546],{"type":254,"tag":622,"props":73380,"children":73381},{"class":624,"line":625},[73382,73386,73391,73395,73399,73403,73408,73412,73416,73420,73424,73428,73433,73437,73441,73445,73449],{"type":254,"tag":622,"props":73383,"children":73384},{"style":714},[73385],{"type":259,"value":1710},{"type":254,"tag":622,"props":73387,"children":73388},{"style":714},[73389],{"type":259,"value":73390}," body",{"type":254,"tag":622,"props":73392,"children":73393},{"style":629},[73394],{"type":259,"value":1619},{"type":254,"tag":622,"props":73396,"children":73397},{"style":629},[73398],{"type":259,"value":1624},{"type":254,"tag":622,"props":73400,"children":73401},{"style":629},[73402],{"type":259,"value":1990},{"type":254,"tag":622,"props":73404,"children":73405},{"style":1202},[73406],{"type":259,"value":73407}," login ",{"type":254,"tag":622,"props":73409,"children":73410},{"style":629},[73411],{"type":259,"value":1654},{"type":254,"tag":622,"props":73413,"children":73414},{"style":629},[73415],{"type":259,"value":661},{"type":254,"tag":622,"props":73417,"children":73418},{"style":664},[73419],{"type":259,"value":60610},{"type":254,"tag":622,"props":73421,"children":73422},{"style":629},[73423],{"type":259,"value":652},{"type":254,"tag":622,"props":73425,"children":73426},{"style":629},[73427],{"type":259,"value":3149},{"type":254,"tag":622,"props":73429,"children":73430},{"style":1202},[73431],{"type":259,"value":73432}," password ",{"type":254,"tag":622,"props":73434,"children":73435},{"style":629},[73436],{"type":259,"value":1654},{"type":254,"tag":622,"props":73438,"children":73439},{"style":629},[73440],{"type":259,"value":661},{"type":254,"tag":622,"props":73442,"children":73443},{"style":664},[73444],{"type":259,"value":60635},{"type":254,"tag":622,"props":73446,"children":73447},{"style":629},[73448],{"type":259,"value":652},{"type":254,"tag":622,"props":73450,"children":73451},{"style":629},[73452],{"type":259,"value":13619},{"type":254,"tag":622,"props":73454,"children":73455},{"class":624,"line":635},[73456,73460,73464,73468,73472,73477,73481,73485,73489,73493,73497,73501,73505,73509,73514,73518,73523,73527,73532,73536,73541],{"type":254,"tag":622,"props":73457,"children":73458},{"style":714},[73459],{"type":259,"value":1710},{"type":254,"tag":622,"props":73461,"children":73462},{"style":714},[73463],{"type":259,"value":3587},{"type":254,"tag":622,"props":73465,"children":73466},{"style":629},[73467],{"type":259,"value":1619},{"type":254,"tag":622,"props":73469,"children":73470},{"style":629},[73471],{"type":259,"value":1724},{"type":254,"tag":622,"props":73473,"children":73474},{"style":1202},[73475],{"type":259,"value":73476}," _httpClient",{"type":254,"tag":622,"props":73478,"children":73479},{"style":629},[73480],{"type":259,"value":594},{"type":254,"tag":622,"props":73482,"children":73483},{"style":1735},[73484],{"type":259,"value":36861},{"type":254,"tag":622,"props":73486,"children":73487},{"style":629},[73488],{"type":259,"value":1668},{"type":254,"tag":622,"props":73490,"children":73491},{"style":629},[73492],{"type":259,"value":652},{"type":254,"tag":622,"props":73494,"children":73495},{"style":664},[73496],{"type":259,"value":60610},{"type":254,"tag":622,"props":73498,"children":73499},{"style":629},[73500],{"type":259,"value":652},{"type":254,"tag":622,"props":73502,"children":73503},{"style":629},[73504],{"type":259,"value":3149},{"type":254,"tag":622,"props":73506,"children":73507},{"style":629},[73508],{"type":259,"value":1624},{"type":254,"tag":622,"props":73510,"children":73511},{"style":714},[73512],{"type":259,"value":73513}," StringContent",{"type":254,"tag":622,"props":73515,"children":73516},{"style":629},[73517],{"type":259,"value":1668},{"type":254,"tag":622,"props":73519,"children":73520},{"style":1202},[73521],{"type":259,"value":73522},"JsonConvert",{"type":254,"tag":622,"props":73524,"children":73525},{"style":629},[73526],{"type":259,"value":594},{"type":254,"tag":622,"props":73528,"children":73529},{"style":1735},[73530],{"type":259,"value":73531},"SerializeObject",{"type":254,"tag":622,"props":73533,"children":73534},{"style":629},[73535],{"type":259,"value":1668},{"type":254,"tag":622,"props":73537,"children":73538},{"style":1202},[73539],{"type":259,"value":73540},"body",{"type":254,"tag":622,"props":73542,"children":73543},{"style":629},[73544],{"type":259,"value":73545},")));\n",{"type":254,"tag":622,"props":73547,"children":73548},{"class":624,"line":679},[73549,73553,73558,73562,73566,73570,73574,73579,73583,73588,73592,73596],{"type":254,"tag":622,"props":73550,"children":73551},{"style":714},[73552],{"type":259,"value":1710},{"type":254,"tag":622,"props":73554,"children":73555},{"style":714},[73556],{"type":259,"value":73557}," authResponse",{"type":254,"tag":622,"props":73559,"children":73560},{"style":629},[73561],{"type":259,"value":1619},{"type":254,"tag":622,"props":73563,"children":73564},{"style":629},[73565],{"type":259,"value":1724},{"type":254,"tag":622,"props":73567,"children":73568},{"style":1202},[73569],{"type":259,"value":3587},{"type":254,"tag":622,"props":73571,"children":73572},{"style":629},[73573],{"type":259,"value":594},{"type":254,"tag":622,"props":73575,"children":73576},{"style":1202},[73577],{"type":259,"value":73578},"Content",{"type":254,"tag":622,"props":73580,"children":73581},{"style":629},[73582],{"type":259,"value":594},{"type":254,"tag":622,"props":73584,"children":73585},{"style":1735},[73586],{"type":259,"value":73587},"ReadAsAsync",{"type":254,"tag":622,"props":73589,"children":73590},{"style":629},[73591],{"type":259,"value":1743},{"type":254,"tag":622,"props":73593,"children":73594},{"style":714},[73595],{"type":259,"value":73191},{"type":254,"tag":622,"props":73597,"children":73598},{"style":629},[73599],{"type":259,"value":6531},{"type":254,"tag":255,"props":73601,"children":73602},{},[73603,73604,73609,73611,73616],{"type":259,"value":73086},{"type":254,"tag":389,"props":73605,"children":73607},{"className":73606},[],[73608],{"type":259,"value":73191},{"type":259,"value":73610}," is a class we defined to map the response of the ",{"type":254,"tag":389,"props":73612,"children":73614},{"className":73613},[],[73615],{"type":259,"value":73183},{"type":259,"value":73233},{"type":254,"tag":612,"props":73618,"children":73620},{"className":1423,"code":73619,"language":1425,"meta":205,"style":205},"public class AuthResponse\n{\n  public string Token { get; set; }\n  public DateTime Expiration { get; set; }\n}\n",[73621],{"type":254,"tag":389,"props":73622,"children":73623},{"__ignoreMap":205},[73624,73640,73647,73688,73729],{"type":254,"tag":622,"props":73625,"children":73626},{"class":624,"line":625},[73627,73631,73635],{"type":254,"tag":622,"props":73628,"children":73629},{"style":644},[73630],{"type":259,"value":1437},{"type":254,"tag":622,"props":73632,"children":73633},{"style":714},[73634],{"type":259,"value":6761},{"type":254,"tag":622,"props":73636,"children":73637},{"style":714},[73638],{"type":259,"value":73639}," AuthResponse\n",{"type":254,"tag":622,"props":73641,"children":73642},{"class":624,"line":635},[73643],{"type":254,"tag":622,"props":73644,"children":73645},{"style":629},[73646],{"type":259,"value":632},{"type":254,"tag":622,"props":73648,"children":73649},{"class":624,"line":679},[73650,73655,73659,73664,73668,73672,73676,73680,73684],{"type":254,"tag":622,"props":73651,"children":73652},{"style":644},[73653],{"type":259,"value":73654},"  public",{"type":254,"tag":622,"props":73656,"children":73657},{"style":629},[73658],{"type":259,"value":1975},{"type":254,"tag":622,"props":73660,"children":73661},{"style":714},[73662],{"type":259,"value":73663}," Token",{"type":254,"tag":622,"props":73665,"children":73666},{"style":629},[73667],{"type":259,"value":1990},{"type":254,"tag":622,"props":73669,"children":73670},{"style":714},[73671],{"type":259,"value":1995},{"type":254,"tag":622,"props":73673,"children":73674},{"style":629},[73675],{"type":259,"value":2000},{"type":254,"tag":622,"props":73677,"children":73678},{"style":714},[73679],{"type":259,"value":2005},{"type":254,"tag":622,"props":73681,"children":73682},{"style":629},[73683],{"type":259,"value":2000},{"type":254,"tag":622,"props":73685,"children":73686},{"style":629},[73687],{"type":259,"value":2014},{"type":254,"tag":622,"props":73689,"children":73690},{"class":624,"line":705},[73691,73695,73700,73705,73709,73713,73717,73721,73725],{"type":254,"tag":622,"props":73692,"children":73693},{"style":644},[73694],{"type":259,"value":73654},{"type":254,"tag":622,"props":73696,"children":73697},{"style":714},[73698],{"type":259,"value":73699}," DateTime",{"type":254,"tag":622,"props":73701,"children":73702},{"style":714},[73703],{"type":259,"value":73704}," Expiration",{"type":254,"tag":622,"props":73706,"children":73707},{"style":629},[73708],{"type":259,"value":1990},{"type":254,"tag":622,"props":73710,"children":73711},{"style":714},[73712],{"type":259,"value":1995},{"type":254,"tag":622,"props":73714,"children":73715},{"style":629},[73716],{"type":259,"value":2000},{"type":254,"tag":622,"props":73718,"children":73719},{"style":714},[73720],{"type":259,"value":2005},{"type":254,"tag":622,"props":73722,"children":73723},{"style":629},[73724],{"type":259,"value":2000},{"type":254,"tag":622,"props":73726,"children":73727},{"style":629},[73728],{"type":259,"value":2014},{"type":254,"tag":622,"props":73730,"children":73731},{"class":624,"line":732},[73732],{"type":254,"tag":622,"props":73733,"children":73734},{"style":629},[73735],{"type":259,"value":1111},{"type":254,"tag":255,"props":73737,"children":73738},{},[73739,73741,73747],{"type":259,"value":73740},"So now we have the code to retrieve the token, how do we use it to implement our ",{"type":254,"tag":389,"props":73742,"children":73744},{"className":73743},[],[73745],{"type":259,"value":73746},"IUserService",{"type":259,"value":1980},{"type":254,"tag":282,"props":73749,"children":73751},{"id":73750},"retrieve-the-token-from-a-private-method",[73752],{"type":259,"value":73753},"Retrieve the token from a private method",{"type":254,"tag":255,"props":73755,"children":73756},{},[73757,73759,73765,73767,73772,73773,73778],{"type":259,"value":73758},"The easiest way to do that is to create a private method in ",{"type":254,"tag":389,"props":73760,"children":73762},{"className":73761},[],[73763],{"type":259,"value":73764},"UserService",{"type":259,"value":73766}," that returns this token and to call it from ",{"type":254,"tag":389,"props":73768,"children":73770},{"className":73769},[],[73771],{"type":259,"value":73224},{"type":259,"value":10347},{"type":254,"tag":389,"props":73774,"children":73776},{"className":73775},[],[73777],{"type":259,"value":73242},{"type":259,"value":73779},". That would give us something like that :",{"type":254,"tag":612,"props":73781,"children":73783},{"className":1423,"code":73782,"language":1425,"meta":205,"style":205},"public class UserService : IUserService\n{\n    private readonly HttpClient _httpClient;\n    public UserService(HttpClient httpClient)\n    {\n        _httpClient = httpClient;\n    }\n\n    public async Task\u003CIReadOnlyCollection\u003CUser>> GetAllUsers()\n    {\n        var token = await RetrieveToken();\n        var request = new HttpRequestMessage(HttpMethod.Get, \"user\");\n        request.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", token);\n        var response = await _httpClient.SendAsync(request);\n        response.EnsureSuccessStatusCode();\n        return await response.Content.ReadAsAsync\u003CIReadOnlyCollection\u003CUser>>();\n    }\n\n    public async Task UpdateUser(User userToUpdate)\n    {\n        var token = await RetrieveToken();\n        var request = new HttpRequestMessage(HttpMethod.Put, $\"user/{userToUpdate.Name}\");\n        request.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", token);\n        request.Content = new StringContent(JsonConvert.SerializeObject(userToUpdate));\n        var response = await _httpClient.SendAsync(request);\n        response.EnsureSuccessStatusCode();\n    }\n\n    private async Task\u003Cstring> RetrieveToken()\n    {\n        var body = new { login = \"login\", password = \"password\" };\n        var response = await _httpClient.PostAsync(\"login\", new StringContent(JsonConvert.SerializeObject(body)));\n        var authResponse = await response.Content.ReadAsAsync\u003CAuthResponse>();\n        return authResponse.Token;\n    }\n}\n",[73784],{"type":254,"tag":389,"props":73785,"children":73786},{"__ignoreMap":205},[73787,73811,73818,73841,73868,73875,73895,73902,73909,73952,73959,73988,74050,74116,74161,74182,74234,74241,74248,74283,74290,74317,74396,74459,74515,74558,74577,74584,74591,74626,74633,74704,74791,74842,74866,74873],{"type":254,"tag":622,"props":73788,"children":73789},{"class":624,"line":625},[73790,73794,73798,73803,73807],{"type":254,"tag":622,"props":73791,"children":73792},{"style":644},[73793],{"type":259,"value":1437},{"type":254,"tag":622,"props":73795,"children":73796},{"style":714},[73797],{"type":259,"value":6761},{"type":254,"tag":622,"props":73799,"children":73800},{"style":714},[73801],{"type":259,"value":73802}," UserService",{"type":254,"tag":622,"props":73804,"children":73805},{"style":629},[73806],{"type":259,"value":3274},{"type":254,"tag":622,"props":73808,"children":73809},{"style":714},[73810],{"type":259,"value":73273},{"type":254,"tag":622,"props":73812,"children":73813},{"class":624,"line":635},[73814],{"type":254,"tag":622,"props":73815,"children":73816},{"style":629},[73817],{"type":259,"value":632},{"type":254,"tag":622,"props":73819,"children":73820},{"class":624,"line":679},[73821,73825,73829,73833,73837],{"type":254,"tag":622,"props":73822,"children":73823},{"style":644},[73824],{"type":259,"value":6790},{"type":254,"tag":622,"props":73826,"children":73827},{"style":644},[73828],{"type":259,"value":6795},{"type":254,"tag":622,"props":73830,"children":73831},{"style":714},[73832],{"type":259,"value":1629},{"type":254,"tag":622,"props":73834,"children":73835},{"style":714},[73836],{"type":259,"value":73476},{"type":254,"tag":622,"props":73838,"children":73839},{"style":629},[73840],{"type":259,"value":2350},{"type":254,"tag":622,"props":73842,"children":73843},{"class":624,"line":705},[73844,73848,73852,73856,73860,73864],{"type":254,"tag":622,"props":73845,"children":73846},{"style":644},[73847],{"type":259,"value":1970},{"type":254,"tag":622,"props":73849,"children":73850},{"style":1735},[73851],{"type":259,"value":73802},{"type":254,"tag":622,"props":73853,"children":73854},{"style":629},[73855],{"type":259,"value":1668},{"type":254,"tag":622,"props":73857,"children":73858},{"style":714},[73859],{"type":259,"value":1565},{"type":254,"tag":622,"props":73861,"children":73862},{"style":714},[73863],{"type":259,"value":1614},{"type":254,"tag":622,"props":73865,"children":73866},{"style":629},[73867],{"type":259,"value":1685},{"type":254,"tag":622,"props":73869,"children":73870},{"class":624,"line":732},[73871],{"type":254,"tag":622,"props":73872,"children":73873},{"style":629},[73874],{"type":259,"value":3574},{"type":254,"tag":622,"props":73876,"children":73877},{"class":624,"line":759},[73878,73883,73887,73891],{"type":254,"tag":622,"props":73879,"children":73880},{"style":1202},[73881],{"type":259,"value":73882},"        _httpClient ",{"type":254,"tag":622,"props":73884,"children":73885},{"style":629},[73886],{"type":259,"value":1654},{"type":254,"tag":622,"props":73888,"children":73889},{"style":1202},[73890],{"type":259,"value":1614},{"type":254,"tag":622,"props":73892,"children":73893},{"style":629},[73894],{"type":259,"value":2350},{"type":254,"tag":622,"props":73896,"children":73897},{"class":624,"line":798},[73898],{"type":254,"tag":622,"props":73899,"children":73900},{"style":629},[73901],{"type":259,"value":1093},{"type":254,"tag":622,"props":73903,"children":73904},{"class":624,"line":836},[73905],{"type":254,"tag":622,"props":73906,"children":73907},{"emptyLinePlaceholder":1699},[73908],{"type":259,"value":1702},{"type":254,"tag":622,"props":73910,"children":73911},{"class":624,"line":862},[73912,73916,73920,73924,73928,73932,73936,73940,73944,73948],{"type":254,"tag":622,"props":73913,"children":73914},{"style":644},[73915],{"type":259,"value":1970},{"type":254,"tag":622,"props":73917,"children":73918},{"style":644},[73919],{"type":259,"value":6987},{"type":254,"tag":622,"props":73921,"children":73922},{"style":714},[73923],{"type":259,"value":2842},{"type":254,"tag":622,"props":73925,"children":73926},{"style":629},[73927],{"type":259,"value":1743},{"type":254,"tag":622,"props":73929,"children":73930},{"style":714},[73931],{"type":259,"value":73296},{"type":254,"tag":622,"props":73933,"children":73934},{"style":629},[73935],{"type":259,"value":1743},{"type":254,"tag":622,"props":73937,"children":73938},{"style":714},[73939],{"type":259,"value":44521},{"type":254,"tag":622,"props":73941,"children":73942},{"style":629},[73943],{"type":259,"value":73309},{"type":254,"tag":622,"props":73945,"children":73946},{"style":1735},[73947],{"type":259,"value":73314},{"type":254,"tag":622,"props":73949,"children":73950},{"style":629},[73951],{"type":259,"value":1634},{"type":254,"tag":622,"props":73953,"children":73954},{"class":624,"line":900},[73955],{"type":254,"tag":622,"props":73956,"children":73957},{"style":629},[73958],{"type":259,"value":3574},{"type":254,"tag":622,"props":73960,"children":73961},{"class":624,"line":938},[73962,73966,73971,73975,73979,73984],{"type":254,"tag":622,"props":73963,"children":73964},{"style":714},[73965],{"type":259,"value":3582},{"type":254,"tag":622,"props":73967,"children":73968},{"style":714},[73969],{"type":259,"value":73970}," token",{"type":254,"tag":622,"props":73972,"children":73973},{"style":629},[73974],{"type":259,"value":1619},{"type":254,"tag":622,"props":73976,"children":73977},{"style":629},[73978],{"type":259,"value":1724},{"type":254,"tag":622,"props":73980,"children":73981},{"style":1735},[73982],{"type":259,"value":73983}," RetrieveToken",{"type":254,"tag":622,"props":73985,"children":73986},{"style":629},[73987],{"type":259,"value":2798},{"type":254,"tag":622,"props":73989,"children":73990},{"class":624,"line":976},[73991,73995,74000,74004,74008,74013,74017,74022,74026,74030,74034,74038,74042,74046],{"type":254,"tag":622,"props":73992,"children":73993},{"style":714},[73994],{"type":259,"value":3582},{"type":254,"tag":622,"props":73996,"children":73997},{"style":714},[73998],{"type":259,"value":73999}," request",{"type":254,"tag":622,"props":74001,"children":74002},{"style":629},[74003],{"type":259,"value":1619},{"type":254,"tag":622,"props":74005,"children":74006},{"style":629},[74007],{"type":259,"value":1624},{"type":254,"tag":622,"props":74009,"children":74010},{"style":714},[74011],{"type":259,"value":74012}," HttpRequestMessage",{"type":254,"tag":622,"props":74014,"children":74015},{"style":629},[74016],{"type":259,"value":1668},{"type":254,"tag":622,"props":74018,"children":74019},{"style":1202},[74020],{"type":259,"value":74021},"HttpMethod",{"type":254,"tag":622,"props":74023,"children":74024},{"style":629},[74025],{"type":259,"value":594},{"type":254,"tag":622,"props":74027,"children":74028},{"style":1202},[74029],{"type":259,"value":15108},{"type":254,"tag":622,"props":74031,"children":74032},{"style":629},[74033],{"type":259,"value":3149},{"type":254,"tag":622,"props":74035,"children":74036},{"style":629},[74037],{"type":259,"value":661},{"type":254,"tag":622,"props":74039,"children":74040},{"style":664},[74041],{"type":259,"value":65323},{"type":254,"tag":622,"props":74043,"children":74044},{"style":629},[74045],{"type":259,"value":652},{"type":254,"tag":622,"props":74047,"children":74048},{"style":629},[74049],{"type":259,"value":1554},{"type":254,"tag":622,"props":74051,"children":74052},{"class":624,"line":1014},[74053,74058,74062,74067,74071,74075,74079,74083,74087,74091,74095,74100,74104,74108,74112],{"type":254,"tag":622,"props":74054,"children":74055},{"style":1202},[74056],{"type":259,"value":74057},"        request",{"type":254,"tag":622,"props":74059,"children":74060},{"style":629},[74061],{"type":259,"value":594},{"type":254,"tag":622,"props":74063,"children":74064},{"style":1202},[74065],{"type":259,"value":74066},"Headers",{"type":254,"tag":622,"props":74068,"children":74069},{"style":629},[74070],{"type":259,"value":594},{"type":254,"tag":622,"props":74072,"children":74073},{"style":1202},[74074],{"type":259,"value":36796},{"type":254,"tag":622,"props":74076,"children":74077},{"style":629},[74078],{"type":259,"value":1654},{"type":254,"tag":622,"props":74080,"children":74081},{"style":629},[74082],{"type":259,"value":1624},{"type":254,"tag":622,"props":74084,"children":74085},{"style":714},[74086],{"type":259,"value":36809},{"type":254,"tag":622,"props":74088,"children":74089},{"style":629},[74090],{"type":259,"value":1668},{"type":254,"tag":622,"props":74092,"children":74093},{"style":629},[74094],{"type":259,"value":652},{"type":254,"tag":622,"props":74096,"children":74097},{"style":664},[74098],{"type":259,"value":74099},"Bearer",{"type":254,"tag":622,"props":74101,"children":74102},{"style":629},[74103],{"type":259,"value":652},{"type":254,"tag":622,"props":74105,"children":74106},{"style":629},[74107],{"type":259,"value":3149},{"type":254,"tag":622,"props":74109,"children":74110},{"style":1202},[74111],{"type":259,"value":73970},{"type":254,"tag":622,"props":74113,"children":74114},{"style":629},[74115],{"type":259,"value":1554},{"type":254,"tag":622,"props":74117,"children":74118},{"class":624,"line":1052},[74119,74123,74127,74131,74135,74139,74143,74148,74152,74157],{"type":254,"tag":622,"props":74120,"children":74121},{"style":714},[74122],{"type":259,"value":3582},{"type":254,"tag":622,"props":74124,"children":74125},{"style":714},[74126],{"type":259,"value":3587},{"type":254,"tag":622,"props":74128,"children":74129},{"style":629},[74130],{"type":259,"value":1619},{"type":254,"tag":622,"props":74132,"children":74133},{"style":629},[74134],{"type":259,"value":1724},{"type":254,"tag":622,"props":74136,"children":74137},{"style":1202},[74138],{"type":259,"value":73476},{"type":254,"tag":622,"props":74140,"children":74141},{"style":629},[74142],{"type":259,"value":594},{"type":254,"tag":622,"props":74144,"children":74145},{"style":1735},[74146],{"type":259,"value":74147},"SendAsync",{"type":254,"tag":622,"props":74149,"children":74150},{"style":629},[74151],{"type":259,"value":1668},{"type":254,"tag":622,"props":74153,"children":74154},{"style":1202},[74155],{"type":259,"value":74156},"request",{"type":254,"tag":622,"props":74158,"children":74159},{"style":629},[74160],{"type":259,"value":1554},{"type":254,"tag":622,"props":74162,"children":74163},{"class":624,"line":1078},[74164,74169,74173,74178],{"type":254,"tag":622,"props":74165,"children":74166},{"style":1202},[74167],{"type":259,"value":74168},"        response",{"type":254,"tag":622,"props":74170,"children":74171},{"style":629},[74172],{"type":259,"value":594},{"type":254,"tag":622,"props":74174,"children":74175},{"style":1735},[74176],{"type":259,"value":74177},"EnsureSuccessStatusCode",{"type":254,"tag":622,"props":74179,"children":74180},{"style":629},[74181],{"type":259,"value":2798},{"type":254,"tag":622,"props":74183,"children":74184},{"class":624,"line":1087},[74185,74189,74193,74197,74201,74205,74209,74213,74217,74221,74225,74229],{"type":254,"tag":622,"props":74186,"children":74187},{"style":1196},[74188],{"type":259,"value":9046},{"type":254,"tag":622,"props":74190,"children":74191},{"style":629},[74192],{"type":259,"value":1724},{"type":254,"tag":622,"props":74194,"children":74195},{"style":1202},[74196],{"type":259,"value":3587},{"type":254,"tag":622,"props":74198,"children":74199},{"style":629},[74200],{"type":259,"value":594},{"type":254,"tag":622,"props":74202,"children":74203},{"style":1202},[74204],{"type":259,"value":73578},{"type":254,"tag":622,"props":74206,"children":74207},{"style":629},[74208],{"type":259,"value":594},{"type":254,"tag":622,"props":74210,"children":74211},{"style":1735},[74212],{"type":259,"value":73587},{"type":254,"tag":622,"props":74214,"children":74215},{"style":629},[74216],{"type":259,"value":1743},{"type":254,"tag":622,"props":74218,"children":74219},{"style":714},[74220],{"type":259,"value":73296},{"type":254,"tag":622,"props":74222,"children":74223},{"style":629},[74224],{"type":259,"value":1743},{"type":254,"tag":622,"props":74226,"children":74227},{"style":714},[74228],{"type":259,"value":44521},{"type":254,"tag":622,"props":74230,"children":74231},{"style":629},[74232],{"type":259,"value":74233},">>();\n",{"type":254,"tag":622,"props":74235,"children":74236},{"class":624,"line":1096},[74237],{"type":254,"tag":622,"props":74238,"children":74239},{"style":629},[74240],{"type":259,"value":1093},{"type":254,"tag":622,"props":74242,"children":74243},{"class":624,"line":1105},[74244],{"type":254,"tag":622,"props":74245,"children":74246},{"emptyLinePlaceholder":1699},[74247],{"type":259,"value":1702},{"type":254,"tag":622,"props":74249,"children":74250},{"class":624,"line":4131},[74251,74255,74259,74263,74267,74271,74275,74279],{"type":254,"tag":622,"props":74252,"children":74253},{"style":644},[74254],{"type":259,"value":1970},{"type":254,"tag":622,"props":74256,"children":74257},{"style":644},[74258],{"type":259,"value":6987},{"type":254,"tag":622,"props":74260,"children":74261},{"style":714},[74262],{"type":259,"value":2842},{"type":254,"tag":622,"props":74264,"children":74265},{"style":1735},[74266],{"type":259,"value":73330},{"type":254,"tag":622,"props":74268,"children":74269},{"style":629},[74270],{"type":259,"value":1668},{"type":254,"tag":622,"props":74272,"children":74273},{"style":714},[74274],{"type":259,"value":44521},{"type":254,"tag":622,"props":74276,"children":74277},{"style":714},[74278],{"type":259,"value":73343},{"type":254,"tag":622,"props":74280,"children":74281},{"style":629},[74282],{"type":259,"value":1685},{"type":254,"tag":622,"props":74284,"children":74285},{"class":624,"line":4183},[74286],{"type":254,"tag":622,"props":74287,"children":74288},{"style":629},[74289],{"type":259,"value":3574},{"type":254,"tag":622,"props":74291,"children":74292},{"class":624,"line":4208},[74293,74297,74301,74305,74309,74313],{"type":254,"tag":622,"props":74294,"children":74295},{"style":714},[74296],{"type":259,"value":3582},{"type":254,"tag":622,"props":74298,"children":74299},{"style":714},[74300],{"type":259,"value":73970},{"type":254,"tag":622,"props":74302,"children":74303},{"style":629},[74304],{"type":259,"value":1619},{"type":254,"tag":622,"props":74306,"children":74307},{"style":629},[74308],{"type":259,"value":1724},{"type":254,"tag":622,"props":74310,"children":74311},{"style":1735},[74312],{"type":259,"value":73983},{"type":254,"tag":622,"props":74314,"children":74315},{"style":629},[74316],{"type":259,"value":2798},{"type":254,"tag":622,"props":74318,"children":74319},{"class":624,"line":4216},[74320,74324,74328,74332,74336,74340,74344,74348,74352,74357,74361,74366,74371,74375,74380,74384,74388,74392],{"type":254,"tag":622,"props":74321,"children":74322},{"style":714},[74323],{"type":259,"value":3582},{"type":254,"tag":622,"props":74325,"children":74326},{"style":714},[74327],{"type":259,"value":73999},{"type":254,"tag":622,"props":74329,"children":74330},{"style":629},[74331],{"type":259,"value":1619},{"type":254,"tag":622,"props":74333,"children":74334},{"style":629},[74335],{"type":259,"value":1624},{"type":254,"tag":622,"props":74337,"children":74338},{"style":714},[74339],{"type":259,"value":74012},{"type":254,"tag":622,"props":74341,"children":74342},{"style":629},[74343],{"type":259,"value":1668},{"type":254,"tag":622,"props":74345,"children":74346},{"style":1202},[74347],{"type":259,"value":74021},{"type":254,"tag":622,"props":74349,"children":74350},{"style":629},[74351],{"type":259,"value":594},{"type":254,"tag":622,"props":74353,"children":74354},{"style":1202},[74355],{"type":259,"value":74356},"Put",{"type":254,"tag":622,"props":74358,"children":74359},{"style":629},[74360],{"type":259,"value":3149},{"type":254,"tag":622,"props":74362,"children":74363},{"style":629},[74364],{"type":259,"value":74365}," $\"",{"type":254,"tag":622,"props":74367,"children":74368},{"style":664},[74369],{"type":259,"value":74370},"user/",{"type":254,"tag":622,"props":74372,"children":74373},{"style":629},[74374],{"type":259,"value":3234},{"type":254,"tag":622,"props":74376,"children":74377},{"style":1202},[74378],{"type":259,"value":74379},"userToUpdate",{"type":254,"tag":622,"props":74381,"children":74382},{"style":629},[74383],{"type":259,"value":594},{"type":254,"tag":622,"props":74385,"children":74386},{"style":1202},[74387],{"type":259,"value":2254},{"type":254,"tag":622,"props":74389,"children":74390},{"style":629},[74391],{"type":259,"value":2443},{"type":254,"tag":622,"props":74393,"children":74394},{"style":629},[74395],{"type":259,"value":1554},{"type":254,"tag":622,"props":74397,"children":74398},{"class":624,"line":4224},[74399,74403,74407,74411,74415,74419,74423,74427,74431,74435,74439,74443,74447,74451,74455],{"type":254,"tag":622,"props":74400,"children":74401},{"style":1202},[74402],{"type":259,"value":74057},{"type":254,"tag":622,"props":74404,"children":74405},{"style":629},[74406],{"type":259,"value":594},{"type":254,"tag":622,"props":74408,"children":74409},{"style":1202},[74410],{"type":259,"value":74066},{"type":254,"tag":622,"props":74412,"children":74413},{"style":629},[74414],{"type":259,"value":594},{"type":254,"tag":622,"props":74416,"children":74417},{"style":1202},[74418],{"type":259,"value":36796},{"type":254,"tag":622,"props":74420,"children":74421},{"style":629},[74422],{"type":259,"value":1654},{"type":254,"tag":622,"props":74424,"children":74425},{"style":629},[74426],{"type":259,"value":1624},{"type":254,"tag":622,"props":74428,"children":74429},{"style":714},[74430],{"type":259,"value":36809},{"type":254,"tag":622,"props":74432,"children":74433},{"style":629},[74434],{"type":259,"value":1668},{"type":254,"tag":622,"props":74436,"children":74437},{"style":629},[74438],{"type":259,"value":652},{"type":254,"tag":622,"props":74440,"children":74441},{"style":664},[74442],{"type":259,"value":74099},{"type":254,"tag":622,"props":74444,"children":74445},{"style":629},[74446],{"type":259,"value":652},{"type":254,"tag":622,"props":74448,"children":74449},{"style":629},[74450],{"type":259,"value":3149},{"type":254,"tag":622,"props":74452,"children":74453},{"style":1202},[74454],{"type":259,"value":73970},{"type":254,"tag":622,"props":74456,"children":74457},{"style":629},[74458],{"type":259,"value":1554},{"type":254,"tag":622,"props":74460,"children":74461},{"class":624,"line":4268},[74462,74466,74470,74475,74479,74483,74487,74491,74495,74499,74503,74507,74511],{"type":254,"tag":622,"props":74463,"children":74464},{"style":1202},[74465],{"type":259,"value":74057},{"type":254,"tag":622,"props":74467,"children":74468},{"style":629},[74469],{"type":259,"value":594},{"type":254,"tag":622,"props":74471,"children":74472},{"style":1202},[74473],{"type":259,"value":74474},"Content ",{"type":254,"tag":622,"props":74476,"children":74477},{"style":629},[74478],{"type":259,"value":1654},{"type":254,"tag":622,"props":74480,"children":74481},{"style":629},[74482],{"type":259,"value":1624},{"type":254,"tag":622,"props":74484,"children":74485},{"style":714},[74486],{"type":259,"value":73513},{"type":254,"tag":622,"props":74488,"children":74489},{"style":629},[74490],{"type":259,"value":1668},{"type":254,"tag":622,"props":74492,"children":74493},{"style":1202},[74494],{"type":259,"value":73522},{"type":254,"tag":622,"props":74496,"children":74497},{"style":629},[74498],{"type":259,"value":594},{"type":254,"tag":622,"props":74500,"children":74501},{"style":1735},[74502],{"type":259,"value":73531},{"type":254,"tag":622,"props":74504,"children":74505},{"style":629},[74506],{"type":259,"value":1668},{"type":254,"tag":622,"props":74508,"children":74509},{"style":1202},[74510],{"type":259,"value":74379},{"type":254,"tag":622,"props":74512,"children":74513},{"style":629},[74514],{"type":259,"value":6624},{"type":254,"tag":622,"props":74516,"children":74517},{"class":624,"line":4276},[74518,74522,74526,74530,74534,74538,74542,74546,74550,74554],{"type":254,"tag":622,"props":74519,"children":74520},{"style":714},[74521],{"type":259,"value":3582},{"type":254,"tag":622,"props":74523,"children":74524},{"style":714},[74525],{"type":259,"value":3587},{"type":254,"tag":622,"props":74527,"children":74528},{"style":629},[74529],{"type":259,"value":1619},{"type":254,"tag":622,"props":74531,"children":74532},{"style":629},[74533],{"type":259,"value":1724},{"type":254,"tag":622,"props":74535,"children":74536},{"style":1202},[74537],{"type":259,"value":73476},{"type":254,"tag":622,"props":74539,"children":74540},{"style":629},[74541],{"type":259,"value":594},{"type":254,"tag":622,"props":74543,"children":74544},{"style":1735},[74545],{"type":259,"value":74147},{"type":254,"tag":622,"props":74547,"children":74548},{"style":629},[74549],{"type":259,"value":1668},{"type":254,"tag":622,"props":74551,"children":74552},{"style":1202},[74553],{"type":259,"value":74156},{"type":254,"tag":622,"props":74555,"children":74556},{"style":629},[74557],{"type":259,"value":1554},{"type":254,"tag":622,"props":74559,"children":74560},{"class":624,"line":4332},[74561,74565,74569,74573],{"type":254,"tag":622,"props":74562,"children":74563},{"style":1202},[74564],{"type":259,"value":74168},{"type":254,"tag":622,"props":74566,"children":74567},{"style":629},[74568],{"type":259,"value":594},{"type":254,"tag":622,"props":74570,"children":74571},{"style":1735},[74572],{"type":259,"value":74177},{"type":254,"tag":622,"props":74574,"children":74575},{"style":629},[74576],{"type":259,"value":2798},{"type":254,"tag":622,"props":74578,"children":74579},{"class":624,"line":4376},[74580],{"type":254,"tag":622,"props":74581,"children":74582},{"style":629},[74583],{"type":259,"value":1093},{"type":254,"tag":622,"props":74585,"children":74586},{"class":624,"line":4384},[74587],{"type":254,"tag":622,"props":74588,"children":74589},{"emptyLinePlaceholder":1699},[74590],{"type":259,"value":1702},{"type":254,"tag":622,"props":74592,"children":74593},{"class":624,"line":4416},[74594,74598,74602,74606,74610,74614,74618,74622],{"type":254,"tag":622,"props":74595,"children":74596},{"style":644},[74597],{"type":259,"value":6790},{"type":254,"tag":622,"props":74599,"children":74600},{"style":644},[74601],{"type":259,"value":6987},{"type":254,"tag":622,"props":74603,"children":74604},{"style":714},[74605],{"type":259,"value":2842},{"type":254,"tag":622,"props":74607,"children":74608},{"style":629},[74609],{"type":259,"value":1743},{"type":254,"tag":622,"props":74611,"children":74612},{"style":629},[74613],{"type":259,"value":2851},{"type":254,"tag":622,"props":74615,"children":74616},{"style":629},[74617],{"type":259,"value":6813},{"type":254,"tag":622,"props":74619,"children":74620},{"style":1735},[74621],{"type":259,"value":73983},{"type":254,"tag":622,"props":74623,"children":74624},{"style":629},[74625],{"type":259,"value":1634},{"type":254,"tag":622,"props":74627,"children":74628},{"class":624,"line":4456},[74629],{"type":254,"tag":622,"props":74630,"children":74631},{"style":629},[74632],{"type":259,"value":3574},{"type":254,"tag":622,"props":74634,"children":74635},{"class":624,"line":4488},[74636,74640,74644,74648,74652,74656,74660,74664,74668,74672,74676,74680,74684,74688,74692,74696,74700],{"type":254,"tag":622,"props":74637,"children":74638},{"style":714},[74639],{"type":259,"value":3582},{"type":254,"tag":622,"props":74641,"children":74642},{"style":714},[74643],{"type":259,"value":73390},{"type":254,"tag":622,"props":74645,"children":74646},{"style":629},[74647],{"type":259,"value":1619},{"type":254,"tag":622,"props":74649,"children":74650},{"style":629},[74651],{"type":259,"value":1624},{"type":254,"tag":622,"props":74653,"children":74654},{"style":629},[74655],{"type":259,"value":1990},{"type":254,"tag":622,"props":74657,"children":74658},{"style":1202},[74659],{"type":259,"value":73407},{"type":254,"tag":622,"props":74661,"children":74662},{"style":629},[74663],{"type":259,"value":1654},{"type":254,"tag":622,"props":74665,"children":74666},{"style":629},[74667],{"type":259,"value":661},{"type":254,"tag":622,"props":74669,"children":74670},{"style":664},[74671],{"type":259,"value":60610},{"type":254,"tag":622,"props":74673,"children":74674},{"style":629},[74675],{"type":259,"value":652},{"type":254,"tag":622,"props":74677,"children":74678},{"style":629},[74679],{"type":259,"value":3149},{"type":254,"tag":622,"props":74681,"children":74682},{"style":1202},[74683],{"type":259,"value":73432},{"type":254,"tag":622,"props":74685,"children":74686},{"style":629},[74687],{"type":259,"value":1654},{"type":254,"tag":622,"props":74689,"children":74690},{"style":629},[74691],{"type":259,"value":661},{"type":254,"tag":622,"props":74693,"children":74694},{"style":664},[74695],{"type":259,"value":60635},{"type":254,"tag":622,"props":74697,"children":74698},{"style":629},[74699],{"type":259,"value":652},{"type":254,"tag":622,"props":74701,"children":74702},{"style":629},[74703],{"type":259,"value":13619},{"type":254,"tag":622,"props":74705,"children":74706},{"class":624,"line":4496},[74707,74711,74715,74719,74723,74727,74731,74735,74739,74743,74747,74751,74755,74759,74763,74767,74771,74775,74779,74783,74787],{"type":254,"tag":622,"props":74708,"children":74709},{"style":714},[74710],{"type":259,"value":3582},{"type":254,"tag":622,"props":74712,"children":74713},{"style":714},[74714],{"type":259,"value":3587},{"type":254,"tag":622,"props":74716,"children":74717},{"style":629},[74718],{"type":259,"value":1619},{"type":254,"tag":622,"props":74720,"children":74721},{"style":629},[74722],{"type":259,"value":1724},{"type":254,"tag":622,"props":74724,"children":74725},{"style":1202},[74726],{"type":259,"value":73476},{"type":254,"tag":622,"props":74728,"children":74729},{"style":629},[74730],{"type":259,"value":594},{"type":254,"tag":622,"props":74732,"children":74733},{"style":1735},[74734],{"type":259,"value":36861},{"type":254,"tag":622,"props":74736,"children":74737},{"style":629},[74738],{"type":259,"value":1668},{"type":254,"tag":622,"props":74740,"children":74741},{"style":629},[74742],{"type":259,"value":652},{"type":254,"tag":622,"props":74744,"children":74745},{"style":664},[74746],{"type":259,"value":60610},{"type":254,"tag":622,"props":74748,"children":74749},{"style":629},[74750],{"type":259,"value":652},{"type":254,"tag":622,"props":74752,"children":74753},{"style":629},[74754],{"type":259,"value":3149},{"type":254,"tag":622,"props":74756,"children":74757},{"style":629},[74758],{"type":259,"value":1624},{"type":254,"tag":622,"props":74760,"children":74761},{"style":714},[74762],{"type":259,"value":73513},{"type":254,"tag":622,"props":74764,"children":74765},{"style":629},[74766],{"type":259,"value":1668},{"type":254,"tag":622,"props":74768,"children":74769},{"style":1202},[74770],{"type":259,"value":73522},{"type":254,"tag":622,"props":74772,"children":74773},{"style":629},[74774],{"type":259,"value":594},{"type":254,"tag":622,"props":74776,"children":74777},{"style":1735},[74778],{"type":259,"value":73531},{"type":254,"tag":622,"props":74780,"children":74781},{"style":629},[74782],{"type":259,"value":1668},{"type":254,"tag":622,"props":74784,"children":74785},{"style":1202},[74786],{"type":259,"value":73540},{"type":254,"tag":622,"props":74788,"children":74789},{"style":629},[74790],{"type":259,"value":73545},{"type":254,"tag":622,"props":74792,"children":74793},{"class":624,"line":4564},[74794,74798,74802,74806,74810,74814,74818,74822,74826,74830,74834,74838],{"type":254,"tag":622,"props":74795,"children":74796},{"style":714},[74797],{"type":259,"value":3582},{"type":254,"tag":622,"props":74799,"children":74800},{"style":714},[74801],{"type":259,"value":73557},{"type":254,"tag":622,"props":74803,"children":74804},{"style":629},[74805],{"type":259,"value":1619},{"type":254,"tag":622,"props":74807,"children":74808},{"style":629},[74809],{"type":259,"value":1724},{"type":254,"tag":622,"props":74811,"children":74812},{"style":1202},[74813],{"type":259,"value":3587},{"type":254,"tag":622,"props":74815,"children":74816},{"style":629},[74817],{"type":259,"value":594},{"type":254,"tag":622,"props":74819,"children":74820},{"style":1202},[74821],{"type":259,"value":73578},{"type":254,"tag":622,"props":74823,"children":74824},{"style":629},[74825],{"type":259,"value":594},{"type":254,"tag":622,"props":74827,"children":74828},{"style":1735},[74829],{"type":259,"value":73587},{"type":254,"tag":622,"props":74831,"children":74832},{"style":629},[74833],{"type":259,"value":1743},{"type":254,"tag":622,"props":74835,"children":74836},{"style":714},[74837],{"type":259,"value":73191},{"type":254,"tag":622,"props":74839,"children":74840},{"style":629},[74841],{"type":259,"value":6531},{"type":254,"tag":622,"props":74843,"children":74844},{"class":624,"line":4572},[74845,74849,74853,74857,74862],{"type":254,"tag":622,"props":74846,"children":74847},{"style":1196},[74848],{"type":259,"value":9046},{"type":254,"tag":622,"props":74850,"children":74851},{"style":1202},[74852],{"type":259,"value":73557},{"type":254,"tag":622,"props":74854,"children":74855},{"style":629},[74856],{"type":259,"value":594},{"type":254,"tag":622,"props":74858,"children":74859},{"style":1202},[74860],{"type":259,"value":74861},"Token",{"type":254,"tag":622,"props":74863,"children":74864},{"style":629},[74865],{"type":259,"value":2350},{"type":254,"tag":622,"props":74867,"children":74868},{"class":624,"line":4644},[74869],{"type":254,"tag":622,"props":74870,"children":74871},{"style":629},[74872],{"type":259,"value":1093},{"type":254,"tag":622,"props":74874,"children":74875},{"class":624,"line":4652},[74876],{"type":254,"tag":622,"props":74877,"children":74878},{"style":629},[74879],{"type":259,"value":1111},{"type":254,"tag":255,"props":74881,"children":74882},{},[74883],{"type":259,"value":74884},"There are two main problems with this way of doing things:",{"type":254,"tag":332,"props":74886,"children":74887},{},[74888,74908],{"type":254,"tag":336,"props":74889,"children":74890},{},[74891,74893,74899,74901,74906],{"type":259,"value":74892},"We have some code duplication as we are calling the ",{"type":254,"tag":389,"props":74894,"children":74896},{"className":74895},[],[74897],{"type":259,"value":74898},"RetrieveToken",{"type":259,"value":74900}," in each of our methods calling the API. That could be okay here as we only have 2 methods calling the API but that can quickly be problematic if we start to have more methods and repeat the call to ",{"type":254,"tag":389,"props":74902,"children":74904},{"className":74903},[],[74905],{"type":259,"value":74898},{"type":259,"value":74907},"in each method.",{"type":254,"tag":336,"props":74909,"children":74910},{},[74911,74913,74918],{"type":259,"value":74912},"For each call to an authenticated route of the API, we are making a call to the ",{"type":254,"tag":389,"props":74914,"children":74916},{"className":74915},[],[74917],{"type":259,"value":60610},{"type":259,"value":74919}," route even if our token from a previous call is probably still valid.",{"type":254,"tag":282,"props":74921,"children":74923},{"id":74922},"use-a-dedicated-service-to-retrieve-the-token-and-save-it-for-future-calls",[74924],{"type":259,"value":74925},"Use a dedicated service to retrieve the token and save it for future calls",{"type":254,"tag":255,"props":74927,"children":74928},{},[74929,74931,74936,74938,74944,74946,74951,74953,74958,74960,74965],{"type":259,"value":74930},"Although it's not necessary at this point, it can be interesting to move the code of our private method ",{"type":254,"tag":389,"props":74932,"children":74934},{"className":74933},[],[74935],{"type":259,"value":74898},{"type":259,"value":74937}," into a separate service ",{"type":254,"tag":389,"props":74939,"children":74941},{"className":74940},[],[74942],{"type":259,"value":74943},"UserApiAuthenticationService",{"type":259,"value":74945}," that will be injected in ",{"type":254,"tag":389,"props":74947,"children":74949},{"className":74948},[],[74950],{"type":259,"value":73764},{"type":259,"value":74952},". That way, if the authentication method changes someday, ",{"type":254,"tag":389,"props":74954,"children":74956},{"className":74955},[],[74957],{"type":259,"value":73764},{"type":259,"value":74959}," implementation won't change. Moreover, we won't mess with the same ",{"type":254,"tag":389,"props":74961,"children":74963},{"className":74962},[],[74964],{"type":259,"value":1565},{"type":259,"value":74966}," for authentication and other calls.",{"type":254,"tag":612,"props":74968,"children":74970},{"className":1423,"code":74969,"language":1425,"meta":205,"style":205},"public class UserApiAuthenticationService : IUserApiAuthenticationService\n{\n    private readonly IMemoryCache _memoryCache;\n    private readonly HttpClient _httpClient;\n\n    public UserApiAuthenticationService(HttpClient httpClient, IMemoryCache memoryCache)\n    {\n        _httpClient = httpClient;\n        _memoryCache = memoryCache;\n    }\n\n    public async Task\u003Cstring> RetrieveToken()\n    {\n        DateTime expirationDate;\n        if (!_memoryCache.TryGetValue(\"Token\", out string token))\n        {\n            var body = new { login = \"login\", password = \"password\" };\n            var response = await _httpClient.PostAsync(\"login\", new StringContent(JsonConvert.SerializeObject(body)));\n            (token,  expirationDate) = await response.Content.ReadAsAsync\u003CAuthResponse>();\n            _memoryCache.Set(\"Token\", token, new DateTimeOffset(expirationDate));\n        }\n        return token;\n    }\n}\n",[74971],{"type":254,"tag":389,"props":74972,"children":74973},{"__ignoreMap":205},[74974,74999,75006,75031,75054,75061,75101,75108,75127,75147,75154,75161,75196,75203,75220,75281,75288,75359,75446,75512,75578,75585,75600,75607],{"type":254,"tag":622,"props":74975,"children":74976},{"class":624,"line":625},[74977,74981,74985,74990,74994],{"type":254,"tag":622,"props":74978,"children":74979},{"style":644},[74980],{"type":259,"value":1437},{"type":254,"tag":622,"props":74982,"children":74983},{"style":714},[74984],{"type":259,"value":6761},{"type":254,"tag":622,"props":74986,"children":74987},{"style":714},[74988],{"type":259,"value":74989}," UserApiAuthenticationService",{"type":254,"tag":622,"props":74991,"children":74992},{"style":629},[74993],{"type":259,"value":3274},{"type":254,"tag":622,"props":74995,"children":74996},{"style":714},[74997],{"type":259,"value":74998}," IUserApiAuthenticationService\n",{"type":254,"tag":622,"props":75000,"children":75001},{"class":624,"line":635},[75002],{"type":254,"tag":622,"props":75003,"children":75004},{"style":629},[75005],{"type":259,"value":632},{"type":254,"tag":622,"props":75007,"children":75008},{"class":624,"line":679},[75009,75013,75017,75022,75027],{"type":254,"tag":622,"props":75010,"children":75011},{"style":644},[75012],{"type":259,"value":6790},{"type":254,"tag":622,"props":75014,"children":75015},{"style":644},[75016],{"type":259,"value":6795},{"type":254,"tag":622,"props":75018,"children":75019},{"style":714},[75020],{"type":259,"value":75021}," IMemoryCache",{"type":254,"tag":622,"props":75023,"children":75024},{"style":714},[75025],{"type":259,"value":75026}," _memoryCache",{"type":254,"tag":622,"props":75028,"children":75029},{"style":629},[75030],{"type":259,"value":2350},{"type":254,"tag":622,"props":75032,"children":75033},{"class":624,"line":705},[75034,75038,75042,75046,75050],{"type":254,"tag":622,"props":75035,"children":75036},{"style":644},[75037],{"type":259,"value":6790},{"type":254,"tag":622,"props":75039,"children":75040},{"style":644},[75041],{"type":259,"value":6795},{"type":254,"tag":622,"props":75043,"children":75044},{"style":714},[75045],{"type":259,"value":1629},{"type":254,"tag":622,"props":75047,"children":75048},{"style":714},[75049],{"type":259,"value":73476},{"type":254,"tag":622,"props":75051,"children":75052},{"style":629},[75053],{"type":259,"value":2350},{"type":254,"tag":622,"props":75055,"children":75056},{"class":624,"line":732},[75057],{"type":254,"tag":622,"props":75058,"children":75059},{"emptyLinePlaceholder":1699},[75060],{"type":259,"value":1702},{"type":254,"tag":622,"props":75062,"children":75063},{"class":624,"line":759},[75064,75068,75072,75076,75080,75084,75088,75092,75097],{"type":254,"tag":622,"props":75065,"children":75066},{"style":644},[75067],{"type":259,"value":1970},{"type":254,"tag":622,"props":75069,"children":75070},{"style":1735},[75071],{"type":259,"value":74989},{"type":254,"tag":622,"props":75073,"children":75074},{"style":629},[75075],{"type":259,"value":1668},{"type":254,"tag":622,"props":75077,"children":75078},{"style":714},[75079],{"type":259,"value":1565},{"type":254,"tag":622,"props":75081,"children":75082},{"style":714},[75083],{"type":259,"value":1614},{"type":254,"tag":622,"props":75085,"children":75086},{"style":629},[75087],{"type":259,"value":3149},{"type":254,"tag":622,"props":75089,"children":75090},{"style":714},[75091],{"type":259,"value":75021},{"type":254,"tag":622,"props":75093,"children":75094},{"style":714},[75095],{"type":259,"value":75096}," memoryCache",{"type":254,"tag":622,"props":75098,"children":75099},{"style":629},[75100],{"type":259,"value":1685},{"type":254,"tag":622,"props":75102,"children":75103},{"class":624,"line":798},[75104],{"type":254,"tag":622,"props":75105,"children":75106},{"style":629},[75107],{"type":259,"value":3574},{"type":254,"tag":622,"props":75109,"children":75110},{"class":624,"line":836},[75111,75115,75119,75123],{"type":254,"tag":622,"props":75112,"children":75113},{"style":1202},[75114],{"type":259,"value":73882},{"type":254,"tag":622,"props":75116,"children":75117},{"style":629},[75118],{"type":259,"value":1654},{"type":254,"tag":622,"props":75120,"children":75121},{"style":1202},[75122],{"type":259,"value":1614},{"type":254,"tag":622,"props":75124,"children":75125},{"style":629},[75126],{"type":259,"value":2350},{"type":254,"tag":622,"props":75128,"children":75129},{"class":624,"line":862},[75130,75135,75139,75143],{"type":254,"tag":622,"props":75131,"children":75132},{"style":1202},[75133],{"type":259,"value":75134},"        _memoryCache ",{"type":254,"tag":622,"props":75136,"children":75137},{"style":629},[75138],{"type":259,"value":1654},{"type":254,"tag":622,"props":75140,"children":75141},{"style":1202},[75142],{"type":259,"value":75096},{"type":254,"tag":622,"props":75144,"children":75145},{"style":629},[75146],{"type":259,"value":2350},{"type":254,"tag":622,"props":75148,"children":75149},{"class":624,"line":900},[75150],{"type":254,"tag":622,"props":75151,"children":75152},{"style":629},[75153],{"type":259,"value":1093},{"type":254,"tag":622,"props":75155,"children":75156},{"class":624,"line":938},[75157],{"type":254,"tag":622,"props":75158,"children":75159},{"emptyLinePlaceholder":1699},[75160],{"type":259,"value":1702},{"type":254,"tag":622,"props":75162,"children":75163},{"class":624,"line":976},[75164,75168,75172,75176,75180,75184,75188,75192],{"type":254,"tag":622,"props":75165,"children":75166},{"style":644},[75167],{"type":259,"value":1970},{"type":254,"tag":622,"props":75169,"children":75170},{"style":644},[75171],{"type":259,"value":6987},{"type":254,"tag":622,"props":75173,"children":75174},{"style":714},[75175],{"type":259,"value":2842},{"type":254,"tag":622,"props":75177,"children":75178},{"style":629},[75179],{"type":259,"value":1743},{"type":254,"tag":622,"props":75181,"children":75182},{"style":629},[75183],{"type":259,"value":2851},{"type":254,"tag":622,"props":75185,"children":75186},{"style":629},[75187],{"type":259,"value":6813},{"type":254,"tag":622,"props":75189,"children":75190},{"style":1735},[75191],{"type":259,"value":73983},{"type":254,"tag":622,"props":75193,"children":75194},{"style":629},[75195],{"type":259,"value":1634},{"type":254,"tag":622,"props":75197,"children":75198},{"class":624,"line":1014},[75199],{"type":254,"tag":622,"props":75200,"children":75201},{"style":629},[75202],{"type":259,"value":3574},{"type":254,"tag":622,"props":75204,"children":75205},{"class":624,"line":1052},[75206,75211,75216],{"type":254,"tag":622,"props":75207,"children":75208},{"style":714},[75209],{"type":259,"value":75210},"        DateTime",{"type":254,"tag":622,"props":75212,"children":75213},{"style":714},[75214],{"type":259,"value":75215}," expirationDate",{"type":254,"tag":622,"props":75217,"children":75218},{"style":629},[75219],{"type":259,"value":2350},{"type":254,"tag":622,"props":75221,"children":75222},{"class":624,"line":1078},[75223,75227,75232,75237,75241,75245,75249,75253,75257,75261,75265,75269,75273,75277],{"type":254,"tag":622,"props":75224,"children":75225},{"style":1196},[75226],{"type":259,"value":54397},{"type":254,"tag":622,"props":75228,"children":75229},{"style":629},[75230],{"type":259,"value":75231}," (!",{"type":254,"tag":622,"props":75233,"children":75234},{"style":1202},[75235],{"type":259,"value":75236},"_memoryCache",{"type":254,"tag":622,"props":75238,"children":75239},{"style":629},[75240],{"type":259,"value":594},{"type":254,"tag":622,"props":75242,"children":75243},{"style":1735},[75244],{"type":259,"value":53250},{"type":254,"tag":622,"props":75246,"children":75247},{"style":629},[75248],{"type":259,"value":1668},{"type":254,"tag":622,"props":75250,"children":75251},{"style":629},[75252],{"type":259,"value":652},{"type":254,"tag":622,"props":75254,"children":75255},{"style":664},[75256],{"type":259,"value":74861},{"type":254,"tag":622,"props":75258,"children":75259},{"style":629},[75260],{"type":259,"value":652},{"type":254,"tag":622,"props":75262,"children":75263},{"style":629},[75264],{"type":259,"value":3149},{"type":254,"tag":622,"props":75266,"children":75267},{"style":644},[75268],{"type":259,"value":2295},{"type":254,"tag":622,"props":75270,"children":75271},{"style":629},[75272],{"type":259,"value":1975},{"type":254,"tag":622,"props":75274,"children":75275},{"style":714},[75276],{"type":259,"value":73970},{"type":254,"tag":622,"props":75278,"children":75279},{"style":629},[75280],{"type":259,"value":7086},{"type":254,"tag":622,"props":75282,"children":75283},{"class":624,"line":1087},[75284],{"type":254,"tag":622,"props":75285,"children":75286},{"style":629},[75287],{"type":259,"value":7094},{"type":254,"tag":622,"props":75289,"children":75290},{"class":624,"line":1096},[75291,75295,75299,75303,75307,75311,75315,75319,75323,75327,75331,75335,75339,75343,75347,75351,75355],{"type":254,"tag":622,"props":75292,"children":75293},{"style":714},[75294],{"type":259,"value":55059},{"type":254,"tag":622,"props":75296,"children":75297},{"style":714},[75298],{"type":259,"value":73390},{"type":254,"tag":622,"props":75300,"children":75301},{"style":629},[75302],{"type":259,"value":1619},{"type":254,"tag":622,"props":75304,"children":75305},{"style":629},[75306],{"type":259,"value":1624},{"type":254,"tag":622,"props":75308,"children":75309},{"style":629},[75310],{"type":259,"value":1990},{"type":254,"tag":622,"props":75312,"children":75313},{"style":1202},[75314],{"type":259,"value":73407},{"type":254,"tag":622,"props":75316,"children":75317},{"style":629},[75318],{"type":259,"value":1654},{"type":254,"tag":622,"props":75320,"children":75321},{"style":629},[75322],{"type":259,"value":661},{"type":254,"tag":622,"props":75324,"children":75325},{"style":664},[75326],{"type":259,"value":60610},{"type":254,"tag":622,"props":75328,"children":75329},{"style":629},[75330],{"type":259,"value":652},{"type":254,"tag":622,"props":75332,"children":75333},{"style":629},[75334],{"type":259,"value":3149},{"type":254,"tag":622,"props":75336,"children":75337},{"style":1202},[75338],{"type":259,"value":73432},{"type":254,"tag":622,"props":75340,"children":75341},{"style":629},[75342],{"type":259,"value":1654},{"type":254,"tag":622,"props":75344,"children":75345},{"style":629},[75346],{"type":259,"value":661},{"type":254,"tag":622,"props":75348,"children":75349},{"style":664},[75350],{"type":259,"value":60635},{"type":254,"tag":622,"props":75352,"children":75353},{"style":629},[75354],{"type":259,"value":652},{"type":254,"tag":622,"props":75356,"children":75357},{"style":629},[75358],{"type":259,"value":13619},{"type":254,"tag":622,"props":75360,"children":75361},{"class":624,"line":1105},[75362,75366,75370,75374,75378,75382,75386,75390,75394,75398,75402,75406,75410,75414,75418,75422,75426,75430,75434,75438,75442],{"type":254,"tag":622,"props":75363,"children":75364},{"style":714},[75365],{"type":259,"value":55059},{"type":254,"tag":622,"props":75367,"children":75368},{"style":714},[75369],{"type":259,"value":3587},{"type":254,"tag":622,"props":75371,"children":75372},{"style":629},[75373],{"type":259,"value":1619},{"type":254,"tag":622,"props":75375,"children":75376},{"style":629},[75377],{"type":259,"value":1724},{"type":254,"tag":622,"props":75379,"children":75380},{"style":1202},[75381],{"type":259,"value":73476},{"type":254,"tag":622,"props":75383,"children":75384},{"style":629},[75385],{"type":259,"value":594},{"type":254,"tag":622,"props":75387,"children":75388},{"style":1735},[75389],{"type":259,"value":36861},{"type":254,"tag":622,"props":75391,"children":75392},{"style":629},[75393],{"type":259,"value":1668},{"type":254,"tag":622,"props":75395,"children":75396},{"style":629},[75397],{"type":259,"value":652},{"type":254,"tag":622,"props":75399,"children":75400},{"style":664},[75401],{"type":259,"value":60610},{"type":254,"tag":622,"props":75403,"children":75404},{"style":629},[75405],{"type":259,"value":652},{"type":254,"tag":622,"props":75407,"children":75408},{"style":629},[75409],{"type":259,"value":3149},{"type":254,"tag":622,"props":75411,"children":75412},{"style":629},[75413],{"type":259,"value":1624},{"type":254,"tag":622,"props":75415,"children":75416},{"style":714},[75417],{"type":259,"value":73513},{"type":254,"tag":622,"props":75419,"children":75420},{"style":629},[75421],{"type":259,"value":1668},{"type":254,"tag":622,"props":75423,"children":75424},{"style":1202},[75425],{"type":259,"value":73522},{"type":254,"tag":622,"props":75427,"children":75428},{"style":629},[75429],{"type":259,"value":594},{"type":254,"tag":622,"props":75431,"children":75432},{"style":1735},[75433],{"type":259,"value":73531},{"type":254,"tag":622,"props":75435,"children":75436},{"style":629},[75437],{"type":259,"value":1668},{"type":254,"tag":622,"props":75439,"children":75440},{"style":1202},[75441],{"type":259,"value":73540},{"type":254,"tag":622,"props":75443,"children":75444},{"style":629},[75445],{"type":259,"value":73545},{"type":254,"tag":622,"props":75447,"children":75448},{"class":624,"line":4131},[75449,75454,75459,75463,75468,75472,75476,75480,75484,75488,75492,75496,75500,75504,75508],{"type":254,"tag":622,"props":75450,"children":75451},{"style":629},[75452],{"type":259,"value":75453},"            (",{"type":254,"tag":622,"props":75455,"children":75456},{"style":1202},[75457],{"type":259,"value":75458},"token",{"type":254,"tag":622,"props":75460,"children":75461},{"style":629},[75462],{"type":259,"value":3149},{"type":254,"tag":622,"props":75464,"children":75465},{"style":1202},[75466],{"type":259,"value":75467},"  expirationDate",{"type":254,"tag":622,"props":75469,"children":75470},{"style":629},[75471],{"type":259,"value":2309},{"type":254,"tag":622,"props":75473,"children":75474},{"style":629},[75475],{"type":259,"value":1619},{"type":254,"tag":622,"props":75477,"children":75478},{"style":629},[75479],{"type":259,"value":1724},{"type":254,"tag":622,"props":75481,"children":75482},{"style":1202},[75483],{"type":259,"value":3587},{"type":254,"tag":622,"props":75485,"children":75486},{"style":629},[75487],{"type":259,"value":594},{"type":254,"tag":622,"props":75489,"children":75490},{"style":1202},[75491],{"type":259,"value":73578},{"type":254,"tag":622,"props":75493,"children":75494},{"style":629},[75495],{"type":259,"value":594},{"type":254,"tag":622,"props":75497,"children":75498},{"style":1735},[75499],{"type":259,"value":73587},{"type":254,"tag":622,"props":75501,"children":75502},{"style":629},[75503],{"type":259,"value":1743},{"type":254,"tag":622,"props":75505,"children":75506},{"style":714},[75507],{"type":259,"value":73191},{"type":254,"tag":622,"props":75509,"children":75510},{"style":629},[75511],{"type":259,"value":6531},{"type":254,"tag":622,"props":75513,"children":75514},{"class":624,"line":4183},[75515,75520,75524,75529,75533,75537,75541,75545,75549,75553,75557,75561,75565,75569,75574],{"type":254,"tag":622,"props":75516,"children":75517},{"style":1202},[75518],{"type":259,"value":75519},"            _memoryCache",{"type":254,"tag":622,"props":75521,"children":75522},{"style":629},[75523],{"type":259,"value":594},{"type":254,"tag":622,"props":75525,"children":75526},{"style":1735},[75527],{"type":259,"value":75528},"Set",{"type":254,"tag":622,"props":75530,"children":75531},{"style":629},[75532],{"type":259,"value":1668},{"type":254,"tag":622,"props":75534,"children":75535},{"style":629},[75536],{"type":259,"value":652},{"type":254,"tag":622,"props":75538,"children":75539},{"style":664},[75540],{"type":259,"value":74861},{"type":254,"tag":622,"props":75542,"children":75543},{"style":629},[75544],{"type":259,"value":652},{"type":254,"tag":622,"props":75546,"children":75547},{"style":629},[75548],{"type":259,"value":3149},{"type":254,"tag":622,"props":75550,"children":75551},{"style":1202},[75552],{"type":259,"value":73970},{"type":254,"tag":622,"props":75554,"children":75555},{"style":629},[75556],{"type":259,"value":3149},{"type":254,"tag":622,"props":75558,"children":75559},{"style":629},[75560],{"type":259,"value":1624},{"type":254,"tag":622,"props":75562,"children":75563},{"style":714},[75564],{"type":259,"value":2113},{"type":254,"tag":622,"props":75566,"children":75567},{"style":629},[75568],{"type":259,"value":1668},{"type":254,"tag":622,"props":75570,"children":75571},{"style":1202},[75572],{"type":259,"value":75573},"expirationDate",{"type":254,"tag":622,"props":75575,"children":75576},{"style":629},[75577],{"type":259,"value":6624},{"type":254,"tag":622,"props":75579,"children":75580},{"class":624,"line":4208},[75581],{"type":254,"tag":622,"props":75582,"children":75583},{"style":629},[75584],{"type":259,"value":7144},{"type":254,"tag":622,"props":75586,"children":75587},{"class":624,"line":4216},[75588,75592,75596],{"type":254,"tag":622,"props":75589,"children":75590},{"style":1196},[75591],{"type":259,"value":9046},{"type":254,"tag":622,"props":75593,"children":75594},{"style":1202},[75595],{"type":259,"value":73970},{"type":254,"tag":622,"props":75597,"children":75598},{"style":629},[75599],{"type":259,"value":2350},{"type":254,"tag":622,"props":75601,"children":75602},{"class":624,"line":4224},[75603],{"type":254,"tag":622,"props":75604,"children":75605},{"style":629},[75606],{"type":259,"value":1093},{"type":254,"tag":622,"props":75608,"children":75609},{"class":624,"line":4268},[75610],{"type":254,"tag":622,"props":75611,"children":75612},{"style":629},[75613],{"type":259,"value":1111},{"type":254,"tag":255,"props":75615,"children":75616},{},[75617,75619,75625,75627,75633],{"type":259,"value":75618},"To avoid requesting always the same token to the API, we added a line to store the token in the memory cache and a line to check if the token is already in the cache before querying the API.\nWe could also have used a class as a singleton to store the token and its expiration date, but the built-in ",{"type":254,"tag":389,"props":75620,"children":75622},{"className":75621},[],[75623],{"type":259,"value":75624},"IMemoryCache",{"type":259,"value":75626}," of ASP.NET Core is more convenient and handle the expiration of the token for us by removing it from the cache when the date is passed. You can find more about cache memory in ASP.NET Core ",{"type":254,"tag":262,"props":75628,"children":75631},{"href":75629,"rel":75630},"https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-3.1",[266],[75632],{"type":259,"value":1414},{"type":259,"value":594},{"type":254,"tag":282,"props":75635,"children":75637},{"id":75636},"use-a-delegating-handler-to-directly-set-the-token-in-the-httpclient-request",[75638],{"type":259,"value":75639},"Use a Delegating handler to directly set the token in the HttpClient request",{"type":254,"tag":255,"props":75641,"children":75642},{},[75643,75645,75650,75652,75657,75659,75664,75666,75671,75673,75678],{"type":259,"value":75644},"Handling the token retrieval in a separate service is nice but that does not solve the issue of duplicated code. Even if the ",{"type":254,"tag":389,"props":75646,"children":75648},{"className":75647},[],[75649],{"type":259,"value":74898},{"type":259,"value":75651}," method is now part of ",{"type":254,"tag":389,"props":75653,"children":75655},{"className":75654},[],[75656],{"type":259,"value":74943},{"type":259,"value":75658},", each method of ",{"type":254,"tag":389,"props":75660,"children":75662},{"className":75661},[],[75663],{"type":259,"value":73764},{"type":259,"value":75665}," will still call ",{"type":254,"tag":389,"props":75667,"children":75669},{"className":75668},[],[75670],{"type":259,"value":74898},{"type":259,"value":75672},". Moreover setting the token on each request should not be a concern of ",{"type":254,"tag":389,"props":75674,"children":75676},{"className":75675},[],[75677],{"type":259,"value":73764},{"type":259,"value":594},{"type":254,"tag":255,"props":75680,"children":75681},{},[75682,75684,75689,75691,75696,75698,75702,75704,75710],{"type":259,"value":75683},"That's where come ",{"type":254,"tag":19837,"props":75685,"children":75686},{},[75687],{"type":259,"value":75688},"delegating handlers",{"type":259,"value":75690},". A delegating handler is quite similar to an ASP.NET Core middleware but instead of applying some processing on an incoming request and its response, it does so on an outgoing request and its response. In concrete terms, you use a delegating handler to apply something (logging, authentication, caching ...) to HTTP requests you make to an API using an ",{"type":254,"tag":389,"props":75692,"children":75694},{"className":75693},[],[75695],{"type":259,"value":1565},{"type":259,"value":75697},". To learn more about ",{"type":254,"tag":19837,"props":75699,"children":75700},{},[75701],{"type":259,"value":75688},{"type":259,"value":75703}," there is a nice ",{"type":254,"tag":262,"props":75705,"children":75708},{"href":75706,"rel":75707},"https://www.stevejgordon.co.uk/httpclientfactory-aspnetcore-outgoing-request-middleware-pipeline-delegatinghandlers",[266],[75709],{"type":259,"value":36966},{"type":259,"value":75711}," from Steve Gordon on the topic.",{"type":254,"tag":255,"props":75713,"children":75714},{},[75715,75717,75722],{"type":259,"value":75716},"A custom delegating handler is exactly what we need: a piece of code that all our HTTP requests from ",{"type":254,"tag":389,"props":75718,"children":75720},{"className":75719},[],[75721],{"type":259,"value":73764},{"type":259,"value":75723}," will go through and where we will be able to set the token on the authentication header of each request. Here is the code of our custom delegating handler:",{"type":254,"tag":612,"props":75725,"children":75727},{"className":1423,"code":75726,"language":1425,"meta":205,"style":205},"public class UserApiAuthenticationHandler : DelegatingHandler\n{\n    private readonly IUserApiAuthenticationService _authenticationService;\n\n    public UserApiAuthenticationHandler(IUserApiAuthenticationService authenticationService)\n    {\n        _authenticationService = authenticationService;\n    }\n    \n    protected async override Task\u003CHttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationTokencancellationToken)\n    {\n        var token = await _authenticationService.RetrieveToken();\n        request.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", token);\n        return await base.SendAsync(request, cancellationToken);\n    }\n}\n",[75728],{"type":254,"tag":389,"props":75729,"children":75730},{"__ignoreMap":205},[75731,75756,75763,75788,75795,75824,75831,75851,75858,75865,75927,75934,75969,76032,76077,76084],{"type":254,"tag":622,"props":75732,"children":75733},{"class":624,"line":625},[75734,75738,75742,75747,75751],{"type":254,"tag":622,"props":75735,"children":75736},{"style":644},[75737],{"type":259,"value":1437},{"type":254,"tag":622,"props":75739,"children":75740},{"style":714},[75741],{"type":259,"value":6761},{"type":254,"tag":622,"props":75743,"children":75744},{"style":714},[75745],{"type":259,"value":75746}," UserApiAuthenticationHandler",{"type":254,"tag":622,"props":75748,"children":75749},{"style":629},[75750],{"type":259,"value":3274},{"type":254,"tag":622,"props":75752,"children":75753},{"style":714},[75754],{"type":259,"value":75755}," DelegatingHandler\n",{"type":254,"tag":622,"props":75757,"children":75758},{"class":624,"line":635},[75759],{"type":254,"tag":622,"props":75760,"children":75761},{"style":629},[75762],{"type":259,"value":632},{"type":254,"tag":622,"props":75764,"children":75765},{"class":624,"line":679},[75766,75770,75774,75779,75784],{"type":254,"tag":622,"props":75767,"children":75768},{"style":644},[75769],{"type":259,"value":6790},{"type":254,"tag":622,"props":75771,"children":75772},{"style":644},[75773],{"type":259,"value":6795},{"type":254,"tag":622,"props":75775,"children":75776},{"style":714},[75777],{"type":259,"value":75778}," IUserApiAuthenticationService",{"type":254,"tag":622,"props":75780,"children":75781},{"style":714},[75782],{"type":259,"value":75783}," _authenticationService",{"type":254,"tag":622,"props":75785,"children":75786},{"style":629},[75787],{"type":259,"value":2350},{"type":254,"tag":622,"props":75789,"children":75790},{"class":624,"line":705},[75791],{"type":254,"tag":622,"props":75792,"children":75793},{"emptyLinePlaceholder":1699},[75794],{"type":259,"value":1702},{"type":254,"tag":622,"props":75796,"children":75797},{"class":624,"line":732},[75798,75802,75806,75810,75815,75820],{"type":254,"tag":622,"props":75799,"children":75800},{"style":644},[75801],{"type":259,"value":1970},{"type":254,"tag":622,"props":75803,"children":75804},{"style":1735},[75805],{"type":259,"value":75746},{"type":254,"tag":622,"props":75807,"children":75808},{"style":629},[75809],{"type":259,"value":1668},{"type":254,"tag":622,"props":75811,"children":75812},{"style":714},[75813],{"type":259,"value":75814},"IUserApiAuthenticationService",{"type":254,"tag":622,"props":75816,"children":75817},{"style":714},[75818],{"type":259,"value":75819}," authenticationService",{"type":254,"tag":622,"props":75821,"children":75822},{"style":629},[75823],{"type":259,"value":1685},{"type":254,"tag":622,"props":75825,"children":75826},{"class":624,"line":759},[75827],{"type":254,"tag":622,"props":75828,"children":75829},{"style":629},[75830],{"type":259,"value":3574},{"type":254,"tag":622,"props":75832,"children":75833},{"class":624,"line":798},[75834,75839,75843,75847],{"type":254,"tag":622,"props":75835,"children":75836},{"style":1202},[75837],{"type":259,"value":75838},"        _authenticationService ",{"type":254,"tag":622,"props":75840,"children":75841},{"style":629},[75842],{"type":259,"value":1654},{"type":254,"tag":622,"props":75844,"children":75845},{"style":1202},[75846],{"type":259,"value":75819},{"type":254,"tag":622,"props":75848,"children":75849},{"style":629},[75850],{"type":259,"value":2350},{"type":254,"tag":622,"props":75852,"children":75853},{"class":624,"line":836},[75854],{"type":254,"tag":622,"props":75855,"children":75856},{"style":629},[75857],{"type":259,"value":1093},{"type":254,"tag":622,"props":75859,"children":75860},{"class":624,"line":862},[75861],{"type":254,"tag":622,"props":75862,"children":75863},{"style":1202},[75864],{"type":259,"value":7336},{"type":254,"tag":622,"props":75866,"children":75867},{"class":624,"line":900},[75868,75872,75876,75880,75884,75888,75893,75897,75902,75906,75910,75914,75918,75923],{"type":254,"tag":622,"props":75869,"children":75870},{"style":644},[75871],{"type":259,"value":6977},{"type":254,"tag":622,"props":75873,"children":75874},{"style":644},[75875],{"type":259,"value":6987},{"type":254,"tag":622,"props":75877,"children":75878},{"style":644},[75879],{"type":259,"value":6982},{"type":254,"tag":622,"props":75881,"children":75882},{"style":714},[75883],{"type":259,"value":2842},{"type":254,"tag":622,"props":75885,"children":75886},{"style":629},[75887],{"type":259,"value":1743},{"type":254,"tag":622,"props":75889,"children":75890},{"style":714},[75891],{"type":259,"value":75892},"HttpResponseMessage",{"type":254,"tag":622,"props":75894,"children":75895},{"style":629},[75896],{"type":259,"value":6813},{"type":254,"tag":622,"props":75898,"children":75899},{"style":1735},[75900],{"type":259,"value":75901}," SendAsync",{"type":254,"tag":622,"props":75903,"children":75904},{"style":629},[75905],{"type":259,"value":1668},{"type":254,"tag":622,"props":75907,"children":75908},{"style":714},[75909],{"type":259,"value":64659},{"type":254,"tag":622,"props":75911,"children":75912},{"style":714},[75913],{"type":259,"value":73999},{"type":254,"tag":622,"props":75915,"children":75916},{"style":629},[75917],{"type":259,"value":3149},{"type":254,"tag":622,"props":75919,"children":75920},{"style":1202},[75921],{"type":259,"value":75922}," CancellationTokencancellationToken",{"type":254,"tag":622,"props":75924,"children":75925},{"style":629},[75926],{"type":259,"value":1685},{"type":254,"tag":622,"props":75928,"children":75929},{"class":624,"line":938},[75930],{"type":254,"tag":622,"props":75931,"children":75932},{"style":629},[75933],{"type":259,"value":3574},{"type":254,"tag":622,"props":75935,"children":75936},{"class":624,"line":976},[75937,75941,75945,75949,75953,75957,75961,75965],{"type":254,"tag":622,"props":75938,"children":75939},{"style":714},[75940],{"type":259,"value":3582},{"type":254,"tag":622,"props":75942,"children":75943},{"style":714},[75944],{"type":259,"value":73970},{"type":254,"tag":622,"props":75946,"children":75947},{"style":629},[75948],{"type":259,"value":1619},{"type":254,"tag":622,"props":75950,"children":75951},{"style":629},[75952],{"type":259,"value":1724},{"type":254,"tag":622,"props":75954,"children":75955},{"style":1202},[75956],{"type":259,"value":75783},{"type":254,"tag":622,"props":75958,"children":75959},{"style":629},[75960],{"type":259,"value":594},{"type":254,"tag":622,"props":75962,"children":75963},{"style":1735},[75964],{"type":259,"value":74898},{"type":254,"tag":622,"props":75966,"children":75967},{"style":629},[75968],{"type":259,"value":2798},{"type":254,"tag":622,"props":75970,"children":75971},{"class":624,"line":1014},[75972,75976,75980,75984,75988,75992,75996,76000,76004,76008,76012,76016,76020,76024,76028],{"type":254,"tag":622,"props":75973,"children":75974},{"style":1202},[75975],{"type":259,"value":74057},{"type":254,"tag":622,"props":75977,"children":75978},{"style":629},[75979],{"type":259,"value":594},{"type":254,"tag":622,"props":75981,"children":75982},{"style":1202},[75983],{"type":259,"value":74066},{"type":254,"tag":622,"props":75985,"children":75986},{"style":629},[75987],{"type":259,"value":594},{"type":254,"tag":622,"props":75989,"children":75990},{"style":1202},[75991],{"type":259,"value":36796},{"type":254,"tag":622,"props":75993,"children":75994},{"style":629},[75995],{"type":259,"value":1654},{"type":254,"tag":622,"props":75997,"children":75998},{"style":629},[75999],{"type":259,"value":1624},{"type":254,"tag":622,"props":76001,"children":76002},{"style":714},[76003],{"type":259,"value":36809},{"type":254,"tag":622,"props":76005,"children":76006},{"style":629},[76007],{"type":259,"value":1668},{"type":254,"tag":622,"props":76009,"children":76010},{"style":629},[76011],{"type":259,"value":652},{"type":254,"tag":622,"props":76013,"children":76014},{"style":664},[76015],{"type":259,"value":74099},{"type":254,"tag":622,"props":76017,"children":76018},{"style":629},[76019],{"type":259,"value":652},{"type":254,"tag":622,"props":76021,"children":76022},{"style":629},[76023],{"type":259,"value":3149},{"type":254,"tag":622,"props":76025,"children":76026},{"style":1202},[76027],{"type":259,"value":73970},{"type":254,"tag":622,"props":76029,"children":76030},{"style":629},[76031],{"type":259,"value":1554},{"type":254,"tag":622,"props":76033,"children":76034},{"class":624,"line":1052},[76035,76039,76043,76048,76052,76056,76060,76064,76068,76073],{"type":254,"tag":622,"props":76036,"children":76037},{"style":1196},[76038],{"type":259,"value":9046},{"type":254,"tag":622,"props":76040,"children":76041},{"style":629},[76042],{"type":259,"value":1724},{"type":254,"tag":622,"props":76044,"children":76045},{"style":1202},[76046],{"type":259,"value":76047}," base",{"type":254,"tag":622,"props":76049,"children":76050},{"style":629},[76051],{"type":259,"value":594},{"type":254,"tag":622,"props":76053,"children":76054},{"style":1735},[76055],{"type":259,"value":74147},{"type":254,"tag":622,"props":76057,"children":76058},{"style":629},[76059],{"type":259,"value":1668},{"type":254,"tag":622,"props":76061,"children":76062},{"style":1202},[76063],{"type":259,"value":74156},{"type":254,"tag":622,"props":76065,"children":76066},{"style":629},[76067],{"type":259,"value":3149},{"type":254,"tag":622,"props":76069,"children":76070},{"style":1202},[76071],{"type":259,"value":76072}," cancellationToken",{"type":254,"tag":622,"props":76074,"children":76075},{"style":629},[76076],{"type":259,"value":1554},{"type":254,"tag":622,"props":76078,"children":76079},{"class":624,"line":1078},[76080],{"type":254,"tag":622,"props":76081,"children":76082},{"style":629},[76083],{"type":259,"value":1093},{"type":254,"tag":622,"props":76085,"children":76086},{"class":624,"line":1087},[76087],{"type":254,"tag":622,"props":76088,"children":76089},{"style":629},[76090],{"type":259,"value":1111},{"type":254,"tag":255,"props":76092,"children":76093},{},[76094],{"type":259,"value":76095},"That's it, we don't need anymore to handle token retrieval on UserService which becomes simpler:",{"type":254,"tag":612,"props":76097,"children":76099},{"className":1423,"code":76098,"language":1425,"meta":205,"style":205},"public class UserService : \n{\n    private readonly HttpClient _httpClient;\n    public UserService(HttpClient httpClient)\n    {\n        _httpClient = httpClient;\n    }\n\n    public async Task\u003CIReadOnlyCollection\u003CUser>> GetAllUsers()\n    {\n        var response = await _httpClient.GetAsync(new Uri(\"user\"));\n        response.EnsureSuccessStatusCode();\n        return await response.Content.ReadAsAsync\u003CIReadOnlyCollection\u003CUser>>();\n    }\n\n    public async Task UpdateUser(User userToUpdate)\n    {\n        var content = new StringContent(JsonConvert.SerializeObject(userToUpdate));\n        var response = await _httpClient.PutAsync($\"user/{userToUpdate.Name}\", content);\n        response.EnsureSuccessStatusCode();\n    }\n}\n",[76100],{"type":254,"tag":389,"props":76101,"children":76102},{"__ignoreMap":205},[76103,76126,76133,76156,76183,76190,76209,76216,76223,76266,76273,76333,76352,76403,76410,76417,76452,76459,76511,76587,76606,76613],{"type":254,"tag":622,"props":76104,"children":76105},{"class":624,"line":625},[76106,76110,76114,76118,76122],{"type":254,"tag":622,"props":76107,"children":76108},{"style":644},[76109],{"type":259,"value":1437},{"type":254,"tag":622,"props":76111,"children":76112},{"style":714},[76113],{"type":259,"value":6761},{"type":254,"tag":622,"props":76115,"children":76116},{"style":714},[76117],{"type":259,"value":73802},{"type":254,"tag":622,"props":76119,"children":76120},{"style":629},[76121],{"type":259,"value":3274},{"type":254,"tag":622,"props":76123,"children":76124},{"style":1202},[76125],{"type":259,"value":2314},{"type":254,"tag":622,"props":76127,"children":76128},{"class":624,"line":635},[76129],{"type":254,"tag":622,"props":76130,"children":76131},{"style":629},[76132],{"type":259,"value":632},{"type":254,"tag":622,"props":76134,"children":76135},{"class":624,"line":679},[76136,76140,76144,76148,76152],{"type":254,"tag":622,"props":76137,"children":76138},{"style":644},[76139],{"type":259,"value":6790},{"type":254,"tag":622,"props":76141,"children":76142},{"style":644},[76143],{"type":259,"value":6795},{"type":254,"tag":622,"props":76145,"children":76146},{"style":714},[76147],{"type":259,"value":1629},{"type":254,"tag":622,"props":76149,"children":76150},{"style":714},[76151],{"type":259,"value":73476},{"type":254,"tag":622,"props":76153,"children":76154},{"style":629},[76155],{"type":259,"value":2350},{"type":254,"tag":622,"props":76157,"children":76158},{"class":624,"line":705},[76159,76163,76167,76171,76175,76179],{"type":254,"tag":622,"props":76160,"children":76161},{"style":644},[76162],{"type":259,"value":1970},{"type":254,"tag":622,"props":76164,"children":76165},{"style":1735},[76166],{"type":259,"value":73802},{"type":254,"tag":622,"props":76168,"children":76169},{"style":629},[76170],{"type":259,"value":1668},{"type":254,"tag":622,"props":76172,"children":76173},{"style":714},[76174],{"type":259,"value":1565},{"type":254,"tag":622,"props":76176,"children":76177},{"style":714},[76178],{"type":259,"value":1614},{"type":254,"tag":622,"props":76180,"children":76181},{"style":629},[76182],{"type":259,"value":1685},{"type":254,"tag":622,"props":76184,"children":76185},{"class":624,"line":732},[76186],{"type":254,"tag":622,"props":76187,"children":76188},{"style":629},[76189],{"type":259,"value":3574},{"type":254,"tag":622,"props":76191,"children":76192},{"class":624,"line":759},[76193,76197,76201,76205],{"type":254,"tag":622,"props":76194,"children":76195},{"style":1202},[76196],{"type":259,"value":73882},{"type":254,"tag":622,"props":76198,"children":76199},{"style":629},[76200],{"type":259,"value":1654},{"type":254,"tag":622,"props":76202,"children":76203},{"style":1202},[76204],{"type":259,"value":1614},{"type":254,"tag":622,"props":76206,"children":76207},{"style":629},[76208],{"type":259,"value":2350},{"type":254,"tag":622,"props":76210,"children":76211},{"class":624,"line":798},[76212],{"type":254,"tag":622,"props":76213,"children":76214},{"style":629},[76215],{"type":259,"value":1093},{"type":254,"tag":622,"props":76217,"children":76218},{"class":624,"line":836},[76219],{"type":254,"tag":622,"props":76220,"children":76221},{"emptyLinePlaceholder":1699},[76222],{"type":259,"value":1702},{"type":254,"tag":622,"props":76224,"children":76225},{"class":624,"line":862},[76226,76230,76234,76238,76242,76246,76250,76254,76258,76262],{"type":254,"tag":622,"props":76227,"children":76228},{"style":644},[76229],{"type":259,"value":1970},{"type":254,"tag":622,"props":76231,"children":76232},{"style":644},[76233],{"type":259,"value":6987},{"type":254,"tag":622,"props":76235,"children":76236},{"style":714},[76237],{"type":259,"value":2842},{"type":254,"tag":622,"props":76239,"children":76240},{"style":629},[76241],{"type":259,"value":1743},{"type":254,"tag":622,"props":76243,"children":76244},{"style":714},[76245],{"type":259,"value":73296},{"type":254,"tag":622,"props":76247,"children":76248},{"style":629},[76249],{"type":259,"value":1743},{"type":254,"tag":622,"props":76251,"children":76252},{"style":714},[76253],{"type":259,"value":44521},{"type":254,"tag":622,"props":76255,"children":76256},{"style":629},[76257],{"type":259,"value":73309},{"type":254,"tag":622,"props":76259,"children":76260},{"style":1735},[76261],{"type":259,"value":73314},{"type":254,"tag":622,"props":76263,"children":76264},{"style":629},[76265],{"type":259,"value":1634},{"type":254,"tag":622,"props":76267,"children":76268},{"class":624,"line":900},[76269],{"type":254,"tag":622,"props":76270,"children":76271},{"style":629},[76272],{"type":259,"value":3574},{"type":254,"tag":622,"props":76274,"children":76275},{"class":624,"line":938},[76276,76280,76284,76288,76292,76296,76300,76305,76309,76313,76317,76321,76325,76329],{"type":254,"tag":622,"props":76277,"children":76278},{"style":714},[76279],{"type":259,"value":3582},{"type":254,"tag":622,"props":76281,"children":76282},{"style":714},[76283],{"type":259,"value":3587},{"type":254,"tag":622,"props":76285,"children":76286},{"style":629},[76287],{"type":259,"value":1619},{"type":254,"tag":622,"props":76289,"children":76290},{"style":629},[76291],{"type":259,"value":1724},{"type":254,"tag":622,"props":76293,"children":76294},{"style":1202},[76295],{"type":259,"value":73476},{"type":254,"tag":622,"props":76297,"children":76298},{"style":629},[76299],{"type":259,"value":594},{"type":254,"tag":622,"props":76301,"children":76302},{"style":1735},[76303],{"type":259,"value":76304},"GetAsync",{"type":254,"tag":622,"props":76306,"children":76307},{"style":629},[76308],{"type":259,"value":6598},{"type":254,"tag":622,"props":76310,"children":76311},{"style":714},[76312],{"type":259,"value":1663},{"type":254,"tag":622,"props":76314,"children":76315},{"style":629},[76316],{"type":259,"value":1668},{"type":254,"tag":622,"props":76318,"children":76319},{"style":629},[76320],{"type":259,"value":652},{"type":254,"tag":622,"props":76322,"children":76323},{"style":664},[76324],{"type":259,"value":65323},{"type":254,"tag":622,"props":76326,"children":76327},{"style":629},[76328],{"type":259,"value":652},{"type":254,"tag":622,"props":76330,"children":76331},{"style":629},[76332],{"type":259,"value":6624},{"type":254,"tag":622,"props":76334,"children":76335},{"class":624,"line":976},[76336,76340,76344,76348],{"type":254,"tag":622,"props":76337,"children":76338},{"style":1202},[76339],{"type":259,"value":74168},{"type":254,"tag":622,"props":76341,"children":76342},{"style":629},[76343],{"type":259,"value":594},{"type":254,"tag":622,"props":76345,"children":76346},{"style":1735},[76347],{"type":259,"value":74177},{"type":254,"tag":622,"props":76349,"children":76350},{"style":629},[76351],{"type":259,"value":2798},{"type":254,"tag":622,"props":76353,"children":76354},{"class":624,"line":1014},[76355,76359,76363,76367,76371,76375,76379,76383,76387,76391,76395,76399],{"type":254,"tag":622,"props":76356,"children":76357},{"style":1196},[76358],{"type":259,"value":9046},{"type":254,"tag":622,"props":76360,"children":76361},{"style":629},[76362],{"type":259,"value":1724},{"type":254,"tag":622,"props":76364,"children":76365},{"style":1202},[76366],{"type":259,"value":3587},{"type":254,"tag":622,"props":76368,"children":76369},{"style":629},[76370],{"type":259,"value":594},{"type":254,"tag":622,"props":76372,"children":76373},{"style":1202},[76374],{"type":259,"value":73578},{"type":254,"tag":622,"props":76376,"children":76377},{"style":629},[76378],{"type":259,"value":594},{"type":254,"tag":622,"props":76380,"children":76381},{"style":1735},[76382],{"type":259,"value":73587},{"type":254,"tag":622,"props":76384,"children":76385},{"style":629},[76386],{"type":259,"value":1743},{"type":254,"tag":622,"props":76388,"children":76389},{"style":714},[76390],{"type":259,"value":73296},{"type":254,"tag":622,"props":76392,"children":76393},{"style":629},[76394],{"type":259,"value":1743},{"type":254,"tag":622,"props":76396,"children":76397},{"style":714},[76398],{"type":259,"value":44521},{"type":254,"tag":622,"props":76400,"children":76401},{"style":629},[76402],{"type":259,"value":74233},{"type":254,"tag":622,"props":76404,"children":76405},{"class":624,"line":1052},[76406],{"type":254,"tag":622,"props":76407,"children":76408},{"style":629},[76409],{"type":259,"value":1093},{"type":254,"tag":622,"props":76411,"children":76412},{"class":624,"line":1078},[76413],{"type":254,"tag":622,"props":76414,"children":76415},{"emptyLinePlaceholder":1699},[76416],{"type":259,"value":1702},{"type":254,"tag":622,"props":76418,"children":76419},{"class":624,"line":1087},[76420,76424,76428,76432,76436,76440,76444,76448],{"type":254,"tag":622,"props":76421,"children":76422},{"style":644},[76423],{"type":259,"value":1970},{"type":254,"tag":622,"props":76425,"children":76426},{"style":644},[76427],{"type":259,"value":6987},{"type":254,"tag":622,"props":76429,"children":76430},{"style":714},[76431],{"type":259,"value":2842},{"type":254,"tag":622,"props":76433,"children":76434},{"style":1735},[76435],{"type":259,"value":73330},{"type":254,"tag":622,"props":76437,"children":76438},{"style":629},[76439],{"type":259,"value":1668},{"type":254,"tag":622,"props":76441,"children":76442},{"style":714},[76443],{"type":259,"value":44521},{"type":254,"tag":622,"props":76445,"children":76446},{"style":714},[76447],{"type":259,"value":73343},{"type":254,"tag":622,"props":76449,"children":76450},{"style":629},[76451],{"type":259,"value":1685},{"type":254,"tag":622,"props":76453,"children":76454},{"class":624,"line":1096},[76455],{"type":254,"tag":622,"props":76456,"children":76457},{"style":629},[76458],{"type":259,"value":3574},{"type":254,"tag":622,"props":76460,"children":76461},{"class":624,"line":1105},[76462,76466,76471,76475,76479,76483,76487,76491,76495,76499,76503,76507],{"type":254,"tag":622,"props":76463,"children":76464},{"style":714},[76465],{"type":259,"value":3582},{"type":254,"tag":622,"props":76467,"children":76468},{"style":714},[76469],{"type":259,"value":76470}," content",{"type":254,"tag":622,"props":76472,"children":76473},{"style":629},[76474],{"type":259,"value":1619},{"type":254,"tag":622,"props":76476,"children":76477},{"style":629},[76478],{"type":259,"value":1624},{"type":254,"tag":622,"props":76480,"children":76481},{"style":714},[76482],{"type":259,"value":73513},{"type":254,"tag":622,"props":76484,"children":76485},{"style":629},[76486],{"type":259,"value":1668},{"type":254,"tag":622,"props":76488,"children":76489},{"style":1202},[76490],{"type":259,"value":73522},{"type":254,"tag":622,"props":76492,"children":76493},{"style":629},[76494],{"type":259,"value":594},{"type":254,"tag":622,"props":76496,"children":76497},{"style":1735},[76498],{"type":259,"value":73531},{"type":254,"tag":622,"props":76500,"children":76501},{"style":629},[76502],{"type":259,"value":1668},{"type":254,"tag":622,"props":76504,"children":76505},{"style":1202},[76506],{"type":259,"value":74379},{"type":254,"tag":622,"props":76508,"children":76509},{"style":629},[76510],{"type":259,"value":6624},{"type":254,"tag":622,"props":76512,"children":76513},{"class":624,"line":4131},[76514,76518,76522,76526,76530,76534,76538,76543,76547,76551,76555,76559,76563,76567,76571,76575,76579,76583],{"type":254,"tag":622,"props":76515,"children":76516},{"style":714},[76517],{"type":259,"value":3582},{"type":254,"tag":622,"props":76519,"children":76520},{"style":714},[76521],{"type":259,"value":3587},{"type":254,"tag":622,"props":76523,"children":76524},{"style":629},[76525],{"type":259,"value":1619},{"type":254,"tag":622,"props":76527,"children":76528},{"style":629},[76529],{"type":259,"value":1724},{"type":254,"tag":622,"props":76531,"children":76532},{"style":1202},[76533],{"type":259,"value":73476},{"type":254,"tag":622,"props":76535,"children":76536},{"style":629},[76537],{"type":259,"value":594},{"type":254,"tag":622,"props":76539,"children":76540},{"style":1735},[76541],{"type":259,"value":76542},"PutAsync",{"type":254,"tag":622,"props":76544,"children":76545},{"style":629},[76546],{"type":259,"value":1668},{"type":254,"tag":622,"props":76548,"children":76549},{"style":629},[76550],{"type":259,"value":9607},{"type":254,"tag":622,"props":76552,"children":76553},{"style":664},[76554],{"type":259,"value":74370},{"type":254,"tag":622,"props":76556,"children":76557},{"style":629},[76558],{"type":259,"value":3234},{"type":254,"tag":622,"props":76560,"children":76561},{"style":1202},[76562],{"type":259,"value":74379},{"type":254,"tag":622,"props":76564,"children":76565},{"style":629},[76566],{"type":259,"value":594},{"type":254,"tag":622,"props":76568,"children":76569},{"style":1202},[76570],{"type":259,"value":2254},{"type":254,"tag":622,"props":76572,"children":76573},{"style":629},[76574],{"type":259,"value":2443},{"type":254,"tag":622,"props":76576,"children":76577},{"style":629},[76578],{"type":259,"value":3149},{"type":254,"tag":622,"props":76580,"children":76581},{"style":1202},[76582],{"type":259,"value":76470},{"type":254,"tag":622,"props":76584,"children":76585},{"style":629},[76586],{"type":259,"value":1554},{"type":254,"tag":622,"props":76588,"children":76589},{"class":624,"line":4183},[76590,76594,76598,76602],{"type":254,"tag":622,"props":76591,"children":76592},{"style":1202},[76593],{"type":259,"value":74168},{"type":254,"tag":622,"props":76595,"children":76596},{"style":629},[76597],{"type":259,"value":594},{"type":254,"tag":622,"props":76599,"children":76600},{"style":1735},[76601],{"type":259,"value":74177},{"type":254,"tag":622,"props":76603,"children":76604},{"style":629},[76605],{"type":259,"value":2798},{"type":254,"tag":622,"props":76607,"children":76608},{"class":624,"line":4208},[76609],{"type":254,"tag":622,"props":76610,"children":76611},{"style":629},[76612],{"type":259,"value":1093},{"type":254,"tag":622,"props":76614,"children":76615},{"class":624,"line":4216},[76616],{"type":254,"tag":622,"props":76617,"children":76618},{"style":629},[76619],{"type":259,"value":1111},{"type":254,"tag":255,"props":76621,"children":76622},{},[76623,76625,76631],{"type":259,"value":76624},"To finish we just have to specify in the ",{"type":254,"tag":389,"props":76626,"children":76628},{"className":76627},[],[76629],{"type":259,"value":76630},"Startup.cs",{"type":259,"value":76632}," on which HttpClient to apply the delegating handler we have just created.",{"type":254,"tag":612,"props":76634,"children":76636},{"className":1423,"code":76635,"language":1425,"meta":205,"style":205},"public void ConfigureServices(IServiceCollectionservices)\n{\n    services.AddMemoryCache();\n    services.AddHttpClient\u003CIUserApiAuthenticationService, UserApiAuthenticationService>()\n            .ConfigureHttpClient(c => c.BaseAddress =\"http://urltotheuserapi.com\");\n    \n    services.AddTransient\u003CUserApiAuthenticationHandler>();\n    services.AddHttpClient\u003CUserService, UserService>()\n            .ConfigureHttpClient(c => c.BaseAddress =\"http://urltotheuserapi.com\")\n            .AddHttpMessageHandler\u003CUserApiAuthenticationHanler>();\n}\n",[76637],{"type":254,"tag":389,"props":76638,"children":76639},{"__ignoreMap":205},[76640,76669,76676,76697,76733,76789,76796,76824,76859,76914,76938],{"type":254,"tag":622,"props":76641,"children":76642},{"class":624,"line":625},[76643,76647,76651,76656,76660,76665],{"type":254,"tag":622,"props":76644,"children":76645},{"style":644},[76646],{"type":259,"value":1437},{"type":254,"tag":622,"props":76648,"children":76649},{"style":629},[76650],{"type":259,"value":9525},{"type":254,"tag":622,"props":76652,"children":76653},{"style":1735},[76654],{"type":259,"value":76655}," ConfigureServices",{"type":254,"tag":622,"props":76657,"children":76658},{"style":629},[76659],{"type":259,"value":1668},{"type":254,"tag":622,"props":76661,"children":76662},{"style":1202},[76663],{"type":259,"value":76664},"IServiceCollectionservices",{"type":254,"tag":622,"props":76666,"children":76667},{"style":629},[76668],{"type":259,"value":1685},{"type":254,"tag":622,"props":76670,"children":76671},{"class":624,"line":635},[76672],{"type":254,"tag":622,"props":76673,"children":76674},{"style":629},[76675],{"type":259,"value":632},{"type":254,"tag":622,"props":76677,"children":76678},{"class":624,"line":679},[76679,76684,76688,76693],{"type":254,"tag":622,"props":76680,"children":76681},{"style":1202},[76682],{"type":259,"value":76683},"    services",{"type":254,"tag":622,"props":76685,"children":76686},{"style":629},[76687],{"type":259,"value":594},{"type":254,"tag":622,"props":76689,"children":76690},{"style":1735},[76691],{"type":259,"value":76692},"AddMemoryCache",{"type":254,"tag":622,"props":76694,"children":76695},{"style":629},[76696],{"type":259,"value":2798},{"type":254,"tag":622,"props":76698,"children":76699},{"class":624,"line":705},[76700,76704,76708,76713,76717,76721,76725,76729],{"type":254,"tag":622,"props":76701,"children":76702},{"style":1202},[76703],{"type":259,"value":76683},{"type":254,"tag":622,"props":76705,"children":76706},{"style":629},[76707],{"type":259,"value":594},{"type":254,"tag":622,"props":76709,"children":76710},{"style":1735},[76711],{"type":259,"value":76712},"AddHttpClient",{"type":254,"tag":622,"props":76714,"children":76715},{"style":629},[76716],{"type":259,"value":1743},{"type":254,"tag":622,"props":76718,"children":76719},{"style":714},[76720],{"type":259,"value":75814},{"type":254,"tag":622,"props":76722,"children":76723},{"style":629},[76724],{"type":259,"value":3149},{"type":254,"tag":622,"props":76726,"children":76727},{"style":714},[76728],{"type":259,"value":74989},{"type":254,"tag":622,"props":76730,"children":76731},{"style":629},[76732],{"type":259,"value":15906},{"type":254,"tag":622,"props":76734,"children":76735},{"class":624,"line":732},[76736,76740,76744,76748,76752,76756,76760,76764,76768,76772,76776,76781,76785],{"type":254,"tag":622,"props":76737,"children":76738},{"style":629},[76739],{"type":259,"value":9102},{"type":254,"tag":622,"props":76741,"children":76742},{"style":1735},[76743],{"type":259,"value":15918},{"type":254,"tag":622,"props":76745,"children":76746},{"style":629},[76747],{"type":259,"value":1668},{"type":254,"tag":622,"props":76749,"children":76750},{"style":714},[76751],{"type":259,"value":21295},{"type":254,"tag":622,"props":76753,"children":76754},{"style":629},[76755],{"type":259,"value":2697},{"type":254,"tag":622,"props":76757,"children":76758},{"style":1202},[76759],{"type":259,"value":21304},{"type":254,"tag":622,"props":76761,"children":76762},{"style":629},[76763],{"type":259,"value":594},{"type":254,"tag":622,"props":76765,"children":76766},{"style":1202},[76767],{"type":259,"value":16026},{"type":254,"tag":622,"props":76769,"children":76770},{"style":629},[76771],{"type":259,"value":1654},{"type":254,"tag":622,"props":76773,"children":76774},{"style":629},[76775],{"type":259,"value":652},{"type":254,"tag":622,"props":76777,"children":76778},{"style":664},[76779],{"type":259,"value":76780},"http://urltotheuserapi.com",{"type":254,"tag":622,"props":76782,"children":76783},{"style":629},[76784],{"type":259,"value":652},{"type":254,"tag":622,"props":76786,"children":76787},{"style":629},[76788],{"type":259,"value":1554},{"type":254,"tag":622,"props":76790,"children":76791},{"class":624,"line":759},[76792],{"type":254,"tag":622,"props":76793,"children":76794},{"style":1202},[76795],{"type":259,"value":7336},{"type":254,"tag":622,"props":76797,"children":76798},{"class":624,"line":798},[76799,76803,76807,76811,76815,76820],{"type":254,"tag":622,"props":76800,"children":76801},{"style":1202},[76802],{"type":259,"value":76683},{"type":254,"tag":622,"props":76804,"children":76805},{"style":629},[76806],{"type":259,"value":594},{"type":254,"tag":622,"props":76808,"children":76809},{"style":1735},[76810],{"type":259,"value":15856},{"type":254,"tag":622,"props":76812,"children":76813},{"style":629},[76814],{"type":259,"value":1743},{"type":254,"tag":622,"props":76816,"children":76817},{"style":714},[76818],{"type":259,"value":76819},"UserApiAuthenticationHandler",{"type":254,"tag":622,"props":76821,"children":76822},{"style":629},[76823],{"type":259,"value":6531},{"type":254,"tag":622,"props":76825,"children":76826},{"class":624,"line":836},[76827,76831,76835,76839,76843,76847,76851,76855],{"type":254,"tag":622,"props":76828,"children":76829},{"style":1202},[76830],{"type":259,"value":76683},{"type":254,"tag":622,"props":76832,"children":76833},{"style":629},[76834],{"type":259,"value":594},{"type":254,"tag":622,"props":76836,"children":76837},{"style":1735},[76838],{"type":259,"value":76712},{"type":254,"tag":622,"props":76840,"children":76841},{"style":629},[76842],{"type":259,"value":1743},{"type":254,"tag":622,"props":76844,"children":76845},{"style":714},[76846],{"type":259,"value":73764},{"type":254,"tag":622,"props":76848,"children":76849},{"style":629},[76850],{"type":259,"value":3149},{"type":254,"tag":622,"props":76852,"children":76853},{"style":714},[76854],{"type":259,"value":73802},{"type":254,"tag":622,"props":76856,"children":76857},{"style":629},[76858],{"type":259,"value":15906},{"type":254,"tag":622,"props":76860,"children":76861},{"class":624,"line":862},[76862,76866,76870,76874,76878,76882,76886,76890,76894,76898,76902,76906,76910],{"type":254,"tag":622,"props":76863,"children":76864},{"style":629},[76865],{"type":259,"value":9102},{"type":254,"tag":622,"props":76867,"children":76868},{"style":1735},[76869],{"type":259,"value":15918},{"type":254,"tag":622,"props":76871,"children":76872},{"style":629},[76873],{"type":259,"value":1668},{"type":254,"tag":622,"props":76875,"children":76876},{"style":714},[76877],{"type":259,"value":21295},{"type":254,"tag":622,"props":76879,"children":76880},{"style":629},[76881],{"type":259,"value":2697},{"type":254,"tag":622,"props":76883,"children":76884},{"style":1202},[76885],{"type":259,"value":21304},{"type":254,"tag":622,"props":76887,"children":76888},{"style":629},[76889],{"type":259,"value":594},{"type":254,"tag":622,"props":76891,"children":76892},{"style":1202},[76893],{"type":259,"value":16026},{"type":254,"tag":622,"props":76895,"children":76896},{"style":629},[76897],{"type":259,"value":1654},{"type":254,"tag":622,"props":76899,"children":76900},{"style":629},[76901],{"type":259,"value":652},{"type":254,"tag":622,"props":76903,"children":76904},{"style":664},[76905],{"type":259,"value":76780},{"type":254,"tag":622,"props":76907,"children":76908},{"style":629},[76909],{"type":259,"value":652},{"type":254,"tag":622,"props":76911,"children":76912},{"style":629},[76913],{"type":259,"value":1685},{"type":254,"tag":622,"props":76915,"children":76916},{"class":624,"line":900},[76917,76921,76925,76929,76934],{"type":254,"tag":622,"props":76918,"children":76919},{"style":629},[76920],{"type":259,"value":9102},{"type":254,"tag":622,"props":76922,"children":76923},{"style":1735},[76924],{"type":259,"value":16080},{"type":254,"tag":622,"props":76926,"children":76927},{"style":629},[76928],{"type":259,"value":1743},{"type":254,"tag":622,"props":76930,"children":76931},{"style":714},[76932],{"type":259,"value":76933},"UserApiAuthenticationHanler",{"type":254,"tag":622,"props":76935,"children":76936},{"style":629},[76937],{"type":259,"value":6531},{"type":254,"tag":622,"props":76939,"children":76940},{"class":624,"line":938},[76941],{"type":254,"tag":622,"props":76942,"children":76943},{"style":629},[76944],{"type":259,"value":1111},{"type":254,"tag":282,"props":76946,"children":76947},{"id":10572},[76948],{"type":259,"value":10575},{"type":254,"tag":255,"props":76950,"children":76951},{},[76952],{"type":259,"value":76953},"To summarize, we have put the code that retrieves a token in a separate dedicated service that caches the token until it expires. And we have created a custom delegating handler that calls this service and sets the retrieved token on the authentication header of each HTTP request to the API.",{"type":254,"tag":1260,"props":76955,"children":76956},{},[76957],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":76959},[76960,76961,76962,76963,76964],{"id":284,"depth":635,"text":287},{"id":73750,"depth":635,"text":73753},{"id":74922,"depth":635,"text":74925},{"id":75636,"depth":635,"text":75639},{"id":10572,"depth":635,"text":10575},"content:1.posts:5.delegating-handler.md","1.posts/5.delegating-handler.md",{"_path":19,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":18,"description":76968,"lead":76969,"date":76970,"image":76971,"tags":76973,"badge":76974,"body":76975,"_type":1275,"_id":77232,"_source":231,"_file":77233,"_extension":1278},"The other day when I was looking for a way to automate my development environment setup, I came across Gitpod. Not really what I was looking for but I discovered an awesome tool for working on open source projects.","An IDE in the browser","2019-08-12T00:00:00.000Z",{"src":76972},"/images/github-usbkey.jpg",[16352,245,37215],{"label":243},{"type":251,"children":76976,"toc":77230},[76977,76991,76996,77025,77030,77041,77064,77093,77108,77113,77128,77133,77138,77147,77165,77182,77187,77225],{"type":254,"tag":255,"props":76978,"children":76979},{},[76980,76982,76989],{"type":259,"value":76981},"The other day when I was looking for a way to automate my development environment setup, I came across ",{"type":254,"tag":262,"props":76983,"children":76986},{"href":76984,"rel":76985},"https://www.gitpod.io/",[266],[76987],{"type":259,"value":76988},"Gitpod",{"type":259,"value":76990},". Not really what I was looking for but I discovered an awesome tool for working on open source projects.",{"type":254,"tag":255,"props":76992,"children":76993},{},[76994],{"type":259,"value":76995},"Gitpod is an online service that aims at providing ready-to-code development environments in the browser for GitHub projects.",{"type":254,"tag":255,"props":76997,"children":76998},{},[76999,77001,77008,77010,77016,77018,77024],{"type":259,"value":77000},"Let's see that with one project I was contributing to a few months ago: the ",{"type":254,"tag":262,"props":77002,"children":77005},{"href":77003,"rel":77004},"https://github.com/voxxrin/voxxrin-companion-ui",[266],[77006],{"type":259,"value":77007},"Voxxrin Companion mobile app",{"type":259,"value":77009},".\nFirst, let's go to the project GitHub's page, and prefix the URL of the page with ",{"type":254,"tag":389,"props":77011,"children":77013},{"className":77012},[],[77014],{"type":259,"value":77015},"gitpod.io/#",{"type":259,"value":77017}," which gives us the following URL: ",{"type":254,"tag":262,"props":77019,"children":77022},{"href":77020,"rel":77021},"http://gitpod.io/#https://github.com/voxxrin/voxxrin-companion-ui",[266],[77023],{"type":259,"value":77020},{"type":259,"value":594},{"type":254,"tag":255,"props":77026,"children":77027},{},[77028],{"type":259,"value":77029},"As I am already logged in to the  Gitpod account I created (free for open source developers with a quota of 100 hours/month) Gitpod will start creating a development environment for me. That means pulling a Docker image with everything I need to code (for specific needs, you can also specify a custom docker image to use) and initializing a workspace for me with the code of the project.",{"type":254,"tag":255,"props":77031,"children":77032},{},[77033,77035],{"type":259,"value":77034},"Less than 30s and here we are :\n",{"type":254,"tag":1161,"props":77036,"children":77040},{"alt":77037,"className":77038,"src":77039},"Voxxr.in project opened in gitpod in browser.",[1165,1166],"/posts/images/gitpod_voxxrin_1.png",[],{"type":254,"tag":255,"props":77042,"children":77043},{},[77044,77046,77053,77055,77062],{"type":259,"value":77045},"I have now a complete development environment running in the cloud with the code of my project already cloned. If the editor looks familiar to you it's because ",{"type":254,"tag":262,"props":77047,"children":77050},{"href":77048,"rel":77049},"https://www.theia-ide.org/",[266],[77051],{"type":259,"value":77052},"Theia",{"type":259,"value":77054}," (the IDE's name) is very similar to ",{"type":254,"tag":262,"props":77056,"children":77059},{"href":77057,"rel":77058},"http://typefox.io/theia-vs-code-in-the-cloud",[266],[77060],{"type":259,"value":77061},"VS Code but in the cloud",{"type":259,"value":77063}," (it reuses many parts from VS Code). This is great because it means I can use an IDE I am already familiar with, and even use vs code extensions (provided you have the .vsix files to install them).",{"type":254,"tag":255,"props":77065,"children":77066},{},[77067,77069,77075,77077,77083,77085,77091],{"type":259,"value":77068},"So let's run my project which is an ionic application. Node is already installed so I just have to type ",{"type":254,"tag":389,"props":77070,"children":77072},{"className":77071},[],[77073],{"type":259,"value":77074},"npm install",{"type":259,"value":77076}," and then ",{"type":254,"tag":389,"props":77078,"children":77080},{"className":77079},[],[77081],{"type":259,"value":77082},"npm run-script serve",{"type":259,"value":77084}," in the terminal opened in the editor exactly as if I was working on my local VS Code. My application is configured to be launched on port ",{"type":254,"tag":389,"props":77086,"children":77088},{"className":77087},[],[77089],{"type":259,"value":77090},"8100",{"type":259,"value":77092}," so Gitpod will suggest I expose this port for me to be able to view my application in my browser.",{"type":254,"tag":255,"props":77094,"children":77095},{},[77096,77102],{"type":254,"tag":1161,"props":77097,"children":77101},{"alt":77098,"className":77099,"src":77100},"GitPod suggestion of opening port to run the application.",[1165,1166],"/posts/images/gitpod_voxxrin_2.png",[],{"type":254,"tag":1161,"props":77103,"children":77107},{"alt":77104,"className":77105,"src":77106},"GitPod suggestion of opening live preview of the application.",[1165,1166],"/posts/images/gitpod_voxxrin_3.png",[],{"type":254,"tag":255,"props":77109,"children":77110},{},[77111],{"type":259,"value":77112},"I can now open in my computer browser the application running in my Gitpod workspace. I can also preview it directly in Gitpod.",{"type":254,"tag":255,"props":77114,"children":77115},{},[77116,77122],{"type":254,"tag":1161,"props":77117,"children":77121},{"alt":77118,"className":77119,"src":77120},"Voxxr.in application served in a browser tab.",[1165,1166],"/posts/images/gitpod_voxxrin_5.png",[],{"type":254,"tag":1161,"props":77123,"children":77127},{"alt":77124,"className":77125,"src":77126},"Voxxr.in application served in GitPod tab.",[1165,1166],"/posts/images/gitpod_voxxrin_4.png",[],{"type":254,"tag":255,"props":77129,"children":77130},{},[77131],{"type":259,"value":77132},"If I have some modifications to do I can push them directly from here to my GitHub repository as my Gitpod account is associated with my GitHub account.",{"type":254,"tag":255,"props":77134,"children":77135},{},[77136],{"type":259,"value":77137},"When I have finished, I can just stop the workspace and that's it.",{"type":254,"tag":255,"props":77139,"children":77140},{},[77141],{"type":254,"tag":1161,"props":77142,"children":77146},{"alt":77143,"className":77144,"src":77145},"GitPod menu to stop the workspace.",[1165,1166],"/posts/images/gitpod_voxxrin_7.png",[],{"type":254,"tag":255,"props":77148,"children":77149},{},[77150,77152,77163],{"type":259,"value":77151},"I created the workspace for my project as-is but lots of things can be configured by having a ",{"type":254,"tag":262,"props":77153,"children":77156},{"href":77154,"rel":77155},"https://www.gitpod.io/docs/41_config_gitpod_file/",[266],[77157],{"type":254,"tag":389,"props":77158,"children":77160},{"className":77159},[],[77161],{"type":259,"value":77162},"gitpod.yml",{"type":259,"value":77164}," file in the repository of your project",{"type":254,"tag":255,"props":77166,"children":77167},{},[77168,77170,77180],{"type":259,"value":77169},"With Gitpod you just need a browser and an internet connection to be able to work on any of your GitHub projects from anywhere. When you work on multiple open source projects the nice thing is that it allows you to easily switch from one to another. I wonder if the Browser-Based Web Companion ",{"type":254,"tag":262,"props":77171,"children":77174},{"href":77172,"rel":77173},"https://devblogs.microsoft.com/visualstudio/intelligent-productivity-and-collaboration-from-anywhere/",[266],[77175],{"type":254,"tag":9718,"props":77176,"children":77177},{},[77178],{"type":259,"value":77179},"Visual Studio Online",{"type":259,"value":77181}," announced by Microsoft at Build 2019 will offer the same capabilities and how it will differ from Gitpod.",{"type":254,"tag":255,"props":77183,"children":77184},{},[77185],{"type":259,"value":77186},"This was just a quick introduction to Gitpod but there are far more things you can do with it:",{"type":254,"tag":332,"props":77188,"children":77189},{},[77190,77201,77206,77211,77216,77221],{"type":254,"tag":336,"props":77191,"children":77192},{},[77193,77195,77200],{"type":259,"value":77194},"running scripts on the start-up of your environment  (an ",{"type":254,"tag":389,"props":77196,"children":77198},{"className":77197},[],[77199],{"type":259,"value":77074},{"type":259,"value":39521},{"type":254,"tag":336,"props":77202,"children":77203},{},[77204],{"type":259,"value":77205},"continuously auto-building your repository",{"type":254,"tag":336,"props":77207,"children":77208},{},[77209],{"type":259,"value":77210},"install a database",{"type":254,"tag":336,"props":77212,"children":77213},{},[77214],{"type":259,"value":77215},"use it for private projects (not free, there are multiple pricings available)",{"type":254,"tag":336,"props":77217,"children":77218},{},[77219],{"type":259,"value":77220},"share an environment with other users",{"type":254,"tag":336,"props":77222,"children":77223},{},[77224],{"type":259,"value":60849},{"type":254,"tag":255,"props":77226,"children":77227},{},[77228],{"type":259,"value":77229},"But the best is you try and judge by yourself.",{"title":205,"searchDepth":635,"depth":635,"links":77231},[],"content:1.posts:4.gitpod.md","1.posts/4.gitpod.md",{"_path":16,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":15,"description":77235,"lead":77236,"date":77237,"image":77238,"badge":77240,"tags":77241,"body":77242,"_type":1275,"_id":79701,"_source":231,"_file":79702,"_extension":1278},"Let's jum into Azure CLI to manage Azure resources. The article will show the syntax, the main functionalities and some less-known features like Azure CLI interactive mode.","Let's jump into Azure CLI!","2019-04-08T00:00:00.000Z",{"src":77239},"/images/green-characters.jpg",{"label":243},[16351,6260,72575],{"type":251,"children":77243,"toc":79684},[77244,77249,77297,77310,77316,77345,77354,77359,77364,77370,77383,77388,77423,77456,77467,77510,77535,77555,77564,77576,77582,77588,77597,77623,77632,77648,77654,77659,77667,77939,77945,77958,77970,77983,78956,78961,78975,78984,78998,79007,79021,79030,79046,79052,79057,79062,79346,79351,79357,79362,79368,79373,79378,79400,79406,79411,79420,79432,79441,79446,79455,79461,79473,79478,79486,79499,79508,79513,79525,79531,79536,79549,79558,79591,79600,79619,79628,79633,79649,79666,79670,79675,79680],{"type":254,"tag":255,"props":77245,"children":77246},{},[77247],{"type":259,"value":77248},"In this article about Azure CLI, we will talk about:",{"type":254,"tag":332,"props":77250,"children":77251},{},[77252,77261,77270,77279,77288],{"type":254,"tag":336,"props":77253,"children":77254},{},[77255],{"type":254,"tag":262,"props":77256,"children":77258},{"href":77257},"#_1-managing-azure-resources",[77259],{"type":259,"value":77260},"Managing Azure resources",{"type":254,"tag":336,"props":77262,"children":77263},{},[77264],{"type":254,"tag":262,"props":77265,"children":77267},{"href":77266},"#_2-azure-cli-syntax",[77268],{"type":259,"value":77269},"Azure CLI Syntax",{"type":254,"tag":336,"props":77271,"children":77272},{},[77273],{"type":254,"tag":262,"props":77274,"children":77276},{"href":77275},"#_3-deep-dive-in-azure-cli",[77277],{"type":259,"value":77278},"Deep dive in Azure CLI",{"type":254,"tag":336,"props":77280,"children":77281},{},[77282],{"type":254,"tag":262,"props":77283,"children":77285},{"href":77284},"#_4-where-to-use-azure-cli",[77286],{"type":259,"value":77287},"Where to use Azure CLI",{"type":254,"tag":336,"props":77289,"children":77290},{},[77291],{"type":254,"tag":262,"props":77292,"children":77294},{"href":77293},"#_5-azure-cli-interactive-mode",[77295],{"type":259,"value":77296},"Azure CLI Interactive mode",{"type":254,"tag":255,"props":77298,"children":77299},{},[77300,77302,77309],{"type":259,"value":77301},"Azure CLI can be installed by following the instructions on ",{"type":254,"tag":262,"props":77303,"children":77306},{"href":77304,"rel":77305},"https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest",[266],[77307],{"type":259,"value":77308},"this page",{"type":259,"value":594},{"type":254,"tag":282,"props":77311,"children":77313},{"id":77312},"_1-managing-azure-resources",[77314],{"type":259,"value":77315},"1 - Managing Azure resources",{"type":254,"tag":255,"props":77317,"children":77318},{},[77319,77321,77328,77329,77336,77337,77343],{"type":259,"value":77320},"Azure resources can be managed in different ways but the main ones are the ",{"type":254,"tag":262,"props":77322,"children":77325},{"href":77323,"rel":77324},"https://portal.azure.com",[266],[77326],{"type":259,"value":77327},"Azure Portal",{"type":259,"value":16280},{"type":254,"tag":262,"props":77330,"children":77333},{"href":77331,"rel":77332},"https://docs.microsoft.com/en-us/powershell/azure/",[266],[77334],{"type":259,"value":77335},"Azure PowerShell",{"type":259,"value":37548},{"type":254,"tag":262,"props":77338,"children":77341},{"href":77339,"rel":77340},"https://docs.microsoft.com/en-us/cli/azure",[266],[77342],{"type":259,"value":16351},{"type":259,"value":77344},". If you are new to Azure or if you are creating resources you are not familiar with, it is always nice to have a GUI to understand what you are doing and Azure Portal is the right way to go.",{"type":254,"tag":255,"props":77346,"children":77347},{},[77348],{"type":254,"tag":1161,"props":77349,"children":77353},{"alt":77350,"className":77351,"src":77352},"Azure portal home",[1165,1166],"/posts/images/azurecli_portal_1.png",[],{"type":254,"tag":255,"props":77355,"children":77356},{},[77357],{"type":259,"value":77358},"However, handling resources in the portal, moving from pane to pane to always do the same kind of operations can quickly become a little cumbersome. So if you want to be more productive or if you are more of a command line person, you will find in Azure CLI or Azure PowerShell really good alternatives to Azure Portal. Both of them are cross-platform tools (PowerShell Core runs on Windows, macOS, and Linux 😀) and provide the same functionalities to manage Azure resources.",{"type":254,"tag":255,"props":77360,"children":77361},{},[77362],{"type":259,"value":77363},"Until recently, when I was not using the Azure Portal I was more prompt to use Azure PowerShell than Azure CLI, probably because I was often finding samples of what I wanted to do written in PowerShell. But in fact, there are lots of commands in Azure PowerShell and even with autocomplete I am not good at remembering them. So I couldn't stop returning to using Azure Portal 😕. And yet, lately, I finally came back to the command line when I started to discover the power of Azure CLI: such a nice and above all easy tool to use and that's what I will try to show you next.",{"type":254,"tag":282,"props":77365,"children":77367},{"id":77366},"_2-azure-cli-syntax",[77368],{"type":259,"value":77369},"2 - Azure CLI Syntax",{"type":254,"tag":255,"props":77371,"children":77372},{},[77373,77375,77381],{"type":259,"value":77374},"The Azure CLI Syntax is simple to understand. For instance, the command ",{"type":254,"tag":389,"props":77376,"children":77378},{"className":77377},[],[77379],{"type":259,"value":77380},"az webapp list",{"type":259,"value":77382}," will list the WebApps in the subscription you are currently logged in.",{"type":254,"tag":255,"props":77384,"children":77385},{},[77386],{"type":259,"value":77387},"An Azure CLI command has the following structure:",{"type":254,"tag":332,"props":77389,"children":77390},{},[77391,77402,77413],{"type":254,"tag":336,"props":77392,"children":77393},{},[77394,77395,77400],{"type":259,"value":63838},{"type":254,"tag":9718,"props":77396,"children":77397},{},[77398],{"type":259,"value":77399},"command group",{"type":259,"value":77401}," that represents an Azure service and which can be the composition of subgroups",{"type":254,"tag":336,"props":77403,"children":77404},{},[77405,77406,77411],{"type":259,"value":63838},{"type":254,"tag":9718,"props":77407,"children":77408},{},[77409],{"type":259,"value":77410},"command",{"type":259,"value":77412}," which is the action you want to do on the group / Azure service",{"type":254,"tag":336,"props":77414,"children":77415},{},[77416,77421],{"type":254,"tag":9718,"props":77417,"children":77418},{},[77419],{"type":259,"value":77420},"arguments",{"type":259,"value":77422}," optionally which are a list of parameter names and values",{"type":254,"tag":255,"props":77424,"children":77425},{},[77426,77428,77434,77435,77440,77442,77447,77449,77454],{"type":259,"value":77427},"To sign in to your Azure account in your terminal, you have to execute the Azure CLI command ",{"type":254,"tag":389,"props":77429,"children":77431},{"className":77430},[],[77432],{"type":259,"value":77433},"az login",{"type":259,"value":48156},{"type":254,"tag":389,"props":77436,"children":77438},{"className":77437},[],[77439],{"type":259,"value":41000},{"type":259,"value":77441}," is the command group and ",{"type":254,"tag":389,"props":77443,"children":77445},{"className":77444},[],[77446],{"type":259,"value":60610},{"type":259,"value":77448}," is the command relative to the group/service ",{"type":254,"tag":389,"props":77450,"children":77452},{"className":77451},[],[77453],{"type":259,"value":41000},{"type":259,"value":77455}," (meaning Azure).",{"type":254,"tag":255,"props":77457,"children":77458},{},[77459,77461,77466],{"type":259,"value":77460},"In our previous example, ",{"type":254,"tag":389,"props":77462,"children":77464},{"className":77463},[],[77465],{"type":259,"value":77380},{"type":259,"value":3274},{"type":254,"tag":332,"props":77468,"children":77469},{},[77470,77494,77505],{"type":254,"tag":336,"props":77471,"children":77472},{},[77473,77479,77481,77486,77488],{"type":254,"tag":389,"props":77474,"children":77476},{"className":77475},[],[77477],{"type":259,"value":77478},"az webapp",{"type":259,"value":77480}," was the command group composed of ",{"type":254,"tag":389,"props":77482,"children":77484},{"className":77483},[],[77485],{"type":259,"value":41000},{"type":259,"value":77487}," and its subgroup ",{"type":254,"tag":389,"props":77489,"children":77491},{"className":77490},[],[77492],{"type":259,"value":77493},"webapp",{"type":254,"tag":336,"props":77495,"children":77496},{},[77497,77503],{"type":254,"tag":389,"props":77498,"children":77500},{"className":77499},[],[77501],{"type":259,"value":77502},"list",{"type":259,"value":77504}," was the command",{"type":254,"tag":336,"props":77506,"children":77507},{},[77508],{"type":259,"value":77509},"no arguments here",{"type":254,"tag":255,"props":77511,"children":77512},{},[77513,77515,77520,77522,77527,77529],{"type":259,"value":77514},"We could have added some arguments to this command, like the ",{"type":254,"tag":389,"props":77516,"children":77518},{"className":77517},[],[77519],{"type":259,"value":60975},{"type":259,"value":77521}," argument (or ",{"type":254,"tag":389,"props":77523,"children":77525},{"className":77524},[],[77526],{"type":259,"value":24273},{"type":259,"value":77528},") to list only the WebApps in a specific resource group and that would have become ",{"type":254,"tag":389,"props":77530,"children":77532},{"className":77531},[],[77533],{"type":259,"value":77534},"az webapp list --resource-group 'myResourceGroupName'",{"type":254,"tag":255,"props":77536,"children":77537},{},[77538,77540,77546,77548,77553],{"type":259,"value":77539},"A few arguments are globally available arguments which means that can be used for every command. This is the case of the ",{"type":254,"tag":389,"props":77541,"children":77543},{"className":77542},[],[77544],{"type":259,"value":77545},"--help",{"type":259,"value":77547}," argument which displays help information about a command. The following example showing the use of the ",{"type":254,"tag":389,"props":77549,"children":77551},{"className":77550},[],[77552],{"type":259,"value":77545},{"type":259,"value":77554}," argument allows us to better understand the structure of an Azure CLI command.",{"type":254,"tag":255,"props":77556,"children":77557},{},[77558],{"type":254,"tag":1161,"props":77559,"children":77563},{"alt":77560,"className":77561,"src":77562},"Azure CLI Help output.",[1165,1166],"/posts/images/azurecli_console_1.png",[],{"type":254,"tag":255,"props":77565,"children":77566},{},[77567,77569,77574],{"type":259,"value":77568},"Once used to the Azure CLI syntax, it is way more convenient than the Azure Portal and you don't have to search what Azure PowerShell command you need to use. You just have to look for the available subgroups and commands in the Azure service where you want to work (don't forget to use ",{"type":254,"tag":389,"props":77570,"children":77572},{"className":77571},[],[77573],{"type":259,"value":77545},{"type":259,"value":77575},") and you will quickly get the job done.",{"type":254,"tag":282,"props":77577,"children":77579},{"id":77578},"_3-deep-dive-in-azure-cli",[77580],{"type":259,"value":77581},"3 - Deep dive in Azure CLI",{"type":254,"tag":455,"props":77583,"children":77585},{"id":77584},"command-output",[77586],{"type":259,"value":77587},"Command output",{"type":254,"tag":255,"props":77589,"children":77590},{},[77591],{"type":254,"tag":1161,"props":77592,"children":77596},{"alt":77593,"className":77594,"src":77595},"Azure CLI command outputs.",[1165,1166],"/posts/images/azurecli_console_2.png",[],{"type":254,"tag":255,"props":77598,"children":77599},{},[77600,77602,77608,77609,77614,77616,77622],{"type":259,"value":77601},"By default, the output format of Azure CLI commands is JSON but there are other formats you can use by specifying an output argument (",{"type":254,"tag":389,"props":77603,"children":77605},{"className":77604},[],[77606],{"type":259,"value":77607},"--output",{"type":259,"value":31561},{"type":254,"tag":389,"props":77610,"children":77612},{"className":77611},[],[77613],{"type":259,"value":48537},{"type":259,"value":77615},") like this: ",{"type":254,"tag":389,"props":77617,"children":77619},{"className":77618},[],[77620],{"type":259,"value":77621},"az group list -o table",{"type":259,"value":594},{"type":254,"tag":255,"props":77624,"children":77625},{},[77626],{"type":254,"tag":1161,"props":77627,"children":77631},{"alt":77628,"className":77629,"src":77630},"Azure CLI json and table outputs.",[1165,1166],"/posts/images/azurecli_console_3.png",[],{"type":254,"tag":427,"props":77633,"children":77634},{"icon":68963},[77635],{"type":254,"tag":255,"props":77636,"children":77637},{},[77638,77640,77646],{"type":259,"value":77639},"The default output format can be configured using the ",{"type":254,"tag":389,"props":77641,"children":77643},{"className":77642},[],[77644],{"type":259,"value":77645},"az configure",{"type":259,"value":77647}," command. This command also allows you to configure other settings like a default resource group for your commands for instance.",{"type":254,"tag":455,"props":77649,"children":77651},{"id":77650},"using-variables",[77652],{"type":259,"value":77653},"Using variables",{"type":254,"tag":255,"props":77655,"children":77656},{},[77657],{"type":259,"value":77658},"Whether you choose to run your commands in Bash or PowerShell, you can use variables with Azure CLI, only the syntax for creating variables will change depending on the command-line shell you use.",{"type":254,"tag":427,"props":77660,"children":77661},{"icon":429},[77662],{"type":254,"tag":255,"props":77663,"children":77664},{},[77665],{"type":259,"value":77666},"The samples in this article use the PowerShell syntax.",{"type":254,"tag":612,"props":77668,"children":77670},{"className":10656,"code":77669,"language":10658,"meta":205,"style":205},"$rgName = 'TestingAzureCLI'\n\n# Create a new resource group\naz group create -n $rgName -l westeurope\n\n# Create an App Service plan with the Free tier\naz appservice plan create -n $webAppName -g $rgName --sku FREE\n\n# Show the App Service plan just created \naz appservice plan show -n $webAppName -g $rgName \n\n# Store the result of the show query in a PowerShell variable\n$servicePlan = az appservice plan show -n $webAppName -g $rgName \n",[77671],{"type":254,"tag":389,"props":77672,"children":77673},{"__ignoreMap":205},[77674,77702,77709,77717,77749,77756,77764,77814,77821,77829,77870,77877,77885],{"type":254,"tag":622,"props":77675,"children":77676},{"class":624,"line":625},[77677,77681,77685,77689,77693,77698],{"type":254,"tag":622,"props":77678,"children":77679},{"style":629},[77680],{"type":259,"value":16922},{"type":254,"tag":622,"props":77682,"children":77683},{"style":1202},[77684],{"type":259,"value":48629},{"type":254,"tag":622,"props":77686,"children":77687},{"style":629},[77688],{"type":259,"value":1654},{"type":254,"tag":622,"props":77690,"children":77691},{"style":629},[77692],{"type":259,"value":16556},{"type":254,"tag":622,"props":77694,"children":77695},{"style":664},[77696],{"type":259,"value":77697},"TestingAzureCLI",{"type":254,"tag":622,"props":77699,"children":77700},{"style":629},[77701],{"type":259,"value":16566},{"type":254,"tag":622,"props":77703,"children":77704},{"class":624,"line":635},[77705],{"type":254,"tag":622,"props":77706,"children":77707},{"emptyLinePlaceholder":1699},[77708],{"type":259,"value":1702},{"type":254,"tag":622,"props":77710,"children":77711},{"class":624,"line":679},[77712],{"type":254,"tag":622,"props":77713,"children":77714},{"style":3195},[77715],{"type":259,"value":77716},"# Create a new resource group\n",{"type":254,"tag":622,"props":77718,"children":77719},{"class":624,"line":705},[77720,77724,77728,77732,77736,77740,77744],{"type":254,"tag":622,"props":77721,"children":77722},{"style":1202},[77723],{"type":259,"value":49756},{"type":254,"tag":622,"props":77725,"children":77726},{"style":629},[77727],{"type":259,"value":17065},{"type":254,"tag":622,"props":77729,"children":77730},{"style":1202},[77731],{"type":259,"value":48602},{"type":254,"tag":622,"props":77733,"children":77734},{"style":629},[77735],{"type":259,"value":16922},{"type":254,"tag":622,"props":77737,"children":77738},{"style":1202},[77739],{"type":259,"value":48629},{"type":254,"tag":622,"props":77741,"children":77742},{"style":629},[77743],{"type":259,"value":17065},{"type":254,"tag":622,"props":77745,"children":77746},{"style":1202},[77747],{"type":259,"value":77748},"l westeurope\n",{"type":254,"tag":622,"props":77750,"children":77751},{"class":624,"line":732},[77752],{"type":254,"tag":622,"props":77753,"children":77754},{"emptyLinePlaceholder":1699},[77755],{"type":259,"value":1702},{"type":254,"tag":622,"props":77757,"children":77758},{"class":624,"line":759},[77759],{"type":254,"tag":622,"props":77760,"children":77761},{"style":3195},[77762],{"type":259,"value":77763},"# Create an App Service plan with the Free tier\n",{"type":254,"tag":622,"props":77765,"children":77766},{"class":624,"line":798},[77767,77772,77776,77780,77784,77789,77793,77797,77801,77805,77809],{"type":254,"tag":622,"props":77768,"children":77769},{"style":1202},[77770],{"type":259,"value":77771},"az appservice plan create ",{"type":254,"tag":622,"props":77773,"children":77774},{"style":629},[77775],{"type":259,"value":17065},{"type":254,"tag":622,"props":77777,"children":77778},{"style":1202},[77779],{"type":259,"value":48602},{"type":254,"tag":622,"props":77781,"children":77782},{"style":629},[77783],{"type":259,"value":16922},{"type":254,"tag":622,"props":77785,"children":77786},{"style":1202},[77787],{"type":259,"value":77788},"webAppName ",{"type":254,"tag":622,"props":77790,"children":77791},{"style":629},[77792],{"type":259,"value":17065},{"type":254,"tag":622,"props":77794,"children":77795},{"style":1202},[77796],{"type":259,"value":48620},{"type":254,"tag":622,"props":77798,"children":77799},{"style":629},[77800],{"type":259,"value":16922},{"type":254,"tag":622,"props":77802,"children":77803},{"style":1202},[77804],{"type":259,"value":48629},{"type":254,"tag":622,"props":77806,"children":77807},{"style":629},[77808],{"type":259,"value":10675},{"type":254,"tag":622,"props":77810,"children":77811},{"style":1202},[77812],{"type":259,"value":77813},"sku FREE\n",{"type":254,"tag":622,"props":77815,"children":77816},{"class":624,"line":836},[77817],{"type":254,"tag":622,"props":77818,"children":77819},{"emptyLinePlaceholder":1699},[77820],{"type":259,"value":1702},{"type":254,"tag":622,"props":77822,"children":77823},{"class":624,"line":862},[77824],{"type":254,"tag":622,"props":77825,"children":77826},{"style":3195},[77827],{"type":259,"value":77828},"# Show the App Service plan just created \n",{"type":254,"tag":622,"props":77830,"children":77831},{"class":624,"line":900},[77832,77837,77841,77845,77849,77853,77857,77861,77865],{"type":254,"tag":622,"props":77833,"children":77834},{"style":1202},[77835],{"type":259,"value":77836},"az appservice plan show ",{"type":254,"tag":622,"props":77838,"children":77839},{"style":629},[77840],{"type":259,"value":17065},{"type":254,"tag":622,"props":77842,"children":77843},{"style":1202},[77844],{"type":259,"value":48602},{"type":254,"tag":622,"props":77846,"children":77847},{"style":629},[77848],{"type":259,"value":16922},{"type":254,"tag":622,"props":77850,"children":77851},{"style":1202},[77852],{"type":259,"value":77788},{"type":254,"tag":622,"props":77854,"children":77855},{"style":629},[77856],{"type":259,"value":17065},{"type":254,"tag":622,"props":77858,"children":77859},{"style":1202},[77860],{"type":259,"value":48620},{"type":254,"tag":622,"props":77862,"children":77863},{"style":629},[77864],{"type":259,"value":16922},{"type":254,"tag":622,"props":77866,"children":77867},{"style":1202},[77868],{"type":259,"value":77869},"rgName \n",{"type":254,"tag":622,"props":77871,"children":77872},{"class":624,"line":938},[77873],{"type":254,"tag":622,"props":77874,"children":77875},{"emptyLinePlaceholder":1699},[77876],{"type":259,"value":1702},{"type":254,"tag":622,"props":77878,"children":77879},{"class":624,"line":976},[77880],{"type":254,"tag":622,"props":77881,"children":77882},{"style":3195},[77883],{"type":259,"value":77884},"# Store the result of the show query in a PowerShell variable\n",{"type":254,"tag":622,"props":77886,"children":77887},{"class":624,"line":1014},[77888,77892,77897,77901,77906,77910,77914,77918,77922,77926,77930,77934],{"type":254,"tag":622,"props":77889,"children":77890},{"style":629},[77891],{"type":259,"value":16922},{"type":254,"tag":622,"props":77893,"children":77894},{"style":1202},[77895],{"type":259,"value":77896},"servicePlan ",{"type":254,"tag":622,"props":77898,"children":77899},{"style":629},[77900],{"type":259,"value":1654},{"type":254,"tag":622,"props":77902,"children":77903},{"style":1202},[77904],{"type":259,"value":77905}," az appservice plan show ",{"type":254,"tag":622,"props":77907,"children":77908},{"style":629},[77909],{"type":259,"value":17065},{"type":254,"tag":622,"props":77911,"children":77912},{"style":1202},[77913],{"type":259,"value":48602},{"type":254,"tag":622,"props":77915,"children":77916},{"style":629},[77917],{"type":259,"value":16922},{"type":254,"tag":622,"props":77919,"children":77920},{"style":1202},[77921],{"type":259,"value":77788},{"type":254,"tag":622,"props":77923,"children":77924},{"style":629},[77925],{"type":259,"value":17065},{"type":254,"tag":622,"props":77927,"children":77928},{"style":1202},[77929],{"type":259,"value":48620},{"type":254,"tag":622,"props":77931,"children":77932},{"style":629},[77933],{"type":259,"value":16922},{"type":254,"tag":622,"props":77935,"children":77936},{"style":1202},[77937],{"type":259,"value":77938},"rgName\n",{"type":254,"tag":455,"props":77940,"children":77942},{"id":77941},"jmespath",[77943],{"type":259,"value":77944},"JMESPath",{"type":254,"tag":255,"props":77946,"children":77947},{},[77948,77950,77956],{"type":259,"value":77949},"If you don't know ",{"type":254,"tag":262,"props":77951,"children":77954},{"href":77952,"rel":77953},"http://jmespath.org/",[266],[77955],{"type":259,"value":77944},{"type":259,"value":77957},", it is a query language for JSON that allows to extract and transform elements from a JSON document or CLI output in the context of Azure CLI.",{"type":254,"tag":255,"props":77959,"children":77960},{},[77961,77963,77968],{"type":259,"value":77962},"To use JMESPath you have to add in your CLI command the argument ",{"type":254,"tag":389,"props":77964,"children":77966},{"className":77965},[],[77967],{"type":259,"value":17258},{"type":259,"value":77969}," followed by your JMESPath query.\nLet's see that with some examples.",{"type":254,"tag":255,"props":77971,"children":77972},{},[77973,77975,77981],{"type":259,"value":77974},"The query ",{"type":254,"tag":389,"props":77976,"children":77978},{"className":77977},[],[77979],{"type":259,"value":77980},"az group list",{"type":259,"value":77982}," executed on my subscription returns the following JSON:",{"type":254,"tag":612,"props":77984,"children":77986},{"className":614,"code":77985,"language":616,"meta":205,"style":205},"[\n  {\n    \"id\": \"/subscriptions/********-****-****-****-************/resourceGroups/CloudShellRG\",\n    \"location\": \"westeurope\",\n    \"managedBy\": null,\n    \"name\": \"CloudShellRG\",\n    \"properties\": {\n      \"provisioningState\": \"Succeeded\"\n    },\n    \"tags\": {\n      \"Environment\": \"Production\"\n    },\n    \"type\": null\n  },\n  {\n    \"id\": \"/subscriptions/********-****-****-****-************/resourceGroups/MyApp1ResourceGroup\",\n    \"location\": \"canadacentral\",\n    \"managedBy\": null,\n    \"name\": \"MyApp1ResourceGroup\",\n    \"properties\": {\n      \"provisioningState\": \"Succeeded\"\n    },\n    \"tags\": {\n      \"Department\": \"RH\",\n      \"Environment\": \"Dev\"\n    },\n    \"type\": null\n  },\n  {\n    \"id\": \"/subscriptions/********-****-****-****-************/resourceGroups/TestingAzureCLI\",\n    \"location\": \"westeurope\",\n    \"managedBy\": null,\n    \"name\": \"TestingAzureCLI\",\n    \"properties\": {\n      \"provisioningState\": \"Succeeded\"\n    },\n    \"tags\": {\n      \"Department\": \"IT\",\n      \"Environment\": \"Production\"\n    },\n    \"type\": null\n  }\n]\n",[77987],{"type":254,"tag":389,"props":77988,"children":77989},{"__ignoreMap":205},[77990,77998,78005,78041,78077,78102,78138,78162,78195,78202,78226,78258,78265,78290,78298,78305,78341,78377,78400,78436,78459,78490,78497,78520,78557,78589,78596,78619,78626,78633,78669,78704,78727,78762,78785,78816,78823,78846,78881,78912,78919,78942,78949],{"type":254,"tag":622,"props":77991,"children":77992},{"class":624,"line":625},[77993],{"type":254,"tag":622,"props":77994,"children":77995},{"style":629},[77996],{"type":259,"value":77997},"[\n",{"type":254,"tag":622,"props":77999,"children":78000},{"class":624,"line":635},[78001],{"type":254,"tag":622,"props":78002,"children":78003},{"style":629},[78004],{"type":259,"value":40525},{"type":254,"tag":622,"props":78006,"children":78007},{"class":624,"line":679},[78008,78012,78016,78020,78024,78028,78033,78037],{"type":254,"tag":622,"props":78009,"children":78010},{"style":629},[78011],{"type":259,"value":711},{"type":254,"tag":622,"props":78013,"children":78014},{"style":644},[78015],{"type":259,"value":17175},{"type":254,"tag":622,"props":78017,"children":78018},{"style":629},[78019],{"type":259,"value":652},{"type":254,"tag":622,"props":78021,"children":78022},{"style":629},[78023],{"type":259,"value":474},{"type":254,"tag":622,"props":78025,"children":78026},{"style":629},[78027],{"type":259,"value":661},{"type":254,"tag":622,"props":78029,"children":78030},{"style":664},[78031],{"type":259,"value":78032},"/subscriptions/********-****-****-****-************/resourceGroups/CloudShellRG",{"type":254,"tag":622,"props":78034,"children":78035},{"style":629},[78036],{"type":259,"value":652},{"type":254,"tag":622,"props":78038,"children":78039},{"style":629},[78040],{"type":259,"value":676},{"type":254,"tag":622,"props":78042,"children":78043},{"class":624,"line":705},[78044,78048,78052,78056,78060,78064,78069,78073],{"type":254,"tag":622,"props":78045,"children":78046},{"style":629},[78047],{"type":259,"value":711},{"type":254,"tag":622,"props":78049,"children":78050},{"style":644},[78051],{"type":259,"value":48283},{"type":254,"tag":622,"props":78053,"children":78054},{"style":629},[78055],{"type":259,"value":652},{"type":254,"tag":622,"props":78057,"children":78058},{"style":629},[78059],{"type":259,"value":474},{"type":254,"tag":622,"props":78061,"children":78062},{"style":629},[78063],{"type":259,"value":661},{"type":254,"tag":622,"props":78065,"children":78066},{"style":664},[78067],{"type":259,"value":78068},"westeurope",{"type":254,"tag":622,"props":78070,"children":78071},{"style":629},[78072],{"type":259,"value":652},{"type":254,"tag":622,"props":78074,"children":78075},{"style":629},[78076],{"type":259,"value":676},{"type":254,"tag":622,"props":78078,"children":78079},{"class":624,"line":732},[78080,78084,78089,78093,78097],{"type":254,"tag":622,"props":78081,"children":78082},{"style":629},[78083],{"type":259,"value":711},{"type":254,"tag":622,"props":78085,"children":78086},{"style":644},[78087],{"type":259,"value":78088},"managedBy",{"type":254,"tag":622,"props":78090,"children":78091},{"style":629},[78092],{"type":259,"value":652},{"type":254,"tag":622,"props":78094,"children":78095},{"style":629},[78096],{"type":259,"value":474},{"type":254,"tag":622,"props":78098,"children":78099},{"style":629},[78100],{"type":259,"value":78101}," null,\n",{"type":254,"tag":622,"props":78103,"children":78104},{"class":624,"line":759},[78105,78109,78113,78117,78121,78125,78130,78134],{"type":254,"tag":622,"props":78106,"children":78107},{"style":629},[78108],{"type":259,"value":711},{"type":254,"tag":622,"props":78110,"children":78111},{"style":644},[78112],{"type":259,"value":16394},{"type":254,"tag":622,"props":78114,"children":78115},{"style":629},[78116],{"type":259,"value":652},{"type":254,"tag":622,"props":78118,"children":78119},{"style":629},[78120],{"type":259,"value":474},{"type":254,"tag":622,"props":78122,"children":78123},{"style":629},[78124],{"type":259,"value":661},{"type":254,"tag":622,"props":78126,"children":78127},{"style":664},[78128],{"type":259,"value":78129},"CloudShellRG",{"type":254,"tag":622,"props":78131,"children":78132},{"style":629},[78133],{"type":259,"value":652},{"type":254,"tag":622,"props":78135,"children":78136},{"style":629},[78137],{"type":259,"value":676},{"type":254,"tag":622,"props":78139,"children":78140},{"class":624,"line":798},[78141,78145,78150,78154,78158],{"type":254,"tag":622,"props":78142,"children":78143},{"style":629},[78144],{"type":259,"value":711},{"type":254,"tag":622,"props":78146,"children":78147},{"style":644},[78148],{"type":259,"value":78149},"properties",{"type":254,"tag":622,"props":78151,"children":78152},{"style":629},[78153],{"type":259,"value":652},{"type":254,"tag":622,"props":78155,"children":78156},{"style":629},[78157],{"type":259,"value":474},{"type":254,"tag":622,"props":78159,"children":78160},{"style":629},[78161],{"type":259,"value":702},{"type":254,"tag":622,"props":78163,"children":78164},{"class":624,"line":836},[78165,78169,78174,78178,78182,78186,78191],{"type":254,"tag":622,"props":78166,"children":78167},{"style":629},[78168],{"type":259,"value":738},{"type":254,"tag":622,"props":78170,"children":78171},{"style":714},[78172],{"type":259,"value":78173},"provisioningState",{"type":254,"tag":622,"props":78175,"children":78176},{"style":629},[78177],{"type":259,"value":652},{"type":254,"tag":622,"props":78179,"children":78180},{"style":629},[78181],{"type":259,"value":474},{"type":254,"tag":622,"props":78183,"children":78184},{"style":629},[78185],{"type":259,"value":661},{"type":254,"tag":622,"props":78187,"children":78188},{"style":664},[78189],{"type":259,"value":78190},"Succeeded",{"type":254,"tag":622,"props":78192,"children":78193},{"style":629},[78194],{"type":259,"value":9909},{"type":254,"tag":622,"props":78196,"children":78197},{"class":624,"line":862},[78198],{"type":254,"tag":622,"props":78199,"children":78200},{"style":629},[78201],{"type":259,"value":20453},{"type":254,"tag":622,"props":78203,"children":78204},{"class":624,"line":900},[78205,78209,78214,78218,78222],{"type":254,"tag":622,"props":78206,"children":78207},{"style":629},[78208],{"type":259,"value":711},{"type":254,"tag":622,"props":78210,"children":78211},{"style":644},[78212],{"type":259,"value":78213},"tags",{"type":254,"tag":622,"props":78215,"children":78216},{"style":629},[78217],{"type":259,"value":652},{"type":254,"tag":622,"props":78219,"children":78220},{"style":629},[78221],{"type":259,"value":474},{"type":254,"tag":622,"props":78223,"children":78224},{"style":629},[78225],{"type":259,"value":702},{"type":254,"tag":622,"props":78227,"children":78228},{"class":624,"line":938},[78229,78233,78237,78241,78245,78249,78254],{"type":254,"tag":622,"props":78230,"children":78231},{"style":629},[78232],{"type":259,"value":738},{"type":254,"tag":622,"props":78234,"children":78235},{"style":714},[78236],{"type":259,"value":55085},{"type":254,"tag":622,"props":78238,"children":78239},{"style":629},[78240],{"type":259,"value":652},{"type":254,"tag":622,"props":78242,"children":78243},{"style":629},[78244],{"type":259,"value":474},{"type":254,"tag":622,"props":78246,"children":78247},{"style":629},[78248],{"type":259,"value":661},{"type":254,"tag":622,"props":78250,"children":78251},{"style":664},[78252],{"type":259,"value":78253},"Production",{"type":254,"tag":622,"props":78255,"children":78256},{"style":629},[78257],{"type":259,"value":9909},{"type":254,"tag":622,"props":78259,"children":78260},{"class":624,"line":976},[78261],{"type":254,"tag":622,"props":78262,"children":78263},{"style":629},[78264],{"type":259,"value":20453},{"type":254,"tag":622,"props":78266,"children":78267},{"class":624,"line":1014},[78268,78272,78277,78281,78285],{"type":254,"tag":622,"props":78269,"children":78270},{"style":629},[78271],{"type":259,"value":711},{"type":254,"tag":622,"props":78273,"children":78274},{"style":644},[78275],{"type":259,"value":78276},"type",{"type":254,"tag":622,"props":78278,"children":78279},{"style":629},[78280],{"type":259,"value":652},{"type":254,"tag":622,"props":78282,"children":78283},{"style":629},[78284],{"type":259,"value":474},{"type":254,"tag":622,"props":78286,"children":78287},{"style":629},[78288],{"type":259,"value":78289}," null\n",{"type":254,"tag":622,"props":78291,"children":78292},{"class":624,"line":1052},[78293],{"type":254,"tag":622,"props":78294,"children":78295},{"style":629},[78296],{"type":259,"value":78297},"  },\n",{"type":254,"tag":622,"props":78299,"children":78300},{"class":624,"line":1078},[78301],{"type":254,"tag":622,"props":78302,"children":78303},{"style":629},[78304],{"type":259,"value":40525},{"type":254,"tag":622,"props":78306,"children":78307},{"class":624,"line":1087},[78308,78312,78316,78320,78324,78328,78333,78337],{"type":254,"tag":622,"props":78309,"children":78310},{"style":629},[78311],{"type":259,"value":711},{"type":254,"tag":622,"props":78313,"children":78314},{"style":644},[78315],{"type":259,"value":17175},{"type":254,"tag":622,"props":78317,"children":78318},{"style":629},[78319],{"type":259,"value":652},{"type":254,"tag":622,"props":78321,"children":78322},{"style":629},[78323],{"type":259,"value":474},{"type":254,"tag":622,"props":78325,"children":78326},{"style":629},[78327],{"type":259,"value":661},{"type":254,"tag":622,"props":78329,"children":78330},{"style":664},[78331],{"type":259,"value":78332},"/subscriptions/********-****-****-****-************/resourceGroups/MyApp1ResourceGroup",{"type":254,"tag":622,"props":78334,"children":78335},{"style":629},[78336],{"type":259,"value":652},{"type":254,"tag":622,"props":78338,"children":78339},{"style":629},[78340],{"type":259,"value":676},{"type":254,"tag":622,"props":78342,"children":78343},{"class":624,"line":1096},[78344,78348,78352,78356,78360,78364,78369,78373],{"type":254,"tag":622,"props":78345,"children":78346},{"style":629},[78347],{"type":259,"value":711},{"type":254,"tag":622,"props":78349,"children":78350},{"style":644},[78351],{"type":259,"value":48283},{"type":254,"tag":622,"props":78353,"children":78354},{"style":629},[78355],{"type":259,"value":652},{"type":254,"tag":622,"props":78357,"children":78358},{"style":629},[78359],{"type":259,"value":474},{"type":254,"tag":622,"props":78361,"children":78362},{"style":629},[78363],{"type":259,"value":661},{"type":254,"tag":622,"props":78365,"children":78366},{"style":664},[78367],{"type":259,"value":78368},"canadacentral",{"type":254,"tag":622,"props":78370,"children":78371},{"style":629},[78372],{"type":259,"value":652},{"type":254,"tag":622,"props":78374,"children":78375},{"style":629},[78376],{"type":259,"value":676},{"type":254,"tag":622,"props":78378,"children":78379},{"class":624,"line":1105},[78380,78384,78388,78392,78396],{"type":254,"tag":622,"props":78381,"children":78382},{"style":629},[78383],{"type":259,"value":711},{"type":254,"tag":622,"props":78385,"children":78386},{"style":644},[78387],{"type":259,"value":78088},{"type":254,"tag":622,"props":78389,"children":78390},{"style":629},[78391],{"type":259,"value":652},{"type":254,"tag":622,"props":78393,"children":78394},{"style":629},[78395],{"type":259,"value":474},{"type":254,"tag":622,"props":78397,"children":78398},{"style":629},[78399],{"type":259,"value":78101},{"type":254,"tag":622,"props":78401,"children":78402},{"class":624,"line":4131},[78403,78407,78411,78415,78419,78423,78428,78432],{"type":254,"tag":622,"props":78404,"children":78405},{"style":629},[78406],{"type":259,"value":711},{"type":254,"tag":622,"props":78408,"children":78409},{"style":644},[78410],{"type":259,"value":16394},{"type":254,"tag":622,"props":78412,"children":78413},{"style":629},[78414],{"type":259,"value":652},{"type":254,"tag":622,"props":78416,"children":78417},{"style":629},[78418],{"type":259,"value":474},{"type":254,"tag":622,"props":78420,"children":78421},{"style":629},[78422],{"type":259,"value":661},{"type":254,"tag":622,"props":78424,"children":78425},{"style":664},[78426],{"type":259,"value":78427},"MyApp1ResourceGroup",{"type":254,"tag":622,"props":78429,"children":78430},{"style":629},[78431],{"type":259,"value":652},{"type":254,"tag":622,"props":78433,"children":78434},{"style":629},[78435],{"type":259,"value":676},{"type":254,"tag":622,"props":78437,"children":78438},{"class":624,"line":4183},[78439,78443,78447,78451,78455],{"type":254,"tag":622,"props":78440,"children":78441},{"style":629},[78442],{"type":259,"value":711},{"type":254,"tag":622,"props":78444,"children":78445},{"style":644},[78446],{"type":259,"value":78149},{"type":254,"tag":622,"props":78448,"children":78449},{"style":629},[78450],{"type":259,"value":652},{"type":254,"tag":622,"props":78452,"children":78453},{"style":629},[78454],{"type":259,"value":474},{"type":254,"tag":622,"props":78456,"children":78457},{"style":629},[78458],{"type":259,"value":702},{"type":254,"tag":622,"props":78460,"children":78461},{"class":624,"line":4208},[78462,78466,78470,78474,78478,78482,78486],{"type":254,"tag":622,"props":78463,"children":78464},{"style":629},[78465],{"type":259,"value":738},{"type":254,"tag":622,"props":78467,"children":78468},{"style":714},[78469],{"type":259,"value":78173},{"type":254,"tag":622,"props":78471,"children":78472},{"style":629},[78473],{"type":259,"value":652},{"type":254,"tag":622,"props":78475,"children":78476},{"style":629},[78477],{"type":259,"value":474},{"type":254,"tag":622,"props":78479,"children":78480},{"style":629},[78481],{"type":259,"value":661},{"type":254,"tag":622,"props":78483,"children":78484},{"style":664},[78485],{"type":259,"value":78190},{"type":254,"tag":622,"props":78487,"children":78488},{"style":629},[78489],{"type":259,"value":9909},{"type":254,"tag":622,"props":78491,"children":78492},{"class":624,"line":4216},[78493],{"type":254,"tag":622,"props":78494,"children":78495},{"style":629},[78496],{"type":259,"value":20453},{"type":254,"tag":622,"props":78498,"children":78499},{"class":624,"line":4224},[78500,78504,78508,78512,78516],{"type":254,"tag":622,"props":78501,"children":78502},{"style":629},[78503],{"type":259,"value":711},{"type":254,"tag":622,"props":78505,"children":78506},{"style":644},[78507],{"type":259,"value":78213},{"type":254,"tag":622,"props":78509,"children":78510},{"style":629},[78511],{"type":259,"value":652},{"type":254,"tag":622,"props":78513,"children":78514},{"style":629},[78515],{"type":259,"value":474},{"type":254,"tag":622,"props":78517,"children":78518},{"style":629},[78519],{"type":259,"value":702},{"type":254,"tag":622,"props":78521,"children":78522},{"class":624,"line":4268},[78523,78527,78532,78536,78540,78544,78549,78553],{"type":254,"tag":622,"props":78524,"children":78525},{"style":629},[78526],{"type":259,"value":738},{"type":254,"tag":622,"props":78528,"children":78529},{"style":714},[78530],{"type":259,"value":78531},"Department",{"type":254,"tag":622,"props":78533,"children":78534},{"style":629},[78535],{"type":259,"value":652},{"type":254,"tag":622,"props":78537,"children":78538},{"style":629},[78539],{"type":259,"value":474},{"type":254,"tag":622,"props":78541,"children":78542},{"style":629},[78543],{"type":259,"value":661},{"type":254,"tag":622,"props":78545,"children":78546},{"style":664},[78547],{"type":259,"value":78548},"RH",{"type":254,"tag":622,"props":78550,"children":78551},{"style":629},[78552],{"type":259,"value":652},{"type":254,"tag":622,"props":78554,"children":78555},{"style":629},[78556],{"type":259,"value":676},{"type":254,"tag":622,"props":78558,"children":78559},{"class":624,"line":4276},[78560,78564,78568,78572,78576,78580,78585],{"type":254,"tag":622,"props":78561,"children":78562},{"style":629},[78563],{"type":259,"value":738},{"type":254,"tag":622,"props":78565,"children":78566},{"style":714},[78567],{"type":259,"value":55085},{"type":254,"tag":622,"props":78569,"children":78570},{"style":629},[78571],{"type":259,"value":652},{"type":254,"tag":622,"props":78573,"children":78574},{"style":629},[78575],{"type":259,"value":474},{"type":254,"tag":622,"props":78577,"children":78578},{"style":629},[78579],{"type":259,"value":661},{"type":254,"tag":622,"props":78581,"children":78582},{"style":664},[78583],{"type":259,"value":78584},"Dev",{"type":254,"tag":622,"props":78586,"children":78587},{"style":629},[78588],{"type":259,"value":9909},{"type":254,"tag":622,"props":78590,"children":78591},{"class":624,"line":4332},[78592],{"type":254,"tag":622,"props":78593,"children":78594},{"style":629},[78595],{"type":259,"value":20453},{"type":254,"tag":622,"props":78597,"children":78598},{"class":624,"line":4376},[78599,78603,78607,78611,78615],{"type":254,"tag":622,"props":78600,"children":78601},{"style":629},[78602],{"type":259,"value":711},{"type":254,"tag":622,"props":78604,"children":78605},{"style":644},[78606],{"type":259,"value":78276},{"type":254,"tag":622,"props":78608,"children":78609},{"style":629},[78610],{"type":259,"value":652},{"type":254,"tag":622,"props":78612,"children":78613},{"style":629},[78614],{"type":259,"value":474},{"type":254,"tag":622,"props":78616,"children":78617},{"style":629},[78618],{"type":259,"value":78289},{"type":254,"tag":622,"props":78620,"children":78621},{"class":624,"line":4384},[78622],{"type":254,"tag":622,"props":78623,"children":78624},{"style":629},[78625],{"type":259,"value":78297},{"type":254,"tag":622,"props":78627,"children":78628},{"class":624,"line":4416},[78629],{"type":254,"tag":622,"props":78630,"children":78631},{"style":629},[78632],{"type":259,"value":40525},{"type":254,"tag":622,"props":78634,"children":78635},{"class":624,"line":4456},[78636,78640,78644,78648,78652,78656,78661,78665],{"type":254,"tag":622,"props":78637,"children":78638},{"style":629},[78639],{"type":259,"value":711},{"type":254,"tag":622,"props":78641,"children":78642},{"style":644},[78643],{"type":259,"value":17175},{"type":254,"tag":622,"props":78645,"children":78646},{"style":629},[78647],{"type":259,"value":652},{"type":254,"tag":622,"props":78649,"children":78650},{"style":629},[78651],{"type":259,"value":474},{"type":254,"tag":622,"props":78653,"children":78654},{"style":629},[78655],{"type":259,"value":661},{"type":254,"tag":622,"props":78657,"children":78658},{"style":664},[78659],{"type":259,"value":78660},"/subscriptions/********-****-****-****-************/resourceGroups/TestingAzureCLI",{"type":254,"tag":622,"props":78662,"children":78663},{"style":629},[78664],{"type":259,"value":652},{"type":254,"tag":622,"props":78666,"children":78667},{"style":629},[78668],{"type":259,"value":676},{"type":254,"tag":622,"props":78670,"children":78671},{"class":624,"line":4488},[78672,78676,78680,78684,78688,78692,78696,78700],{"type":254,"tag":622,"props":78673,"children":78674},{"style":629},[78675],{"type":259,"value":711},{"type":254,"tag":622,"props":78677,"children":78678},{"style":644},[78679],{"type":259,"value":48283},{"type":254,"tag":622,"props":78681,"children":78682},{"style":629},[78683],{"type":259,"value":652},{"type":254,"tag":622,"props":78685,"children":78686},{"style":629},[78687],{"type":259,"value":474},{"type":254,"tag":622,"props":78689,"children":78690},{"style":629},[78691],{"type":259,"value":661},{"type":254,"tag":622,"props":78693,"children":78694},{"style":664},[78695],{"type":259,"value":78068},{"type":254,"tag":622,"props":78697,"children":78698},{"style":629},[78699],{"type":259,"value":652},{"type":254,"tag":622,"props":78701,"children":78702},{"style":629},[78703],{"type":259,"value":676},{"type":254,"tag":622,"props":78705,"children":78706},{"class":624,"line":4496},[78707,78711,78715,78719,78723],{"type":254,"tag":622,"props":78708,"children":78709},{"style":629},[78710],{"type":259,"value":711},{"type":254,"tag":622,"props":78712,"children":78713},{"style":644},[78714],{"type":259,"value":78088},{"type":254,"tag":622,"props":78716,"children":78717},{"style":629},[78718],{"type":259,"value":652},{"type":254,"tag":622,"props":78720,"children":78721},{"style":629},[78722],{"type":259,"value":474},{"type":254,"tag":622,"props":78724,"children":78725},{"style":629},[78726],{"type":259,"value":78101},{"type":254,"tag":622,"props":78728,"children":78729},{"class":624,"line":4564},[78730,78734,78738,78742,78746,78750,78754,78758],{"type":254,"tag":622,"props":78731,"children":78732},{"style":629},[78733],{"type":259,"value":711},{"type":254,"tag":622,"props":78735,"children":78736},{"style":644},[78737],{"type":259,"value":16394},{"type":254,"tag":622,"props":78739,"children":78740},{"style":629},[78741],{"type":259,"value":652},{"type":254,"tag":622,"props":78743,"children":78744},{"style":629},[78745],{"type":259,"value":474},{"type":254,"tag":622,"props":78747,"children":78748},{"style":629},[78749],{"type":259,"value":661},{"type":254,"tag":622,"props":78751,"children":78752},{"style":664},[78753],{"type":259,"value":77697},{"type":254,"tag":622,"props":78755,"children":78756},{"style":629},[78757],{"type":259,"value":652},{"type":254,"tag":622,"props":78759,"children":78760},{"style":629},[78761],{"type":259,"value":676},{"type":254,"tag":622,"props":78763,"children":78764},{"class":624,"line":4572},[78765,78769,78773,78777,78781],{"type":254,"tag":622,"props":78766,"children":78767},{"style":629},[78768],{"type":259,"value":711},{"type":254,"tag":622,"props":78770,"children":78771},{"style":644},[78772],{"type":259,"value":78149},{"type":254,"tag":622,"props":78774,"children":78775},{"style":629},[78776],{"type":259,"value":652},{"type":254,"tag":622,"props":78778,"children":78779},{"style":629},[78780],{"type":259,"value":474},{"type":254,"tag":622,"props":78782,"children":78783},{"style":629},[78784],{"type":259,"value":702},{"type":254,"tag":622,"props":78786,"children":78787},{"class":624,"line":4644},[78788,78792,78796,78800,78804,78808,78812],{"type":254,"tag":622,"props":78789,"children":78790},{"style":629},[78791],{"type":259,"value":738},{"type":254,"tag":622,"props":78793,"children":78794},{"style":714},[78795],{"type":259,"value":78173},{"type":254,"tag":622,"props":78797,"children":78798},{"style":629},[78799],{"type":259,"value":652},{"type":254,"tag":622,"props":78801,"children":78802},{"style":629},[78803],{"type":259,"value":474},{"type":254,"tag":622,"props":78805,"children":78806},{"style":629},[78807],{"type":259,"value":661},{"type":254,"tag":622,"props":78809,"children":78810},{"style":664},[78811],{"type":259,"value":78190},{"type":254,"tag":622,"props":78813,"children":78814},{"style":629},[78815],{"type":259,"value":9909},{"type":254,"tag":622,"props":78817,"children":78818},{"class":624,"line":4652},[78819],{"type":254,"tag":622,"props":78820,"children":78821},{"style":629},[78822],{"type":259,"value":20453},{"type":254,"tag":622,"props":78824,"children":78825},{"class":624,"line":4660},[78826,78830,78834,78838,78842],{"type":254,"tag":622,"props":78827,"children":78828},{"style":629},[78829],{"type":259,"value":711},{"type":254,"tag":622,"props":78831,"children":78832},{"style":644},[78833],{"type":259,"value":78213},{"type":254,"tag":622,"props":78835,"children":78836},{"style":629},[78837],{"type":259,"value":652},{"type":254,"tag":622,"props":78839,"children":78840},{"style":629},[78841],{"type":259,"value":474},{"type":254,"tag":622,"props":78843,"children":78844},{"style":629},[78845],{"type":259,"value":702},{"type":254,"tag":622,"props":78847,"children":78848},{"class":624,"line":4692},[78849,78853,78857,78861,78865,78869,78873,78877],{"type":254,"tag":622,"props":78850,"children":78851},{"style":629},[78852],{"type":259,"value":738},{"type":254,"tag":622,"props":78854,"children":78855},{"style":714},[78856],{"type":259,"value":78531},{"type":254,"tag":622,"props":78858,"children":78859},{"style":629},[78860],{"type":259,"value":652},{"type":254,"tag":622,"props":78862,"children":78863},{"style":629},[78864],{"type":259,"value":474},{"type":254,"tag":622,"props":78866,"children":78867},{"style":629},[78868],{"type":259,"value":661},{"type":254,"tag":622,"props":78870,"children":78871},{"style":664},[78872],{"type":259,"value":65789},{"type":254,"tag":622,"props":78874,"children":78875},{"style":629},[78876],{"type":259,"value":652},{"type":254,"tag":622,"props":78878,"children":78879},{"style":629},[78880],{"type":259,"value":676},{"type":254,"tag":622,"props":78882,"children":78883},{"class":624,"line":4700},[78884,78888,78892,78896,78900,78904,78908],{"type":254,"tag":622,"props":78885,"children":78886},{"style":629},[78887],{"type":259,"value":738},{"type":254,"tag":622,"props":78889,"children":78890},{"style":714},[78891],{"type":259,"value":55085},{"type":254,"tag":622,"props":78893,"children":78894},{"style":629},[78895],{"type":259,"value":652},{"type":254,"tag":622,"props":78897,"children":78898},{"style":629},[78899],{"type":259,"value":474},{"type":254,"tag":622,"props":78901,"children":78902},{"style":629},[78903],{"type":259,"value":661},{"type":254,"tag":622,"props":78905,"children":78906},{"style":664},[78907],{"type":259,"value":78253},{"type":254,"tag":622,"props":78909,"children":78910},{"style":629},[78911],{"type":259,"value":9909},{"type":254,"tag":622,"props":78913,"children":78914},{"class":624,"line":4756},[78915],{"type":254,"tag":622,"props":78916,"children":78917},{"style":629},[78918],{"type":259,"value":20453},{"type":254,"tag":622,"props":78920,"children":78921},{"class":624,"line":4808},[78922,78926,78930,78934,78938],{"type":254,"tag":622,"props":78923,"children":78924},{"style":629},[78925],{"type":259,"value":711},{"type":254,"tag":622,"props":78927,"children":78928},{"style":644},[78929],{"type":259,"value":78276},{"type":254,"tag":622,"props":78931,"children":78932},{"style":629},[78933],{"type":259,"value":652},{"type":254,"tag":622,"props":78935,"children":78936},{"style":629},[78937],{"type":259,"value":474},{"type":254,"tag":622,"props":78939,"children":78940},{"style":629},[78941],{"type":259,"value":78289},{"type":254,"tag":622,"props":78943,"children":78944},{"class":624,"line":4864},[78945],{"type":254,"tag":622,"props":78946,"children":78947},{"style":629},[78948],{"type":259,"value":1102},{"type":254,"tag":622,"props":78950,"children":78951},{"class":624,"line":4892},[78952],{"type":254,"tag":622,"props":78953,"children":78954},{"style":629},[78955],{"type":259,"value":11003},{"type":254,"tag":255,"props":78957,"children":78958},{},[78959],{"type":259,"value":78960},"Here are some CLI commands that query more precisely the resource groups:",{"type":254,"tag":332,"props":78962,"children":78963},{},[78964],{"type":254,"tag":336,"props":78965,"children":78966},{},[78967,78969],{"type":259,"value":78968},"Select only the resource groups names with ",{"type":254,"tag":389,"props":78970,"children":78972},{"className":78971},[],[78973],{"type":259,"value":78974},"az group list --query '[].name'",{"type":254,"tag":255,"props":78976,"children":78977},{},[78978],{"type":254,"tag":1161,"props":78979,"children":78983},{"alt":78980,"className":78981,"src":78982,"width":19735},"az group command output using JMESPath property selection.",[1165,1166],"/posts/images/azurecli_jmespath_2.png",[],{"type":254,"tag":332,"props":78985,"children":78986},{},[78987],{"type":254,"tag":336,"props":78988,"children":78989},{},[78990,78992],{"type":259,"value":78991},"Filter on resource groups in West Europe with ",{"type":254,"tag":389,"props":78993,"children":78995},{"className":78994},[],[78996],{"type":259,"value":78997},"az group list --query \"[?location=='westeurope']\"",{"type":254,"tag":255,"props":78999,"children":79000},{},[79001],{"type":254,"tag":1161,"props":79002,"children":79006},{"alt":79003,"className":79004,"src":79005,"width":19735},"az group command output using JMESPath property filter.",[1165,1166],"/posts/images/azurecli_jmespath_1.png",[],{"type":254,"tag":332,"props":79008,"children":79009},{},[79010],{"type":254,"tag":336,"props":79011,"children":79012},{},[79013,79015],{"type":259,"value":79014},"Filter on resource groups with an Environment tag with the value Production, select and rename name and tags properties with ",{"type":254,"tag":389,"props":79016,"children":79018},{"className":79017},[],[79019],{"type":259,"value":79020},"az group list --query \"[?tags.Environment=='Production'].{RGName:name, RGTags:tags}\"",{"type":254,"tag":255,"props":79022,"children":79023},{},[79024],{"type":254,"tag":1161,"props":79025,"children":79029},{"alt":79026,"className":79027,"src":79028,"width":19735},"az group command output using JMESPath property filter and selection.",[1165,1166],"/posts/images/azurecli_jmespath_3.png",[],{"type":254,"tag":427,"props":79031,"children":79032},{"icon":68963},[79033],{"type":254,"tag":255,"props":79034,"children":79035},{},[79036,79038,79045],{"type":259,"value":79037},"JMESPath is not an Azure CLI only thing, it is used in different other projects or tools like the ",{"type":254,"tag":262,"props":79039,"children":79042},{"href":79040,"rel":79041},"https://docs.aws.amazon.com/cli/latest/userguide/cli-usage-output.html#controlling-output-filter",[266],[79043],{"type":259,"value":79044},"AWS CLI",{"type":259,"value":594},{"type":254,"tag":455,"props":79047,"children":79049},{"id":79048},"mixing-output-variables-and-jmespath",[79050],{"type":259,"value":79051},"Mixing output, variables, and JMESPath",{"type":254,"tag":255,"props":79053,"children":79054},{},[79055],{"type":259,"value":79056},"Once you have understood how to use variables, commands output, and JMESPath with Azure CLI you can mix everything to manage Azure resources with CLI commands that use results of previous CLI commands, which can make powerful scripts 😀.",{"type":254,"tag":255,"props":79058,"children":79059},{},[79060],{"type":259,"value":79061},"Let's presume that we want to create a Web App for the IT Department in an existing App Service Plan of our subscription located in West Europe.\nWe can do the following:",{"type":254,"tag":612,"props":79063,"children":79065},{"className":10656,"code":79064,"language":10658,"meta":205,"style":205},"# Retrieve resource group name of IT Department in West Europe\n$itRgName = az group list --query \"[?tags.Department=='IT' && location=='westeurope'].name\" -o tsv\n\n# Retrieve app service plan name in this resource group\n$appServicePlan = az appservice plan list -g $itRgName --query \"[0].name\" -o tsv\n\n# Create a new Web App on this app service plan\naz webapp create -n \"MyNewWebAp\" -g $itRgName --plan $appServicePlan\n\n# List all Web App in this resource group\naz webapp list -g $itRgName --query \"[].name\"\n",[79066],{"type":254,"tag":389,"props":79067,"children":79068},{"__ignoreMap":205},[79069,79077,79127,79134,79142,79208,79215,79223,79286,79293,79301],{"type":254,"tag":622,"props":79070,"children":79071},{"class":624,"line":625},[79072],{"type":254,"tag":622,"props":79073,"children":79074},{"style":3195},[79075],{"type":259,"value":79076},"# Retrieve resource group name of IT Department in West Europe\n",{"type":254,"tag":622,"props":79078,"children":79079},{"class":624,"line":635},[79080,79084,79089,79093,79098,79102,79106,79110,79115,79119,79123],{"type":254,"tag":622,"props":79081,"children":79082},{"style":629},[79083],{"type":259,"value":16922},{"type":254,"tag":622,"props":79085,"children":79086},{"style":1202},[79087],{"type":259,"value":79088},"itRgName ",{"type":254,"tag":622,"props":79090,"children":79091},{"style":629},[79092],{"type":259,"value":1654},{"type":254,"tag":622,"props":79094,"children":79095},{"style":1202},[79096],{"type":259,"value":79097}," az group list ",{"type":254,"tag":622,"props":79099,"children":79100},{"style":629},[79101],{"type":259,"value":10675},{"type":254,"tag":622,"props":79103,"children":79104},{"style":1202},[79105],{"type":259,"value":17166},{"type":254,"tag":622,"props":79107,"children":79108},{"style":629},[79109],{"type":259,"value":652},{"type":254,"tag":622,"props":79111,"children":79112},{"style":664},[79113],{"type":259,"value":79114},"[?tags.Department=='IT' && location=='westeurope'].name",{"type":254,"tag":622,"props":79116,"children":79117},{"style":629},[79118],{"type":259,"value":652},{"type":254,"tag":622,"props":79120,"children":79121},{"style":629},[79122],{"type":259,"value":10733},{"type":254,"tag":622,"props":79124,"children":79125},{"style":1202},[79126],{"type":259,"value":49211},{"type":254,"tag":622,"props":79128,"children":79129},{"class":624,"line":679},[79130],{"type":254,"tag":622,"props":79131,"children":79132},{"emptyLinePlaceholder":1699},[79133],{"type":259,"value":1702},{"type":254,"tag":622,"props":79135,"children":79136},{"class":624,"line":705},[79137],{"type":254,"tag":622,"props":79138,"children":79139},{"style":3195},[79140],{"type":259,"value":79141},"# Retrieve app service plan name in this resource group\n",{"type":254,"tag":622,"props":79143,"children":79144},{"class":624,"line":732},[79145,79149,79154,79158,79163,79167,79171,79175,79179,79183,79187,79191,79196,79200,79204],{"type":254,"tag":622,"props":79146,"children":79147},{"style":629},[79148],{"type":259,"value":16922},{"type":254,"tag":622,"props":79150,"children":79151},{"style":1202},[79152],{"type":259,"value":79153},"appServicePlan ",{"type":254,"tag":622,"props":79155,"children":79156},{"style":629},[79157],{"type":259,"value":1654},{"type":254,"tag":622,"props":79159,"children":79160},{"style":1202},[79161],{"type":259,"value":79162}," az appservice plan list ",{"type":254,"tag":622,"props":79164,"children":79165},{"style":629},[79166],{"type":259,"value":17065},{"type":254,"tag":622,"props":79168,"children":79169},{"style":1202},[79170],{"type":259,"value":48620},{"type":254,"tag":622,"props":79172,"children":79173},{"style":629},[79174],{"type":259,"value":16922},{"type":254,"tag":622,"props":79176,"children":79177},{"style":1202},[79178],{"type":259,"value":79088},{"type":254,"tag":622,"props":79180,"children":79181},{"style":629},[79182],{"type":259,"value":10675},{"type":254,"tag":622,"props":79184,"children":79185},{"style":1202},[79186],{"type":259,"value":17166},{"type":254,"tag":622,"props":79188,"children":79189},{"style":629},[79190],{"type":259,"value":652},{"type":254,"tag":622,"props":79192,"children":79193},{"style":664},[79194],{"type":259,"value":79195},"[0].name",{"type":254,"tag":622,"props":79197,"children":79198},{"style":629},[79199],{"type":259,"value":652},{"type":254,"tag":622,"props":79201,"children":79202},{"style":629},[79203],{"type":259,"value":10733},{"type":254,"tag":622,"props":79205,"children":79206},{"style":1202},[79207],{"type":259,"value":49211},{"type":254,"tag":622,"props":79209,"children":79210},{"class":624,"line":759},[79211],{"type":254,"tag":622,"props":79212,"children":79213},{"emptyLinePlaceholder":1699},[79214],{"type":259,"value":1702},{"type":254,"tag":622,"props":79216,"children":79217},{"class":624,"line":798},[79218],{"type":254,"tag":622,"props":79219,"children":79220},{"style":3195},[79221],{"type":259,"value":79222},"# Create a new Web App on this app service plan\n",{"type":254,"tag":622,"props":79224,"children":79225},{"class":624,"line":836},[79226,79231,79235,79239,79243,79248,79252,79256,79260,79264,79268,79272,79277,79281],{"type":254,"tag":622,"props":79227,"children":79228},{"style":1202},[79229],{"type":259,"value":79230},"az webapp create ",{"type":254,"tag":622,"props":79232,"children":79233},{"style":629},[79234],{"type":259,"value":17065},{"type":254,"tag":622,"props":79236,"children":79237},{"style":1202},[79238],{"type":259,"value":48602},{"type":254,"tag":622,"props":79240,"children":79241},{"style":629},[79242],{"type":259,"value":652},{"type":254,"tag":622,"props":79244,"children":79245},{"style":664},[79246],{"type":259,"value":79247},"MyNewWebAp",{"type":254,"tag":622,"props":79249,"children":79250},{"style":629},[79251],{"type":259,"value":652},{"type":254,"tag":622,"props":79253,"children":79254},{"style":629},[79255],{"type":259,"value":10733},{"type":254,"tag":622,"props":79257,"children":79258},{"style":1202},[79259],{"type":259,"value":48620},{"type":254,"tag":622,"props":79261,"children":79262},{"style":629},[79263],{"type":259,"value":16922},{"type":254,"tag":622,"props":79265,"children":79266},{"style":1202},[79267],{"type":259,"value":79088},{"type":254,"tag":622,"props":79269,"children":79270},{"style":629},[79271],{"type":259,"value":10675},{"type":254,"tag":622,"props":79273,"children":79274},{"style":1202},[79275],{"type":259,"value":79276},"plan ",{"type":254,"tag":622,"props":79278,"children":79279},{"style":629},[79280],{"type":259,"value":16922},{"type":254,"tag":622,"props":79282,"children":79283},{"style":1202},[79284],{"type":259,"value":79285},"appServicePlan\n",{"type":254,"tag":622,"props":79287,"children":79288},{"class":624,"line":862},[79289],{"type":254,"tag":622,"props":79290,"children":79291},{"emptyLinePlaceholder":1699},[79292],{"type":259,"value":1702},{"type":254,"tag":622,"props":79294,"children":79295},{"class":624,"line":900},[79296],{"type":254,"tag":622,"props":79297,"children":79298},{"style":3195},[79299],{"type":259,"value":79300},"# List all Web App in this resource group\n",{"type":254,"tag":622,"props":79302,"children":79303},{"class":624,"line":938},[79304,79309,79313,79317,79321,79325,79329,79333,79337,79342],{"type":254,"tag":622,"props":79305,"children":79306},{"style":1202},[79307],{"type":259,"value":79308},"az webapp list ",{"type":254,"tag":622,"props":79310,"children":79311},{"style":629},[79312],{"type":259,"value":17065},{"type":254,"tag":622,"props":79314,"children":79315},{"style":1202},[79316],{"type":259,"value":48620},{"type":254,"tag":622,"props":79318,"children":79319},{"style":629},[79320],{"type":259,"value":16922},{"type":254,"tag":622,"props":79322,"children":79323},{"style":1202},[79324],{"type":259,"value":79088},{"type":254,"tag":622,"props":79326,"children":79327},{"style":629},[79328],{"type":259,"value":10675},{"type":254,"tag":622,"props":79330,"children":79331},{"style":1202},[79332],{"type":259,"value":17166},{"type":254,"tag":622,"props":79334,"children":79335},{"style":629},[79336],{"type":259,"value":652},{"type":254,"tag":622,"props":79338,"children":79339},{"style":664},[79340],{"type":259,"value":79341},"[].name",{"type":254,"tag":622,"props":79343,"children":79344},{"style":629},[79345],{"type":259,"value":9909},{"type":254,"tag":255,"props":79347,"children":79348},{},[79349],{"type":259,"value":79350},"This is just a quick example but you can easily imagine all the possibilities that Azure CLI offers you.",{"type":254,"tag":282,"props":79352,"children":79354},{"id":79353},"_4-where-to-use-azure-cli",[79355],{"type":259,"value":79356},"4 - Where to use Azure CLI",{"type":254,"tag":255,"props":79358,"children":79359},{},[79360],{"type":259,"value":79361},"All that is great but we haven't talked about where we can use Azure CLI, let's do that now!",{"type":254,"tag":455,"props":79363,"children":79365},{"id":79364},"bash-vs-powershell",[79366],{"type":259,"value":79367},"Bash vs PowerShell",{"type":254,"tag":255,"props":79369,"children":79370},{},[79371],{"type":259,"value":79372},"We briefly mentioned that you can run Azure CLI in Bash or PowerShell, and as a matter of fact you can also run it in Windows Command Prompt.\nAlthough Azure CLI is originally designed to be bash-oriented, it works fine in PowerShell so it's a perfectly valid choice too. What command line shell you choose is up to you!",{"type":254,"tag":255,"props":79374,"children":79375},{},[79376],{"type":259,"value":79377},"And don't think that you will be limited to using one or the other shell depending on the platform you are working on, because both PowerShell and Bash are available whatever your platform (PowerShell Core is cross-platform and Bash is available in Windows through Windows Subsystem for Linux). Therefore I would just suggest you use Azure CLI with the shell you are most familiar with.",{"type":254,"tag":255,"props":79379,"children":79380},{},[79381,79383,79389,79391,79398],{"type":259,"value":79382},"Just a quick note about autocompletion: at this time tab completion is only supported in bash and Microsoft currently does not plan to add support for PowerShell but is open to contributions from the community. That's not a big deal but that's good to know, there is a ",{"type":254,"tag":262,"props":79384,"children":79387},{"href":79385,"rel":79386},"https://github.com/Azure/azure-cli/issues/2324",[266],[79388],{"type":259,"value":58700},{"type":259,"value":79390}," on this matter. If you are not using bash and that tab completion is important for you, ",{"type":254,"tag":262,"props":79392,"children":79395},{"href":79393,"rel":79394},"https://docs.microsoft.com/en-us/cli/azure/interactive-azure-cli?view=azure-cli-latest",[266],[79396],{"type":259,"value":79397},"Azure CLI interactive mode",{"type":259,"value":79399}," is what you are looking for (we will talk about it later in the article)!",{"type":254,"tag":455,"props":79401,"children":79403},{"id":79402},"azure-cli-in-visual-studio-code",[79404],{"type":259,"value":79405},"Azure CLI in Visual Studio Code",{"type":254,"tag":255,"props":79407,"children":79408},{},[79409],{"type":259,"value":79410},"As for most of Azure components, there is an extension in vs code for Azure CLI: Azure CLI Tools.",{"type":254,"tag":255,"props":79412,"children":79413},{},[79414],{"type":254,"tag":1161,"props":79415,"children":79419},{"alt":79416,"className":79417,"src":79418},"Azure CLI Tools vscode extension.",[1165,1166],"/posts/images/azurecli_vscode_1.png",[],{"type":254,"tag":255,"props":79421,"children":79422},{},[79423,79425,79430],{"type":259,"value":79424},"With this extension you can create Azure CLI Scrapbooks which are files with an ",{"type":254,"tag":389,"props":79426,"children":79428},{"className":79427},[],[79429],{"type":259,"value":48200},{"type":259,"value":79431}," extension where you have IntelliSense on CLI commands you write.",{"type":254,"tag":255,"props":79433,"children":79434},{},[79435],{"type":254,"tag":1161,"props":79436,"children":79440},{"alt":79437,"className":79438,"src":79439,"width":19735},"Intellisense with Azure CLI vscode extension.",[1165,1166],"/posts/images/azurecli_vscode_3.png",[],{"type":254,"tag":255,"props":79442,"children":79443},{},[79444],{"type":259,"value":79445},"It also allows you to run commands in the integrated terminal or run them and show their output in a side-by-side editor as showN in the screenshot below.",{"type":254,"tag":255,"props":79447,"children":79448},{},[79449],{"type":254,"tag":1161,"props":79450,"children":79454},{"alt":79451,"className":79452,"src":79453,"width":19735},"Azcli file with integrated integrated terminal.",[1165,1166],"/posts/images/azurecli_vscode_2.png",[],{"type":254,"tag":455,"props":79456,"children":79458},{"id":79457},"azure-cloud-shell",[79459],{"type":259,"value":79460},"Azure Cloud Shell",{"type":254,"tag":255,"props":79462,"children":79463},{},[79464,79466,79471],{"type":259,"value":79465},"I did not talk about Azure CLI installation but you can find everything you need in ",{"type":254,"tag":262,"props":79467,"children":79469},{"href":77304,"rel":79468},[266],[79470],{"type":259,"value":42210},{"type":259,"value":79472},". Actually, Azure Cloud Shell provides you with a way to use Azure CLI without installing anything.",{"type":254,"tag":255,"props":79474,"children":79475},{},[79476],{"type":259,"value":79477},"If we quote the documentation:",{"type":254,"tag":31352,"props":79479,"children":79480},{},[79481],{"type":254,"tag":255,"props":79482,"children":79483},{},[79484],{"type":259,"value":79485},"Azure Cloud Shell is an interactive, browser-accessible shell for managing Azure resources",{"type":254,"tag":255,"props":79487,"children":79488},{},[79489,79491,79497],{"type":259,"value":79490},"In concrete terms, you open a browser, go to ",{"type":254,"tag":262,"props":79492,"children":79495},{"href":79493,"rel":79494},"https://shell.azure.com",[266],[79496],{"type":259,"value":79493},{"type":259,"value":79498},", log in to your Azure subscription and you have access to a shell (PowerShell or Bash) that can interact with all the resources of your subscription through Azure CLI.",{"type":254,"tag":255,"props":79500,"children":79501},{},[79502],{"type":254,"tag":1161,"props":79503,"children":79507},{"alt":79504,"className":79505,"src":79506,"width":19735},"Azure cloud shell in browser.",[1165,1166],"/posts/images/azurecli_cloudshell_1.png",[],{"type":254,"tag":255,"props":79509,"children":79510},{},[79511],{"type":259,"value":79512},"Cloud Shell in itself is free but requires an Azure file share to be mounted so you will have regular storage costs (which are low).",{"type":254,"tag":255,"props":79514,"children":79515},{},[79516,79518,79524],{"type":259,"value":79517},"Azure Cloud Shell is also available in the Azure Portal or directly in your vs code integrated terminal through the ",{"type":254,"tag":262,"props":79519,"children":79521},{"href":65036,"rel":79520},[266],[79522],{"type":259,"value":79523},"Azure Account extension",{"type":259,"value":594},{"type":254,"tag":282,"props":79526,"children":79528},{"id":79527},"_5-azure-cli-interactive-mode",[79529],{"type":259,"value":79530},"5 - Azure CLI Interactive mode",{"type":254,"tag":255,"props":79532,"children":79533},{},[79534],{"type":259,"value":79535},"I mentioned Azure CLI interactive mode as a way to have auto-completion. In fact, it is much more than that as it also provides you with command descriptions, examples, completion on resources names, JMESPath on the previous command...",{"type":254,"tag":255,"props":79537,"children":79538},{},[79539,79541,79547],{"type":259,"value":79540},"To enter in interactive mode, you have to type ",{"type":254,"tag":389,"props":79542,"children":79544},{"className":79543},[],[79545],{"type":259,"value":79546},"az interactive",{"type":259,"value":79548}," in your terminal.\nThen you can type your CLI commands with autocompletion, a description of the command you are using, and examples for it. Moreover, while you are typing a command you will see help about the arguments for this command.",{"type":254,"tag":255,"props":79550,"children":79551},{},[79552],{"type":254,"tag":1161,"props":79553,"children":79557},{"alt":79554,"className":79555,"src":79556,"width":19735},"Azure CLI interactive mode display.",[1165,1166],"/posts/images/azurecli_interactive_1.png",[],{"type":254,"tag":255,"props":79559,"children":79560},{},[79561,79563,79568,79570,79575,79577,79583,79584,79589],{"type":259,"value":79562},"One thing to notice is that commands you type are scoped to a command group. By default, it's scoped to ",{"type":254,"tag":389,"props":79564,"children":79566},{"className":79565},[],[79567],{"type":259,"value":41000},{"type":259,"value":79569}," group so you don't have to type the ",{"type":254,"tag":389,"props":79571,"children":79573},{"className":79572},[],[79574],{"type":259,"value":41000},{"type":259,"value":79576}," keyword before your commands. So by default, it only saves you two characters as you will write ",{"type":254,"tag":389,"props":79578,"children":79580},{"className":79579},[],[79581],{"type":259,"value":79582},"group list",{"type":259,"value":24283},{"type":254,"tag":389,"props":79585,"children":79587},{"className":79586},[],[79588],{"type":259,"value":77980},{"type":259,"value":79590},". But you can set the scope to any subgroup as you can see in the following example.",{"type":254,"tag":255,"props":79592,"children":79593},{},[79594],{"type":254,"tag":1161,"props":79595,"children":79599},{"alt":79596,"className":79597,"src":79598},"Scoped commands in Azure CLI interactive mode.",[1165,1166],"/posts/images/azurecli_interactive_2.png",[],{"type":254,"tag":255,"props":79601,"children":79602},{},[79603,79605,79610,79612,79618],{"type":259,"value":79604},"Another nice thing you can do with the interactive mode is to query the results of your previous command using the JMESPath syntax.\nImagine I just have executed the command ",{"type":254,"tag":389,"props":79606,"children":79608},{"className":79607},[],[79609],{"type":259,"value":79582},{"type":259,"value":79611}," in interactive mode. If I want to select only the names in the results of this command, I can just type ",{"type":254,"tag":389,"props":79613,"children":79615},{"className":79614},[],[79616],{"type":259,"value":79617},"\"?? [*].name\"",{"type":259,"value":594},{"type":254,"tag":255,"props":79620,"children":79621},{},[79622],{"type":254,"tag":1161,"props":79623,"children":79627},{"alt":79624,"className":79625,"src":79626},"JMESPath examples to filter Azure CLI interactive output.",[1165,1166],"/posts/images/azurecli_interactive_3.png",[],{"type":254,"tag":255,"props":79629,"children":79630},{},[79631],{"type":259,"value":79632},"As you can see in the example above, you can even use a JMESPath query on the previous command as an argument for your next command. Here we are listing all resources on the TestingAzureCLI group.",{"type":254,"tag":427,"props":79634,"children":79635},{"icon":68963},[79636],{"type":254,"tag":255,"props":79637,"children":79638},{},[79639,79641,79647],{"type":259,"value":79640},"It can sometimes be a little slow to use Azure interactive mode on ",{"type":254,"tag":262,"props":79642,"children":79645},{"href":79643,"rel":79644},"https://shell.azure.com/",[266],[79646],{"type":259,"value":79643},{"type":259,"value":79648},", so if it happens to you I suggest you use the vs code integrated terminal (if you want to use Azure Cloud Shell) or any terminal you want (if you have the Azure CLI installed) instead of the browser.",{"type":254,"tag":427,"props":79650,"children":79651},{"icon":68963},[79652],{"type":254,"tag":255,"props":79653,"children":79654},{},[79655,79657,79664],{"type":259,"value":79656},"If you like interactive CLI, you can also have a look at ",{"type":254,"tag":262,"props":79658,"children":79661},{"href":79659,"rel":79660},"https://github.com/lawrencegripper/azbrowse",[266],[79662],{"type":259,"value":79663},"AzBrowse",{"type":259,"value":79665}," which is a nice community project.",{"type":254,"tag":282,"props":79667,"children":79668},{"id":10572},[79669],{"type":259,"value":10575},{"type":254,"tag":255,"props":79671,"children":79672},{},[79673],{"type":259,"value":79674},"Even if the title of this article suggests saying goodbye to Azure Portal, when you start using Azure CLI you are not going to stop completely using Azure Portal. You will always need a GUI for some tasks or to better visualize things. However, you will probably Azure Portal use less often as you are going to find yourself more productive with Azure CLI and will enjoy being able to script everything ✨.",{"type":254,"tag":255,"props":79676,"children":79677},{},[79678],{"type":259,"value":79679},"There is still much to say about Azure CLI but that's already a long article so the best thing is that you try it by yourself!",{"type":254,"tag":1260,"props":79681,"children":79682},{},[79683],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":79685},[79686,79687,79688,79694,79699,79700],{"id":77312,"depth":635,"text":77315},{"id":77366,"depth":635,"text":77369},{"id":77578,"depth":635,"text":77581,"children":79689},[79690,79691,79692,79693],{"id":77584,"depth":679,"text":77587},{"id":77650,"depth":679,"text":77653},{"id":77941,"depth":679,"text":77944},{"id":79048,"depth":679,"text":79051},{"id":79353,"depth":635,"text":79356,"children":79695},[79696,79697,79698],{"id":79364,"depth":679,"text":79367},{"id":79402,"depth":679,"text":79405},{"id":79457,"depth":679,"text":79460},{"id":79527,"depth":635,"text":79530},{"id":10572,"depth":635,"text":10575},"content:1.posts:3.welcome-azure-cli.md","1.posts/3.welcome-azure-cli.md",{"_path":13,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":12,"description":79704,"lead":79705,"date":79706,"image":79707,"badge":79709,"tags":79710,"body":79714,"_type":1275,"_id":81303,"_source":231,"_file":81304,"_extension":1278},"There are often situations where you need to do some HTML templating and having a powerful HTML templating engine like Razor can be really helpful.\nWhat is nice is that you don't need to be in an ASP.NET context to use Razor templates, in fact, you can even use them in a Xamarin Application.","Razor templates, Handlebars.Net ...","2019-03-10T00:00:00.000Z",{"src":79708},"/images/phone_2.jpg",{"label":1286},[79711,79712,79713],"Razor","Xamarin","Templating",{"type":251,"children":79715,"toc":81299},[79716,79720,79726,79766,79775,79804,79813,79818,79823,80150,80155,80423,80428,80610,80616,80639,81129,81134,81254,81282,81287,81290,81295],{"type":254,"tag":255,"props":79717,"children":79718},{},[79719],{"type":259,"value":79704},{"type":254,"tag":282,"props":79721,"children":79723},{"id":79722},"razor-templates",[79724],{"type":259,"value":79725},"Razor templates",{"type":254,"tag":255,"props":79727,"children":79728},{},[79729,79731,79737,79739,79745,79747,79752,79753,79758,79760,79764],{"type":259,"value":79730},"There is already a complete article about Razor HTML templates in Xamarin in the ",{"type":254,"tag":262,"props":79732,"children":79735},{"href":79733,"rel":79734},"https://docs.microsoft.com/en-us/xamarin/cross-platform/platform/razor-html-templates/",[266],[79736],{"type":259,"value":42210},{"type":259,"value":79738}," so if you want an in-depth explanation I suggest you read it. In this article, it is explained how to add a Razor template file (",{"type":254,"tag":389,"props":79740,"children":79742},{"className":79741},[],[79743],{"type":259,"value":79744},".cshtml",{"type":259,"value":79746}," file) to a Xamarin project by using the ",{"type":254,"tag":19837,"props":79748,"children":79749},{},[79750],{"type":259,"value":79751},"Text Templating",{"type":259,"value":31498},{"type":254,"tag":19837,"props":79754,"children":79755},{},[79756],{"type":259,"value":79757},"New file",{"type":259,"value":79759}," dialog.\nHowever, if you try to add a new item in a Xamarin project you won't see a ",{"type":254,"tag":19837,"props":79761,"children":79762},{},[79763],{"type":259,"value":79751},{"type":259,"value":79765}," section.",{"type":254,"tag":255,"props":79767,"children":79768},{},[79769],{"type":254,"tag":1161,"props":79770,"children":79774},{"alt":79771,"className":79772,"src":79773},"Add new item in Xamarin Project in Visual Studio",[1165,1166],"/posts/images/htmltemplating_vs_1.png",[],{"type":254,"tag":255,"props":79776,"children":79777},{},[79778,79780,79787,79789,79794,79796,79802],{"type":259,"value":79779},"That's because, at the time of writing this article, it only exists in Visual Studio for Mac (probably because it comes from Xamarin Studio). So how to add a Razor template file in Visual Studio (Windows)?\nThere is a little tip to do that (I found it in an old post on ",{"type":254,"tag":262,"props":79781,"children":79784},{"href":79782,"rel":79783},"https://stackoverflow.com/questions/39048900/is-there-a-preprocessed-razor-template-for-visual-studio-2015",[266],[79785],{"type":259,"value":79786},"StackOverflow",{"type":259,"value":79788},"): you have to manually add a ",{"type":254,"tag":389,"props":79790,"children":79792},{"className":79791},[],[79793],{"type":259,"value":79744},{"type":259,"value":79795}," file to your project and set the custom tool to ",{"type":254,"tag":389,"props":79797,"children":79799},{"className":79798},[],[79800],{"type":259,"value":79801},"RazorTemplatePreprocessor",{"type":259,"value":79803}," in the properties of the file (this will generate the code-behind file).",{"type":254,"tag":255,"props":79805,"children":79806},{},[79807],{"type":254,"tag":1161,"props":79808,"children":79812},{"alt":79809,"className":79810,"src":79811},"File properties of a cshtml file in Visual Studio",[1165,1166],"/posts/images/htmltemplating_vs_2.png",[],{"type":254,"tag":255,"props":79814,"children":79815},{},[79816],{"type":259,"value":79817},"Then you can generate an HTML string from your Razor template and your data in your Xamarin project.",{"type":254,"tag":255,"props":79819,"children":79820},{},[79821],{"type":259,"value":79822},"Code to generate the HTML string:",{"type":254,"tag":612,"props":79824,"children":79826},{"className":1423,"code":79825,"language":1425,"meta":205,"style":205},"var people = new Character[]\n{\n    new Character() { FirstName = \"Ellana\", LastName = \"Caldin\", Job = \"Marchombre\"},\n    new Character() { FirstName = \"Edwin\", LastName = \"Til'Illan\", Job = \"General\"}\n};\nvar template = new RazorTemplate() { Model =  people};\nvar page = template.GenerateString();\n",[79827],{"type":254,"tag":389,"props":79828,"children":79829},{"__ignoreMap":205},[79830,79859,79866,79965,80059,80066,80117],{"type":254,"tag":622,"props":79831,"children":79832},{"class":624,"line":625},[79833,79837,79842,79846,79850,79854],{"type":254,"tag":622,"props":79834,"children":79835},{"style":714},[79836],{"type":259,"value":1710},{"type":254,"tag":622,"props":79838,"children":79839},{"style":714},[79840],{"type":259,"value":79841}," people",{"type":254,"tag":622,"props":79843,"children":79844},{"style":629},[79845],{"type":259,"value":1619},{"type":254,"tag":622,"props":79847,"children":79848},{"style":629},[79849],{"type":259,"value":1624},{"type":254,"tag":622,"props":79851,"children":79852},{"style":714},[79853],{"type":259,"value":64296},{"type":254,"tag":622,"props":79855,"children":79856},{"style":629},[79857],{"type":259,"value":79858},"[]\n",{"type":254,"tag":622,"props":79860,"children":79861},{"class":624,"line":635},[79862],{"type":254,"tag":622,"props":79863,"children":79864},{"style":629},[79865],{"type":259,"value":632},{"type":254,"tag":622,"props":79867,"children":79868},{"class":624,"line":679},[79869,79874,79878,79882,79886,79891,79895,79899,79904,79908,79912,79917,79921,79925,79930,79934,79938,79943,79947,79951,79956,79960],{"type":254,"tag":622,"props":79870,"children":79871},{"style":629},[79872],{"type":259,"value":79873},"    new",{"type":254,"tag":622,"props":79875,"children":79876},{"style":714},[79877],{"type":259,"value":64296},{"type":254,"tag":622,"props":79879,"children":79880},{"style":629},[79881],{"type":259,"value":2724},{"type":254,"tag":622,"props":79883,"children":79884},{"style":629},[79885],{"type":259,"value":1990},{"type":254,"tag":622,"props":79887,"children":79888},{"style":1202},[79889],{"type":259,"value":79890}," FirstName ",{"type":254,"tag":622,"props":79892,"children":79893},{"style":629},[79894],{"type":259,"value":1654},{"type":254,"tag":622,"props":79896,"children":79897},{"style":629},[79898],{"type":259,"value":661},{"type":254,"tag":622,"props":79900,"children":79901},{"style":664},[79902],{"type":259,"value":79903},"Ellana",{"type":254,"tag":622,"props":79905,"children":79906},{"style":629},[79907],{"type":259,"value":652},{"type":254,"tag":622,"props":79909,"children":79910},{"style":629},[79911],{"type":259,"value":3149},{"type":254,"tag":622,"props":79913,"children":79914},{"style":1202},[79915],{"type":259,"value":79916}," LastName ",{"type":254,"tag":622,"props":79918,"children":79919},{"style":629},[79920],{"type":259,"value":1654},{"type":254,"tag":622,"props":79922,"children":79923},{"style":629},[79924],{"type":259,"value":661},{"type":254,"tag":622,"props":79926,"children":79927},{"style":664},[79928],{"type":259,"value":79929},"Caldin",{"type":254,"tag":622,"props":79931,"children":79932},{"style":629},[79933],{"type":259,"value":652},{"type":254,"tag":622,"props":79935,"children":79936},{"style":629},[79937],{"type":259,"value":3149},{"type":254,"tag":622,"props":79939,"children":79940},{"style":1202},[79941],{"type":259,"value":79942}," Job ",{"type":254,"tag":622,"props":79944,"children":79945},{"style":629},[79946],{"type":259,"value":1654},{"type":254,"tag":622,"props":79948,"children":79949},{"style":629},[79950],{"type":259,"value":661},{"type":254,"tag":622,"props":79952,"children":79953},{"style":664},[79954],{"type":259,"value":79955},"Marchombre",{"type":254,"tag":622,"props":79957,"children":79958},{"style":629},[79959],{"type":259,"value":652},{"type":254,"tag":622,"props":79961,"children":79962},{"style":629},[79963],{"type":259,"value":79964},"},\n",{"type":254,"tag":622,"props":79966,"children":79967},{"class":624,"line":705},[79968,79972,79976,79980,79984,79988,79992,79996,80001,80005,80009,80013,80017,80021,80026,80030,80034,80038,80042,80046,80051,80055],{"type":254,"tag":622,"props":79969,"children":79970},{"style":629},[79971],{"type":259,"value":79873},{"type":254,"tag":622,"props":79973,"children":79974},{"style":714},[79975],{"type":259,"value":64296},{"type":254,"tag":622,"props":79977,"children":79978},{"style":629},[79979],{"type":259,"value":2724},{"type":254,"tag":622,"props":79981,"children":79982},{"style":629},[79983],{"type":259,"value":1990},{"type":254,"tag":622,"props":79985,"children":79986},{"style":1202},[79987],{"type":259,"value":79890},{"type":254,"tag":622,"props":79989,"children":79990},{"style":629},[79991],{"type":259,"value":1654},{"type":254,"tag":622,"props":79993,"children":79994},{"style":629},[79995],{"type":259,"value":661},{"type":254,"tag":622,"props":79997,"children":79998},{"style":664},[79999],{"type":259,"value":80000},"Edwin",{"type":254,"tag":622,"props":80002,"children":80003},{"style":629},[80004],{"type":259,"value":652},{"type":254,"tag":622,"props":80006,"children":80007},{"style":629},[80008],{"type":259,"value":3149},{"type":254,"tag":622,"props":80010,"children":80011},{"style":1202},[80012],{"type":259,"value":79916},{"type":254,"tag":622,"props":80014,"children":80015},{"style":629},[80016],{"type":259,"value":1654},{"type":254,"tag":622,"props":80018,"children":80019},{"style":629},[80020],{"type":259,"value":661},{"type":254,"tag":622,"props":80022,"children":80023},{"style":664},[80024],{"type":259,"value":80025},"Til'Illan",{"type":254,"tag":622,"props":80027,"children":80028},{"style":629},[80029],{"type":259,"value":652},{"type":254,"tag":622,"props":80031,"children":80032},{"style":629},[80033],{"type":259,"value":3149},{"type":254,"tag":622,"props":80035,"children":80036},{"style":1202},[80037],{"type":259,"value":79942},{"type":254,"tag":622,"props":80039,"children":80040},{"style":629},[80041],{"type":259,"value":1654},{"type":254,"tag":622,"props":80043,"children":80044},{"style":629},[80045],{"type":259,"value":661},{"type":254,"tag":622,"props":80047,"children":80048},{"style":664},[80049],{"type":259,"value":80050},"General",{"type":254,"tag":622,"props":80052,"children":80053},{"style":629},[80054],{"type":259,"value":652},{"type":254,"tag":622,"props":80056,"children":80057},{"style":629},[80058],{"type":259,"value":1111},{"type":254,"tag":622,"props":80060,"children":80061},{"class":624,"line":732},[80062],{"type":254,"tag":622,"props":80063,"children":80064},{"style":629},[80065],{"type":259,"value":1693},{"type":254,"tag":622,"props":80067,"children":80068},{"class":624,"line":759},[80069,80073,80078,80082,80086,80091,80095,80099,80104,80108,80113],{"type":254,"tag":622,"props":80070,"children":80071},{"style":714},[80072],{"type":259,"value":1710},{"type":254,"tag":622,"props":80074,"children":80075},{"style":714},[80076],{"type":259,"value":80077}," template",{"type":254,"tag":622,"props":80079,"children":80080},{"style":629},[80081],{"type":259,"value":1619},{"type":254,"tag":622,"props":80083,"children":80084},{"style":629},[80085],{"type":259,"value":1624},{"type":254,"tag":622,"props":80087,"children":80088},{"style":714},[80089],{"type":259,"value":80090}," RazorTemplate",{"type":254,"tag":622,"props":80092,"children":80093},{"style":629},[80094],{"type":259,"value":2724},{"type":254,"tag":622,"props":80096,"children":80097},{"style":629},[80098],{"type":259,"value":1990},{"type":254,"tag":622,"props":80100,"children":80101},{"style":1202},[80102],{"type":259,"value":80103}," Model ",{"type":254,"tag":622,"props":80105,"children":80106},{"style":629},[80107],{"type":259,"value":1654},{"type":254,"tag":622,"props":80109,"children":80110},{"style":1202},[80111],{"type":259,"value":80112},"  people",{"type":254,"tag":622,"props":80114,"children":80115},{"style":629},[80116],{"type":259,"value":1693},{"type":254,"tag":622,"props":80118,"children":80119},{"class":624,"line":798},[80120,80124,80129,80133,80137,80141,80146],{"type":254,"tag":622,"props":80121,"children":80122},{"style":714},[80123],{"type":259,"value":1710},{"type":254,"tag":622,"props":80125,"children":80126},{"style":714},[80127],{"type":259,"value":80128}," page",{"type":254,"tag":622,"props":80130,"children":80131},{"style":629},[80132],{"type":259,"value":1619},{"type":254,"tag":622,"props":80134,"children":80135},{"style":1202},[80136],{"type":259,"value":80077},{"type":254,"tag":622,"props":80138,"children":80139},{"style":629},[80140],{"type":259,"value":594},{"type":254,"tag":622,"props":80142,"children":80143},{"style":1735},[80144],{"type":259,"value":80145},"GenerateString",{"type":254,"tag":622,"props":80147,"children":80148},{"style":629},[80149],{"type":259,"value":2798},{"type":254,"tag":255,"props":80151,"children":80152},{},[80153],{"type":259,"value":80154},"Razor template:",{"type":254,"tag":612,"props":80156,"children":80160},{"className":80157,"code":80158,"language":80159,"meta":205,"style":205},"language-razor shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","@model IEnumerable\u003CPdfGeneration.Models.Character>\n\u003Chtml>\n\u003Cbody>\n    \u003Cul class=\"people\">\n    @foreach (var person in @Model)\n    {\n        \u003Cli>@person.FirstName @person.LastName - @person.Job\u003C/li>\n    }\n    \u003C/ul>\n\u003C/body>\n\u003C/html>\n","razor",[80161],{"type":254,"tag":389,"props":80162,"children":80163},{"__ignoreMap":205},[80164,80206,80222,80237,80274,80308,80315,80370,80377,80393,80408],{"type":254,"tag":622,"props":80165,"children":80166},{"class":624,"line":625},[80167,80172,80177,80181,80186,80190,80194,80198,80202],{"type":254,"tag":622,"props":80168,"children":80169},{"style":1196},[80170],{"type":259,"value":80171},"@model",{"type":254,"tag":622,"props":80173,"children":80174},{"style":714},[80175],{"type":259,"value":80176}," IEnumerable",{"type":254,"tag":622,"props":80178,"children":80179},{"style":629},[80180],{"type":259,"value":1743},{"type":254,"tag":622,"props":80182,"children":80183},{"style":714},[80184],{"type":259,"value":80185},"PdfGeneration",{"type":254,"tag":622,"props":80187,"children":80188},{"style":629},[80189],{"type":259,"value":594},{"type":254,"tag":622,"props":80191,"children":80192},{"style":714},[80193],{"type":259,"value":63844},{"type":254,"tag":622,"props":80195,"children":80196},{"style":629},[80197],{"type":259,"value":594},{"type":254,"tag":622,"props":80199,"children":80200},{"style":714},[80201],{"type":259,"value":64365},{"type":254,"tag":622,"props":80203,"children":80204},{"style":629},[80205],{"type":259,"value":15654},{"type":254,"tag":622,"props":80207,"children":80208},{"class":624,"line":635},[80209,80213,80218],{"type":254,"tag":622,"props":80210,"children":80211},{"style":629},[80212],{"type":259,"value":1743},{"type":254,"tag":622,"props":80214,"children":80215},{"style":768},[80216],{"type":259,"value":80217},"html",{"type":254,"tag":622,"props":80219,"children":80220},{"style":629},[80221],{"type":259,"value":15654},{"type":254,"tag":622,"props":80223,"children":80224},{"class":624,"line":679},[80225,80229,80233],{"type":254,"tag":622,"props":80226,"children":80227},{"style":629},[80228],{"type":259,"value":1743},{"type":254,"tag":622,"props":80230,"children":80231},{"style":768},[80232],{"type":259,"value":73540},{"type":254,"tag":622,"props":80234,"children":80235},{"style":629},[80236],{"type":259,"value":15654},{"type":254,"tag":622,"props":80238,"children":80239},{"class":624,"line":705},[80240,80245,80249,80253,80257,80261,80266,80270],{"type":254,"tag":622,"props":80241,"children":80242},{"style":629},[80243],{"type":259,"value":80244},"    \u003C",{"type":254,"tag":622,"props":80246,"children":80247},{"style":768},[80248],{"type":259,"value":332},{"type":254,"tag":622,"props":80250,"children":80251},{"style":644},[80252],{"type":259,"value":6761},{"type":254,"tag":622,"props":80254,"children":80255},{"style":629},[80256],{"type":259,"value":1654},{"type":254,"tag":622,"props":80258,"children":80259},{"style":629},[80260],{"type":259,"value":652},{"type":254,"tag":622,"props":80262,"children":80263},{"style":664},[80264],{"type":259,"value":80265},"people",{"type":254,"tag":622,"props":80267,"children":80268},{"style":629},[80269],{"type":259,"value":652},{"type":254,"tag":622,"props":80271,"children":80272},{"style":629},[80273],{"type":259,"value":15654},{"type":254,"tag":622,"props":80275,"children":80276},{"class":624,"line":732},[80277,80282,80286,80290,80295,80299,80304],{"type":254,"tag":622,"props":80278,"children":80279},{"style":1196},[80280],{"type":259,"value":80281},"    @foreach",{"type":254,"tag":622,"props":80283,"children":80284},{"style":629},[80285],{"type":259,"value":3547},{"type":254,"tag":622,"props":80287,"children":80288},{"style":741},[80289],{"type":259,"value":1710},{"type":254,"tag":622,"props":80291,"children":80292},{"style":714},[80293],{"type":259,"value":80294}," person",{"type":254,"tag":622,"props":80296,"children":80297},{"style":1196},[80298],{"type":259,"value":4113},{"type":254,"tag":622,"props":80300,"children":80301},{"style":1202},[80302],{"type":259,"value":80303}," @Model",{"type":254,"tag":622,"props":80305,"children":80306},{"style":629},[80307],{"type":259,"value":1685},{"type":254,"tag":622,"props":80309,"children":80310},{"class":624,"line":759},[80311],{"type":254,"tag":622,"props":80312,"children":80313},{"style":629},[80314],{"type":259,"value":3574},{"type":254,"tag":622,"props":80316,"children":80317},{"class":624,"line":798},[80318,80322,80326,80330,80334,80339,80343,80348,80352,80357,80362,80366],{"type":254,"tag":622,"props":80319,"children":80320},{"style":629},[80321],{"type":259,"value":12969},{"type":254,"tag":622,"props":80323,"children":80324},{"style":768},[80325],{"type":259,"value":336},{"type":254,"tag":622,"props":80327,"children":80328},{"style":629},[80329],{"type":259,"value":6813},{"type":254,"tag":622,"props":80331,"children":80332},{"style":1196},[80333],{"type":259,"value":10788},{"type":254,"tag":622,"props":80335,"children":80336},{"style":1202},[80337],{"type":259,"value":80338},"person.FirstName ",{"type":254,"tag":622,"props":80340,"children":80341},{"style":1196},[80342],{"type":259,"value":10788},{"type":254,"tag":622,"props":80344,"children":80345},{"style":1202},[80346],{"type":259,"value":80347},"person.LastName - ",{"type":254,"tag":622,"props":80349,"children":80350},{"style":1196},[80351],{"type":259,"value":10788},{"type":254,"tag":622,"props":80353,"children":80354},{"style":1202},[80355],{"type":259,"value":80356},"person.Job",{"type":254,"tag":622,"props":80358,"children":80359},{"style":629},[80360],{"type":259,"value":80361},"\u003C/",{"type":254,"tag":622,"props":80363,"children":80364},{"style":768},[80365],{"type":259,"value":336},{"type":254,"tag":622,"props":80367,"children":80368},{"style":629},[80369],{"type":259,"value":15654},{"type":254,"tag":622,"props":80371,"children":80372},{"class":624,"line":836},[80373],{"type":254,"tag":622,"props":80374,"children":80375},{"style":629},[80376],{"type":259,"value":1093},{"type":254,"tag":622,"props":80378,"children":80379},{"class":624,"line":862},[80380,80385,80389],{"type":254,"tag":622,"props":80381,"children":80382},{"style":629},[80383],{"type":259,"value":80384},"    \u003C/",{"type":254,"tag":622,"props":80386,"children":80387},{"style":768},[80388],{"type":259,"value":332},{"type":254,"tag":622,"props":80390,"children":80391},{"style":629},[80392],{"type":259,"value":15654},{"type":254,"tag":622,"props":80394,"children":80395},{"class":624,"line":900},[80396,80400,80404],{"type":254,"tag":622,"props":80397,"children":80398},{"style":629},[80399],{"type":259,"value":80361},{"type":254,"tag":622,"props":80401,"children":80402},{"style":768},[80403],{"type":259,"value":73540},{"type":254,"tag":622,"props":80405,"children":80406},{"style":629},[80407],{"type":259,"value":15654},{"type":254,"tag":622,"props":80409,"children":80410},{"class":624,"line":938},[80411,80415,80419],{"type":254,"tag":622,"props":80412,"children":80413},{"style":629},[80414],{"type":259,"value":80361},{"type":254,"tag":622,"props":80416,"children":80417},{"style":768},[80418],{"type":259,"value":80217},{"type":254,"tag":622,"props":80420,"children":80421},{"style":629},[80422],{"type":259,"value":15654},{"type":254,"tag":255,"props":80424,"children":80425},{},[80426],{"type":259,"value":80427},"This code produces the following html:",{"type":254,"tag":612,"props":80429,"children":80432},{"className":80430,"code":80431,"language":80217,"meta":205,"style":205},"language-html shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Chtml>\n\u003Cbody>\n    \u003Cul class=\"people\">\n        \u003Cli>Ellana Caldin - Marchombre\u003C/li>\n        \u003Cli>Edwin Til&#39;Illan - General\u003C/li>\n    \u003C/ul>\n\u003C/body>\n\u003C/html>\n",[80433],{"type":254,"tag":389,"props":80434,"children":80435},{"__ignoreMap":205},[80436,80451,80466,80501,80533,80565,80580,80595],{"type":254,"tag":622,"props":80437,"children":80438},{"class":624,"line":625},[80439,80443,80447],{"type":254,"tag":622,"props":80440,"children":80441},{"style":629},[80442],{"type":259,"value":1743},{"type":254,"tag":622,"props":80444,"children":80445},{"style":768},[80446],{"type":259,"value":80217},{"type":254,"tag":622,"props":80448,"children":80449},{"style":629},[80450],{"type":259,"value":15654},{"type":254,"tag":622,"props":80452,"children":80453},{"class":624,"line":635},[80454,80458,80462],{"type":254,"tag":622,"props":80455,"children":80456},{"style":629},[80457],{"type":259,"value":1743},{"type":254,"tag":622,"props":80459,"children":80460},{"style":768},[80461],{"type":259,"value":73540},{"type":254,"tag":622,"props":80463,"children":80464},{"style":629},[80465],{"type":259,"value":15654},{"type":254,"tag":622,"props":80467,"children":80468},{"class":624,"line":679},[80469,80473,80477,80481,80485,80489,80493,80497],{"type":254,"tag":622,"props":80470,"children":80471},{"style":629},[80472],{"type":259,"value":80244},{"type":254,"tag":622,"props":80474,"children":80475},{"style":768},[80476],{"type":259,"value":332},{"type":254,"tag":622,"props":80478,"children":80479},{"style":644},[80480],{"type":259,"value":6761},{"type":254,"tag":622,"props":80482,"children":80483},{"style":629},[80484],{"type":259,"value":1654},{"type":254,"tag":622,"props":80486,"children":80487},{"style":629},[80488],{"type":259,"value":652},{"type":254,"tag":622,"props":80490,"children":80491},{"style":664},[80492],{"type":259,"value":80265},{"type":254,"tag":622,"props":80494,"children":80495},{"style":629},[80496],{"type":259,"value":652},{"type":254,"tag":622,"props":80498,"children":80499},{"style":629},[80500],{"type":259,"value":15654},{"type":254,"tag":622,"props":80502,"children":80503},{"class":624,"line":705},[80504,80508,80512,80516,80521,80525,80529],{"type":254,"tag":622,"props":80505,"children":80506},{"style":629},[80507],{"type":259,"value":12969},{"type":254,"tag":622,"props":80509,"children":80510},{"style":768},[80511],{"type":259,"value":336},{"type":254,"tag":622,"props":80513,"children":80514},{"style":629},[80515],{"type":259,"value":6813},{"type":254,"tag":622,"props":80517,"children":80518},{"style":1202},[80519],{"type":259,"value":80520},"Ellana Caldin - Marchombre",{"type":254,"tag":622,"props":80522,"children":80523},{"style":629},[80524],{"type":259,"value":80361},{"type":254,"tag":622,"props":80526,"children":80527},{"style":768},[80528],{"type":259,"value":336},{"type":254,"tag":622,"props":80530,"children":80531},{"style":629},[80532],{"type":259,"value":15654},{"type":254,"tag":622,"props":80534,"children":80535},{"class":624,"line":732},[80536,80540,80544,80548,80553,80557,80561],{"type":254,"tag":622,"props":80537,"children":80538},{"style":629},[80539],{"type":259,"value":12969},{"type":254,"tag":622,"props":80541,"children":80542},{"style":768},[80543],{"type":259,"value":336},{"type":254,"tag":622,"props":80545,"children":80546},{"style":629},[80547],{"type":259,"value":6813},{"type":254,"tag":622,"props":80549,"children":80550},{"style":1202},[80551],{"type":259,"value":80552},"Edwin Til&#39;Illan - General",{"type":254,"tag":622,"props":80554,"children":80555},{"style":629},[80556],{"type":259,"value":80361},{"type":254,"tag":622,"props":80558,"children":80559},{"style":768},[80560],{"type":259,"value":336},{"type":254,"tag":622,"props":80562,"children":80563},{"style":629},[80564],{"type":259,"value":15654},{"type":254,"tag":622,"props":80566,"children":80567},{"class":624,"line":759},[80568,80572,80576],{"type":254,"tag":622,"props":80569,"children":80570},{"style":629},[80571],{"type":259,"value":80384},{"type":254,"tag":622,"props":80573,"children":80574},{"style":768},[80575],{"type":259,"value":332},{"type":254,"tag":622,"props":80577,"children":80578},{"style":629},[80579],{"type":259,"value":15654},{"type":254,"tag":622,"props":80581,"children":80582},{"class":624,"line":798},[80583,80587,80591],{"type":254,"tag":622,"props":80584,"children":80585},{"style":629},[80586],{"type":259,"value":80361},{"type":254,"tag":622,"props":80588,"children":80589},{"style":768},[80590],{"type":259,"value":73540},{"type":254,"tag":622,"props":80592,"children":80593},{"style":629},[80594],{"type":259,"value":15654},{"type":254,"tag":622,"props":80596,"children":80597},{"class":624,"line":836},[80598,80602,80606],{"type":254,"tag":622,"props":80599,"children":80600},{"style":629},[80601],{"type":259,"value":80361},{"type":254,"tag":622,"props":80603,"children":80604},{"style":768},[80605],{"type":259,"value":80217},{"type":254,"tag":622,"props":80607,"children":80608},{"style":629},[80609],{"type":259,"value":15654},{"type":254,"tag":282,"props":80611,"children":80613},{"id":80612},"handlebarsnet-an-alternative-to-razor-templates",[80614],{"type":259,"value":80615},"Handlebars.Net, an alternative to Razor templates",{"type":254,"tag":255,"props":80617,"children":80618},{},[80619,80621,80628,80630,80637],{"type":259,"value":80620},"You can also do HTML templating by using ",{"type":254,"tag":262,"props":80622,"children":80625},{"href":80623,"rel":80624},"https://github.com/rexm/Handlebars.Net",[266],[80626],{"type":259,"value":80627},"Handlebars.Net",{"type":259,"value":80629}," which is a .NET Handlebars engine. It allows to build semantic templates in a .NET application. It uses the same syntax than ",{"type":254,"tag":262,"props":80631,"children":80634},{"href":80632,"rel":80633},"http://handlebarsjs.com/",[266],[80635],{"type":259,"value":80636},"handlebars.js",{"type":259,"value":80638}," for the templates and try to mimic the JS API. Nothing better than a piece of code to illustrate that:",{"type":254,"tag":612,"props":80640,"children":80643},{"className":1423,"code":80641,"filename":80642,"language":1425,"meta":205,"style":205},"#r \"nuget: Handlebars.Net, 1.9.5\"\n\nusing HandlebarsDotNet;\nstring html = @\"\n\u003Cul class=\"\"people\"\">\n  {{#each people}}\n    \u003Cli>{{FirstName}} {{LastName}} - {{Job}}\u003C/li>\n  {{/each}}\n\u003C/ul>\";\n\nvar data = new \n{\n    people = new [] \n    {\n        new { FirstName = \"Ellana\", LastName = \"Caldin\", Job = \"Marchombre\"},\n        new { FirstName = \"Edwin\", LastName = \"Til'Illan\", Job = \"General\"}\n    }\n};\n\nvar template = Handlebars.Compile(html);\nstring result = template(data);\nConsole.WriteLine(result);\n","TestingHandlebars.csx",[80644],{"type":254,"tag":389,"props":80645,"children":80646},{"__ignoreMap":205},[80647,80660,80667,80683,80704,80729,80737,80745,80753,80769,80776,80800,80807,80831,80838,80922,81005,81012,81019,81026,81066,81099],{"type":254,"tag":622,"props":80648,"children":80649},{"class":624,"line":625},[80650,80655],{"type":254,"tag":622,"props":80651,"children":80652},{"style":629},[80653],{"type":259,"value":80654},"#r",{"type":254,"tag":622,"props":80656,"children":80657},{"style":664},[80658],{"type":259,"value":80659}," \"nuget: Handlebars.Net, 1.9.5\"\n",{"type":254,"tag":622,"props":80661,"children":80662},{"class":624,"line":635},[80663],{"type":254,"tag":622,"props":80664,"children":80665},{"emptyLinePlaceholder":1699},[80666],{"type":259,"value":1702},{"type":254,"tag":622,"props":80668,"children":80669},{"class":624,"line":679},[80670,80674,80679],{"type":254,"tag":622,"props":80671,"children":80672},{"style":741},[80673],{"type":259,"value":1604},{"type":254,"tag":622,"props":80675,"children":80676},{"style":1202},[80677],{"type":259,"value":80678}," HandlebarsDotNet",{"type":254,"tag":622,"props":80680,"children":80681},{"style":629},[80682],{"type":259,"value":2350},{"type":254,"tag":622,"props":80684,"children":80685},{"class":624,"line":705},[80686,80690,80695,80699],{"type":254,"tag":622,"props":80687,"children":80688},{"style":629},[80689],{"type":259,"value":2851},{"type":254,"tag":622,"props":80691,"children":80692},{"style":714},[80693],{"type":259,"value":80694}," html",{"type":254,"tag":622,"props":80696,"children":80697},{"style":629},[80698],{"type":259,"value":1619},{"type":254,"tag":622,"props":80700,"children":80701},{"style":629},[80702],{"type":259,"value":80703}," @\"\n",{"type":254,"tag":622,"props":80705,"children":80706},{"class":624,"line":732},[80707,80712,80717,80721,80725],{"type":254,"tag":622,"props":80708,"children":80709},{"style":664},[80710],{"type":259,"value":80711},"\u003Cul class=",{"type":254,"tag":622,"props":80713,"children":80714},{"style":1202},[80715],{"type":259,"value":80716},"\"\"",{"type":254,"tag":622,"props":80718,"children":80719},{"style":664},[80720],{"type":259,"value":80265},{"type":254,"tag":622,"props":80722,"children":80723},{"style":1202},[80724],{"type":259,"value":80716},{"type":254,"tag":622,"props":80726,"children":80727},{"style":664},[80728],{"type":259,"value":15654},{"type":254,"tag":622,"props":80730,"children":80731},{"class":624,"line":759},[80732],{"type":254,"tag":622,"props":80733,"children":80734},{"style":664},[80735],{"type":259,"value":80736},"  {{#each people}}\n",{"type":254,"tag":622,"props":80738,"children":80739},{"class":624,"line":798},[80740],{"type":254,"tag":622,"props":80741,"children":80742},{"style":664},[80743],{"type":259,"value":80744},"    \u003Cli>{{FirstName}} {{LastName}} - {{Job}}\u003C/li>\n",{"type":254,"tag":622,"props":80746,"children":80747},{"class":624,"line":836},[80748],{"type":254,"tag":622,"props":80749,"children":80750},{"style":664},[80751],{"type":259,"value":80752},"  {{/each}}\n",{"type":254,"tag":622,"props":80754,"children":80755},{"class":624,"line":862},[80756,80761,80765],{"type":254,"tag":622,"props":80757,"children":80758},{"style":664},[80759],{"type":259,"value":80760},"\u003C/ul>",{"type":254,"tag":622,"props":80762,"children":80763},{"style":629},[80764],{"type":259,"value":652},{"type":254,"tag":622,"props":80766,"children":80767},{"style":629},[80768],{"type":259,"value":2350},{"type":254,"tag":622,"props":80770,"children":80771},{"class":624,"line":900},[80772],{"type":254,"tag":622,"props":80773,"children":80774},{"emptyLinePlaceholder":1699},[80775],{"type":259,"value":1702},{"type":254,"tag":622,"props":80777,"children":80778},{"class":624,"line":938},[80779,80783,80788,80792,80796],{"type":254,"tag":622,"props":80780,"children":80781},{"style":714},[80782],{"type":259,"value":1710},{"type":254,"tag":622,"props":80784,"children":80785},{"style":714},[80786],{"type":259,"value":80787}," data",{"type":254,"tag":622,"props":80789,"children":80790},{"style":629},[80791],{"type":259,"value":1619},{"type":254,"tag":622,"props":80793,"children":80794},{"style":629},[80795],{"type":259,"value":1624},{"type":254,"tag":622,"props":80797,"children":80798},{"style":1202},[80799],{"type":259,"value":2314},{"type":254,"tag":622,"props":80801,"children":80802},{"class":624,"line":976},[80803],{"type":254,"tag":622,"props":80804,"children":80805},{"style":629},[80806],{"type":259,"value":632},{"type":254,"tag":622,"props":80808,"children":80809},{"class":624,"line":1014},[80810,80815,80819,80823,80827],{"type":254,"tag":622,"props":80811,"children":80812},{"style":1202},[80813],{"type":259,"value":80814},"    people ",{"type":254,"tag":622,"props":80816,"children":80817},{"style":629},[80818],{"type":259,"value":1654},{"type":254,"tag":622,"props":80820,"children":80821},{"style":629},[80822],{"type":259,"value":1624},{"type":254,"tag":622,"props":80824,"children":80825},{"style":629},[80826],{"type":259,"value":13454},{"type":254,"tag":622,"props":80828,"children":80829},{"style":1202},[80830],{"type":259,"value":2314},{"type":254,"tag":622,"props":80832,"children":80833},{"class":624,"line":1052},[80834],{"type":254,"tag":622,"props":80835,"children":80836},{"style":629},[80837],{"type":259,"value":3574},{"type":254,"tag":622,"props":80839,"children":80840},{"class":624,"line":1078},[80841,80846,80850,80854,80858,80862,80866,80870,80874,80878,80882,80886,80890,80894,80898,80902,80906,80910,80914,80918],{"type":254,"tag":622,"props":80842,"children":80843},{"style":629},[80844],{"type":259,"value":80845},"        new",{"type":254,"tag":622,"props":80847,"children":80848},{"style":629},[80849],{"type":259,"value":1990},{"type":254,"tag":622,"props":80851,"children":80852},{"style":1202},[80853],{"type":259,"value":79890},{"type":254,"tag":622,"props":80855,"children":80856},{"style":629},[80857],{"type":259,"value":1654},{"type":254,"tag":622,"props":80859,"children":80860},{"style":629},[80861],{"type":259,"value":661},{"type":254,"tag":622,"props":80863,"children":80864},{"style":664},[80865],{"type":259,"value":79903},{"type":254,"tag":622,"props":80867,"children":80868},{"style":629},[80869],{"type":259,"value":652},{"type":254,"tag":622,"props":80871,"children":80872},{"style":629},[80873],{"type":259,"value":3149},{"type":254,"tag":622,"props":80875,"children":80876},{"style":1202},[80877],{"type":259,"value":79916},{"type":254,"tag":622,"props":80879,"children":80880},{"style":629},[80881],{"type":259,"value":1654},{"type":254,"tag":622,"props":80883,"children":80884},{"style":629},[80885],{"type":259,"value":661},{"type":254,"tag":622,"props":80887,"children":80888},{"style":664},[80889],{"type":259,"value":79929},{"type":254,"tag":622,"props":80891,"children":80892},{"style":629},[80893],{"type":259,"value":652},{"type":254,"tag":622,"props":80895,"children":80896},{"style":629},[80897],{"type":259,"value":3149},{"type":254,"tag":622,"props":80899,"children":80900},{"style":1202},[80901],{"type":259,"value":79942},{"type":254,"tag":622,"props":80903,"children":80904},{"style":629},[80905],{"type":259,"value":1654},{"type":254,"tag":622,"props":80907,"children":80908},{"style":629},[80909],{"type":259,"value":661},{"type":254,"tag":622,"props":80911,"children":80912},{"style":664},[80913],{"type":259,"value":79955},{"type":254,"tag":622,"props":80915,"children":80916},{"style":629},[80917],{"type":259,"value":652},{"type":254,"tag":622,"props":80919,"children":80920},{"style":629},[80921],{"type":259,"value":79964},{"type":254,"tag":622,"props":80923,"children":80924},{"class":624,"line":1087},[80925,80929,80933,80937,80941,80945,80949,80953,80957,80961,80965,80969,80973,80977,80981,80985,80989,80993,80997,81001],{"type":254,"tag":622,"props":80926,"children":80927},{"style":629},[80928],{"type":259,"value":80845},{"type":254,"tag":622,"props":80930,"children":80931},{"style":629},[80932],{"type":259,"value":1990},{"type":254,"tag":622,"props":80934,"children":80935},{"style":1202},[80936],{"type":259,"value":79890},{"type":254,"tag":622,"props":80938,"children":80939},{"style":629},[80940],{"type":259,"value":1654},{"type":254,"tag":622,"props":80942,"children":80943},{"style":629},[80944],{"type":259,"value":661},{"type":254,"tag":622,"props":80946,"children":80947},{"style":664},[80948],{"type":259,"value":80000},{"type":254,"tag":622,"props":80950,"children":80951},{"style":629},[80952],{"type":259,"value":652},{"type":254,"tag":622,"props":80954,"children":80955},{"style":629},[80956],{"type":259,"value":3149},{"type":254,"tag":622,"props":80958,"children":80959},{"style":1202},[80960],{"type":259,"value":79916},{"type":254,"tag":622,"props":80962,"children":80963},{"style":629},[80964],{"type":259,"value":1654},{"type":254,"tag":622,"props":80966,"children":80967},{"style":629},[80968],{"type":259,"value":661},{"type":254,"tag":622,"props":80970,"children":80971},{"style":664},[80972],{"type":259,"value":80025},{"type":254,"tag":622,"props":80974,"children":80975},{"style":629},[80976],{"type":259,"value":652},{"type":254,"tag":622,"props":80978,"children":80979},{"style":629},[80980],{"type":259,"value":3149},{"type":254,"tag":622,"props":80982,"children":80983},{"style":1202},[80984],{"type":259,"value":79942},{"type":254,"tag":622,"props":80986,"children":80987},{"style":629},[80988],{"type":259,"value":1654},{"type":254,"tag":622,"props":80990,"children":80991},{"style":629},[80992],{"type":259,"value":661},{"type":254,"tag":622,"props":80994,"children":80995},{"style":664},[80996],{"type":259,"value":80050},{"type":254,"tag":622,"props":80998,"children":80999},{"style":629},[81000],{"type":259,"value":652},{"type":254,"tag":622,"props":81002,"children":81003},{"style":629},[81004],{"type":259,"value":1111},{"type":254,"tag":622,"props":81006,"children":81007},{"class":624,"line":1096},[81008],{"type":254,"tag":622,"props":81009,"children":81010},{"style":629},[81011],{"type":259,"value":1093},{"type":254,"tag":622,"props":81013,"children":81014},{"class":624,"line":1105},[81015],{"type":254,"tag":622,"props":81016,"children":81017},{"style":629},[81018],{"type":259,"value":1693},{"type":254,"tag":622,"props":81020,"children":81021},{"class":624,"line":4131},[81022],{"type":254,"tag":622,"props":81023,"children":81024},{"emptyLinePlaceholder":1699},[81025],{"type":259,"value":1702},{"type":254,"tag":622,"props":81027,"children":81028},{"class":624,"line":4183},[81029,81033,81037,81041,81046,81050,81054,81058,81062],{"type":254,"tag":622,"props":81030,"children":81031},{"style":714},[81032],{"type":259,"value":1710},{"type":254,"tag":622,"props":81034,"children":81035},{"style":714},[81036],{"type":259,"value":80077},{"type":254,"tag":622,"props":81038,"children":81039},{"style":629},[81040],{"type":259,"value":1619},{"type":254,"tag":622,"props":81042,"children":81043},{"style":1202},[81044],{"type":259,"value":81045}," Handlebars",{"type":254,"tag":622,"props":81047,"children":81048},{"style":629},[81049],{"type":259,"value":594},{"type":254,"tag":622,"props":81051,"children":81052},{"style":1735},[81053],{"type":259,"value":33922},{"type":254,"tag":622,"props":81055,"children":81056},{"style":629},[81057],{"type":259,"value":1668},{"type":254,"tag":622,"props":81059,"children":81060},{"style":1202},[81061],{"type":259,"value":80217},{"type":254,"tag":622,"props":81063,"children":81064},{"style":629},[81065],{"type":259,"value":1554},{"type":254,"tag":622,"props":81067,"children":81068},{"class":624,"line":4208},[81069,81073,81078,81082,81086,81090,81095],{"type":254,"tag":622,"props":81070,"children":81071},{"style":629},[81072],{"type":259,"value":2851},{"type":254,"tag":622,"props":81074,"children":81075},{"style":714},[81076],{"type":259,"value":81077}," result",{"type":254,"tag":622,"props":81079,"children":81080},{"style":629},[81081],{"type":259,"value":1619},{"type":254,"tag":622,"props":81083,"children":81084},{"style":1735},[81085],{"type":259,"value":80077},{"type":254,"tag":622,"props":81087,"children":81088},{"style":629},[81089],{"type":259,"value":1668},{"type":254,"tag":622,"props":81091,"children":81092},{"style":1202},[81093],{"type":259,"value":81094},"data",{"type":254,"tag":622,"props":81096,"children":81097},{"style":629},[81098],{"type":259,"value":1554},{"type":254,"tag":622,"props":81100,"children":81101},{"class":624,"line":4216},[81102,81107,81111,81116,81120,81125],{"type":254,"tag":622,"props":81103,"children":81104},{"style":1202},[81105],{"type":259,"value":81106},"Console",{"type":254,"tag":622,"props":81108,"children":81109},{"style":629},[81110],{"type":259,"value":594},{"type":254,"tag":622,"props":81112,"children":81113},{"style":1735},[81114],{"type":259,"value":81115},"WriteLine",{"type":254,"tag":622,"props":81117,"children":81118},{"style":629},[81119],{"type":259,"value":1668},{"type":254,"tag":622,"props":81121,"children":81122},{"style":1202},[81123],{"type":259,"value":81124},"result",{"type":254,"tag":622,"props":81126,"children":81127},{"style":629},[81128],{"type":259,"value":1554},{"type":254,"tag":255,"props":81130,"children":81131},{},[81132],{"type":259,"value":81133},"This produces the following html:",{"type":254,"tag":612,"props":81135,"children":81137},{"className":80430,"code":81136,"language":80217,"meta":205,"style":205},"\u003Cul class=\"people\">\n    \u003Cli>Ellana Caldin - Marchombre\u003C/li>\n    \u003Cli>Edwin Til'Illan - General\u003C/li>\n\u003C/ul>\n",[81138],{"type":254,"tag":389,"props":81139,"children":81140},{"__ignoreMap":205},[81141,81176,81207,81239],{"type":254,"tag":622,"props":81142,"children":81143},{"class":624,"line":625},[81144,81148,81152,81156,81160,81164,81168,81172],{"type":254,"tag":622,"props":81145,"children":81146},{"style":629},[81147],{"type":259,"value":1743},{"type":254,"tag":622,"props":81149,"children":81150},{"style":768},[81151],{"type":259,"value":332},{"type":254,"tag":622,"props":81153,"children":81154},{"style":644},[81155],{"type":259,"value":6761},{"type":254,"tag":622,"props":81157,"children":81158},{"style":629},[81159],{"type":259,"value":1654},{"type":254,"tag":622,"props":81161,"children":81162},{"style":629},[81163],{"type":259,"value":652},{"type":254,"tag":622,"props":81165,"children":81166},{"style":664},[81167],{"type":259,"value":80265},{"type":254,"tag":622,"props":81169,"children":81170},{"style":629},[81171],{"type":259,"value":652},{"type":254,"tag":622,"props":81173,"children":81174},{"style":629},[81175],{"type":259,"value":15654},{"type":254,"tag":622,"props":81177,"children":81178},{"class":624,"line":635},[81179,81183,81187,81191,81195,81199,81203],{"type":254,"tag":622,"props":81180,"children":81181},{"style":629},[81182],{"type":259,"value":80244},{"type":254,"tag":622,"props":81184,"children":81185},{"style":768},[81186],{"type":259,"value":336},{"type":254,"tag":622,"props":81188,"children":81189},{"style":629},[81190],{"type":259,"value":6813},{"type":254,"tag":622,"props":81192,"children":81193},{"style":1202},[81194],{"type":259,"value":80520},{"type":254,"tag":622,"props":81196,"children":81197},{"style":629},[81198],{"type":259,"value":80361},{"type":254,"tag":622,"props":81200,"children":81201},{"style":768},[81202],{"type":259,"value":336},{"type":254,"tag":622,"props":81204,"children":81205},{"style":629},[81206],{"type":259,"value":15654},{"type":254,"tag":622,"props":81208,"children":81209},{"class":624,"line":679},[81210,81214,81218,81222,81227,81231,81235],{"type":254,"tag":622,"props":81211,"children":81212},{"style":629},[81213],{"type":259,"value":80244},{"type":254,"tag":622,"props":81215,"children":81216},{"style":768},[81217],{"type":259,"value":336},{"type":254,"tag":622,"props":81219,"children":81220},{"style":629},[81221],{"type":259,"value":6813},{"type":254,"tag":622,"props":81223,"children":81224},{"style":1202},[81225],{"type":259,"value":81226},"Edwin Til'Illan - General",{"type":254,"tag":622,"props":81228,"children":81229},{"style":629},[81230],{"type":259,"value":80361},{"type":254,"tag":622,"props":81232,"children":81233},{"style":768},[81234],{"type":259,"value":336},{"type":254,"tag":622,"props":81236,"children":81237},{"style":629},[81238],{"type":259,"value":15654},{"type":254,"tag":622,"props":81240,"children":81241},{"class":624,"line":705},[81242,81246,81250],{"type":254,"tag":622,"props":81243,"children":81244},{"style":629},[81245],{"type":259,"value":80361},{"type":254,"tag":622,"props":81247,"children":81248},{"style":768},[81249],{"type":259,"value":332},{"type":254,"tag":622,"props":81251,"children":81252},{"style":629},[81253],{"type":259,"value":15654},{"type":254,"tag":255,"props":81255,"children":81256},{},[81257,81259,81264,81266,81273,81275,81281],{"type":259,"value":81258},"The code above ",{"type":254,"tag":389,"props":81260,"children":81262},{"className":81261},[],[81263],{"type":259,"value":80642},{"type":259,"value":81265}," is a C# script so if you have ",{"type":254,"tag":262,"props":81267,"children":81270},{"href":81268,"rel":81269},"https://github.com/filipw/dotnet-script",[266],[81271],{"type":259,"value":81272},"dotnet-script",{"type":259,"value":81274}," installed you can run it just by copy/pasting the following command in your terminal: ",{"type":254,"tag":389,"props":81276,"children":81278},{"className":81277},[],[81279],{"type":259,"value":81280},"dotnet script http://tinyurl.com/y5vvv2nm",{"type":259,"value":594},{"type":254,"tag":255,"props":81283,"children":81284},{},[81285],{"type":259,"value":81286},"Using Handlebars.Net is pretty simple and quite powerful. Moreover it runs fine on Xamarin too 👌.",{"type":254,"tag":58395,"props":81288,"children":81289},{},[],{"type":254,"tag":255,"props":81291,"children":81292},{},[81293],{"type":259,"value":81294},"So now you have 2 solutions to do HTML templating in Xamarin, choose the one you like the best.",{"type":254,"tag":1260,"props":81296,"children":81297},{},[81298],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":81300},[81301,81302],{"id":79722,"depth":635,"text":79725},{"id":80612,"depth":635,"text":80615},"content:1.posts:2.html-templating-in-xamarin.md","1.posts/2.html-templating-in-xamarin.md",{"_path":10,"_dir":236,"_draft":206,"_partial":206,"_locale":205,"title":9,"description":81306,"lead":81307,"date":81308,"image":81309,"badge":81311,"tags":81312,"body":81313,"_type":1275,"_id":82173,"_source":231,"_file":82174,"_extension":1278},"Let's talk about tooling and testing an API!","Why using the vscode extension \"REST Client\" instead of Postman?","2019-03-05T00:00:00.000Z",{"src":81310},"/images/swiss_knifes.jpg",{"label":243},[245,37215,37463,246],{"type":251,"children":81314,"toc":82166},[81315,81319,81325,81330,81339,81344,81355,81361,81370,81399,81404,81418,81447,81457,81463,81497,81506,81511,81747,81792,81801,81807,81812,81817,81822,81828,81833,81845,81850,82162],{"type":254,"tag":255,"props":81316,"children":81317},{},[81318],{"type":259,"value":81306},{"type":254,"tag":282,"props":81320,"children":81322},{"id":81321},"gui-tools-and-their-limits",[81323],{"type":259,"value":81324},"GUI Tools and their limits",{"type":254,"tag":255,"props":81326,"children":81327},{},[81328],{"type":259,"value":81329},"Like most developers I guess, I often use GUI tools like Fiddler or Postman to query an API.\nOnce you get used to the HMI of Postman with all its tabs, it's quite easy to create GET / POST / PUT / ... requests, save them in a collection, and visualize the answers. Postman offers a lot of other features, but a very handy one is the possibility to use environment variables in your requests.",{"type":254,"tag":255,"props":81331,"children":81332},{},[81333],{"type":254,"tag":1161,"props":81334,"children":81338},{"alt":81335,"className":81336,"src":81337},"Postman UI",[1165,1166],"/posts/images/restclient_postman_1.png",[],{"type":254,"tag":255,"props":81340,"children":81341},{},[81342],{"type":259,"value":81343},"Seems to be the perfect tool you would say. Well, that's true if you want to quickly test an API on your own, and to be honest I use it a lot for that. But when you are collaborating on a real project with other developers, there might be some things you will miss. For me, it's a way to edit, version, and share requests with other developers.",{"type":254,"tag":255,"props":81345,"children":81346},{},[81347,81349,81354],{"type":259,"value":81348},"Okay, I know that Postman paid plan allows you to share requests with other people of your team. And I am aware of the collection export feature on the free plan, but that's not enough. I don't really like to be too tied to a proprietary software and its GUI to do things. As a developer what I want is to be able to edit my requests in simple text files that I can put in version control. And here's come ",{"type":254,"tag":262,"props":81350,"children":81352},{"href":10849,"rel":81351},[266],[81353],{"type":259,"value":37506},{"type":259,"value":594},{"type":254,"tag":282,"props":81356,"children":81358},{"id":81357},"what-is-rest-client",[81359],{"type":259,"value":81360},"What is REST Client?",{"type":254,"tag":255,"props":81362,"children":81363},{},[81364],{"type":254,"tag":1161,"props":81365,"children":81369},{"alt":81366,"className":81367,"src":81368},"REST Client extension in vscode marketplace",[1165,1166],"/posts/images/restclient_vscode_1.png",[],{"type":254,"tag":255,"props":81371,"children":81372},{},[81373,81375,81382,81384,81389,81391,81398],{"type":259,"value":81374},"The REST Client extension is an open source vs code extension developed by ",{"type":254,"tag":262,"props":81376,"children":81379},{"href":81377,"rel":81378},"https://github.com/Huachao",[266],[81380],{"type":259,"value":81381},"Huachao Mao",{"type":259,"value":81383},". If I quote the README of its ",{"type":254,"tag":262,"props":81385,"children":81387},{"href":46972,"rel":81386},[266],[81388],{"type":259,"value":44981},{"type":259,"value":81390},": REST Client allows you to send HTTP request and view the response in Visual Studio Code directly.\nLet's see that with a simple GET request to ",{"type":254,"tag":262,"props":81392,"children":81395},{"href":81393,"rel":81394},"https://swapi.co/",[266],[81396],{"type":259,"value":81397},"The Start Wars API",{"type":259,"value":474},{"type":254,"tag":255,"props":81400,"children":81401},{},[81402],{"type":259,"value":81403},"05578273",{"type":254,"tag":255,"props":81405,"children":81406},{},[81407,81409,81416],{"type":259,"value":81408},"Nothing new or complicated here, just the request you would have written intuitively. Like this, you can write any kind of request you want simply following the standard RFC 2616. Even if you don't know the standard, it's pretty straightforward and you often find samples with this format on the documentation of the API you are querying, like on the ",{"type":254,"tag":262,"props":81410,"children":81413},{"href":81411,"rel":81412},"https://docs.microsoft.com/en-us/graph/api/user-list-memberof?view=graph-rest-1.0#example",[266],[81414],{"type":259,"value":81415},"Microsoft Graph API documentation",{"type":259,"value":81417}," for instance.",{"type":254,"tag":255,"props":81419,"children":81420},{},[81421,81423,81427,81429,81433,81434,81438,81440,81445],{"type":259,"value":81422},"REST Client works on text files in vscode by selecting ",{"type":254,"tag":19837,"props":81424,"children":81425},{},[81426],{"type":259,"value":246},{"type":259,"value":81428}," as the Language Mode (by default this language mode is associated with files having the ",{"type":254,"tag":19837,"props":81430,"children":81431},{},[81432],{"type":259,"value":37743},{"type":259,"value":31561},{"type":254,"tag":19837,"props":81435,"children":81436},{},[81437],{"type":259,"value":37749},{"type":259,"value":81439}," extension). It provides you with some autocompletion and a few snippets to help you write your queries. You can write multiple requests on the same file in vscode just by separating them with ###. Above each request an actionable ",{"type":254,"tag":19837,"props":81441,"children":81442},{},[81443],{"type":259,"value":81444},"Send Request",{"type":259,"value":81446}," link allows you to run the request and see the response in a response panel.",{"type":254,"tag":255,"props":81448,"children":81449},{},[81450],{"type":254,"tag":1161,"props":81451,"children":81456},{"alt":81452,"className":81453,"src":81454,"height":81455,"width":72692},"Intellisense in HTTP files in vscode",[1165,1166],"/posts/images/restclient_swapi_3.png",400,[],{"type":254,"tag":282,"props":81458,"children":81460},{"id":81459},"using-variables-in-rest-client",[81461],{"type":259,"value":81462},"Using variables in REST Client",{"type":254,"tag":255,"props":81464,"children":81465},{},[81466,81468,81474,81476,81487,81489,81495],{"type":259,"value":81467},"As you can see below, it is possible to use variables with REST Client. A variable ",{"type":254,"tag":389,"props":81469,"children":81471},{"className":81470},[],[81472],{"type":259,"value":81473},"planetName",{"type":259,"value":81475}," is defined in the file and reused in 2 requests. A variable is also used to name the request ",{"type":254,"tag":19837,"props":81477,"children":81478},{},[81479,81481],{"type":259,"value":81480},"GET ",{"type":254,"tag":262,"props":81482,"children":81485},{"href":81483,"rel":81484},"https://swapi.co/api/people/?search=Luke",[266],[81486],{"type":259,"value":81483},{"type":259,"value":81488}," and makes it possible to use elements from the response (that you can see on the right). Here we are using the ",{"type":254,"tag":389,"props":81490,"children":81492},{"className":81491},[],[81493],{"type":259,"value":81494},"homeworld",{"type":259,"value":81496}," property of the response to retrieve the planet from which Luke was from in the following GET request. With the help of variables you can easily combine and chain requests for the scenario you need to realize.",{"type":254,"tag":255,"props":81498,"children":81499},{},[81500],{"type":254,"tag":1161,"props":81501,"children":81505},{"alt":81502,"className":81503,"src":81504},"Execution of an HTTP request in vscode",[1165,1166],"/posts/images/restclient_swapi_2.png",[],{"type":254,"tag":255,"props":81507,"children":81508},{},[81509],{"type":259,"value":81510},"REST Client allows you to define environments and their associated variables in the user settings file of vscode. For instance, let's say I want to query the Microsoft Graph API (both the V1 version and the beta version of the API), I will add the following JSON to my  settings file:",{"type":254,"tag":612,"props":81512,"children":81514},{"className":614,"code":81513,"language":616,"meta":205,"style":205},"\"rest-client.environmentVariables\": {\n    \"$shared\": {\n        \"host\": \"https://graph.microsoft.com/\",\n    },\n    \"graphV1\": {\n        \"version\": \"v1.0\",\n    },\n    \"graphBeta\": {\n        \"version\": \"beta\"\n    },\n}\n",[81515],{"type":254,"tag":389,"props":81516,"children":81517},{"__ignoreMap":205},[81518,81542,81566,81602,81609,81633,81670,81677,81701,81733,81740],{"type":254,"tag":622,"props":81519,"children":81520},{"class":624,"line":625},[81521,81525,81530,81534,81538],{"type":254,"tag":622,"props":81522,"children":81523},{"style":629},[81524],{"type":259,"value":652},{"type":254,"tag":622,"props":81526,"children":81527},{"style":664},[81528],{"type":259,"value":81529},"rest-client.environmentVariables",{"type":254,"tag":622,"props":81531,"children":81532},{"style":629},[81533],{"type":259,"value":652},{"type":254,"tag":622,"props":81535,"children":81536},{"style":1202},[81537],{"type":259,"value":9857},{"type":254,"tag":622,"props":81539,"children":81540},{"style":629},[81541],{"type":259,"value":632},{"type":254,"tag":622,"props":81543,"children":81544},{"class":624,"line":635},[81545,81549,81554,81558,81562],{"type":254,"tag":622,"props":81546,"children":81547},{"style":629},[81548],{"type":259,"value":711},{"type":254,"tag":622,"props":81550,"children":81551},{"style":644},[81552],{"type":259,"value":81553},"$shared",{"type":254,"tag":622,"props":81555,"children":81556},{"style":629},[81557],{"type":259,"value":652},{"type":254,"tag":622,"props":81559,"children":81560},{"style":629},[81561],{"type":259,"value":474},{"type":254,"tag":622,"props":81563,"children":81564},{"style":629},[81565],{"type":259,"value":702},{"type":254,"tag":622,"props":81567,"children":81568},{"class":624,"line":679},[81569,81573,81577,81581,81585,81589,81594,81598],{"type":254,"tag":622,"props":81570,"children":81571},{"style":629},[81572],{"type":259,"value":765},{"type":254,"tag":622,"props":81574,"children":81575},{"style":714},[81576],{"type":259,"value":6709},{"type":254,"tag":622,"props":81578,"children":81579},{"style":629},[81580],{"type":259,"value":652},{"type":254,"tag":622,"props":81582,"children":81583},{"style":629},[81584],{"type":259,"value":474},{"type":254,"tag":622,"props":81586,"children":81587},{"style":629},[81588],{"type":259,"value":661},{"type":254,"tag":622,"props":81590,"children":81591},{"style":664},[81592],{"type":259,"value":81593},"https://graph.microsoft.com/",{"type":254,"tag":622,"props":81595,"children":81596},{"style":629},[81597],{"type":259,"value":652},{"type":254,"tag":622,"props":81599,"children":81600},{"style":629},[81601],{"type":259,"value":676},{"type":254,"tag":622,"props":81603,"children":81604},{"class":624,"line":705},[81605],{"type":254,"tag":622,"props":81606,"children":81607},{"style":629},[81608],{"type":259,"value":20453},{"type":254,"tag":622,"props":81610,"children":81611},{"class":624,"line":732},[81612,81616,81621,81625,81629],{"type":254,"tag":622,"props":81613,"children":81614},{"style":629},[81615],{"type":259,"value":711},{"type":254,"tag":622,"props":81617,"children":81618},{"style":644},[81619],{"type":259,"value":81620},"graphV1",{"type":254,"tag":622,"props":81622,"children":81623},{"style":629},[81624],{"type":259,"value":652},{"type":254,"tag":622,"props":81626,"children":81627},{"style":629},[81628],{"type":259,"value":474},{"type":254,"tag":622,"props":81630,"children":81631},{"style":629},[81632],{"type":259,"value":702},{"type":254,"tag":622,"props":81634,"children":81635},{"class":624,"line":759},[81636,81640,81645,81649,81653,81657,81662,81666],{"type":254,"tag":622,"props":81637,"children":81638},{"style":629},[81639],{"type":259,"value":765},{"type":254,"tag":622,"props":81641,"children":81642},{"style":714},[81643],{"type":259,"value":81644},"version",{"type":254,"tag":622,"props":81646,"children":81647},{"style":629},[81648],{"type":259,"value":652},{"type":254,"tag":622,"props":81650,"children":81651},{"style":629},[81652],{"type":259,"value":474},{"type":254,"tag":622,"props":81654,"children":81655},{"style":629},[81656],{"type":259,"value":661},{"type":254,"tag":622,"props":81658,"children":81659},{"style":664},[81660],{"type":259,"value":81661},"v1.0",{"type":254,"tag":622,"props":81663,"children":81664},{"style":629},[81665],{"type":259,"value":652},{"type":254,"tag":622,"props":81667,"children":81668},{"style":629},[81669],{"type":259,"value":676},{"type":254,"tag":622,"props":81671,"children":81672},{"class":624,"line":798},[81673],{"type":254,"tag":622,"props":81674,"children":81675},{"style":629},[81676],{"type":259,"value":20453},{"type":254,"tag":622,"props":81678,"children":81679},{"class":624,"line":836},[81680,81684,81689,81693,81697],{"type":254,"tag":622,"props":81681,"children":81682},{"style":629},[81683],{"type":259,"value":711},{"type":254,"tag":622,"props":81685,"children":81686},{"style":644},[81687],{"type":259,"value":81688},"graphBeta",{"type":254,"tag":622,"props":81690,"children":81691},{"style":629},[81692],{"type":259,"value":652},{"type":254,"tag":622,"props":81694,"children":81695},{"style":629},[81696],{"type":259,"value":474},{"type":254,"tag":622,"props":81698,"children":81699},{"style":629},[81700],{"type":259,"value":702},{"type":254,"tag":622,"props":81702,"children":81703},{"class":624,"line":862},[81704,81708,81712,81716,81720,81724,81729],{"type":254,"tag":622,"props":81705,"children":81706},{"style":629},[81707],{"type":259,"value":765},{"type":254,"tag":622,"props":81709,"children":81710},{"style":714},[81711],{"type":259,"value":81644},{"type":254,"tag":622,"props":81713,"children":81714},{"style":629},[81715],{"type":259,"value":652},{"type":254,"tag":622,"props":81717,"children":81718},{"style":629},[81719],{"type":259,"value":474},{"type":254,"tag":622,"props":81721,"children":81722},{"style":629},[81723],{"type":259,"value":661},{"type":254,"tag":622,"props":81725,"children":81726},{"style":664},[81727],{"type":259,"value":81728},"beta",{"type":254,"tag":622,"props":81730,"children":81731},{"style":629},[81732],{"type":259,"value":9909},{"type":254,"tag":622,"props":81734,"children":81735},{"class":624,"line":900},[81736],{"type":254,"tag":622,"props":81737,"children":81738},{"style":629},[81739],{"type":259,"value":20453},{"type":254,"tag":622,"props":81741,"children":81742},{"class":624,"line":938},[81743],{"type":254,"tag":622,"props":81744,"children":81745},{"style":629},[81746],{"type":259,"value":1111},{"type":254,"tag":255,"props":81748,"children":81749},{},[81750,81752,81756,81757,81761,81763,81767,81769,81773,81775,81779,81781,81785,81786,81790],{"type":259,"value":81751},"I have defined 2 environments ",{"type":254,"tag":19837,"props":81753,"children":81754},{},[81755],{"type":259,"value":81620},{"type":259,"value":10347},{"type":254,"tag":19837,"props":81758,"children":81759},{},[81760],{"type":259,"value":81688},{"type":259,"value":81762}," with a specific value for the ",{"type":254,"tag":19837,"props":81764,"children":81765},{},[81766],{"type":259,"value":81644},{"type":259,"value":81768}," variable. These environments share the ",{"type":254,"tag":19837,"props":81770,"children":81771},{},[81772],{"type":259,"value":6709},{"type":259,"value":81774}," as a common variable which is contained in the shared environment ",{"type":254,"tag":19837,"props":81776,"children":81777},{},[81778],{"type":259,"value":81553},{"type":259,"value":81780},". From my request file, I can now switch between environments and use the variables ",{"type":254,"tag":19837,"props":81782,"children":81783},{},[81784],{"type":259,"value":81644},{"type":259,"value":10347},{"type":254,"tag":19837,"props":81787,"children":81788},{},[81789],{"type":259,"value":6709},{"type":259,"value":81791}," to request the Microsoft Graph API.",{"type":254,"tag":255,"props":81793,"children":81794},{},[81795],{"type":254,"tag":1161,"props":81796,"children":81800},{"alt":81797,"className":81798,"src":81799},"Environment selection in vscode.",[1165,1166],"/posts/images/restclient_msgraph_1.png",[],{"type":254,"tag":282,"props":81802,"children":81804},{"id":81803},"what-i-like-about-rest-client",[81805],{"type":259,"value":81806},"What I like about REST Client?",{"type":254,"tag":255,"props":81808,"children":81809},{},[81810],{"type":259,"value":81811},"REST Client is a nice alternative to Postman as it allows to easily write requests and query APIs from Visual Studio Code. I am already a vscode user so I appreciate staying in the same environment I know and like for testing an API. REST Client may not offer as many functionalities as Postman but for the usage I have, it is quite enough.",{"type":254,"tag":255,"props":81813,"children":81814},{},[81815],{"type":259,"value":81816},"What I like about this tool is that you treat your requests as code: you can commit the files containing your requests, keep track of their modifications and share the requests with your colleagues in your Git project repository.",{"type":254,"tag":255,"props":81818,"children":81819},{},[81820],{"type":259,"value":81821},"I have seen quite a few people using it recently in video tutorials and if you have a look at the number of downloads it seems I am not the only one to find it useful.",{"type":254,"tag":282,"props":81823,"children":81825},{"id":81824},"getting-started",[81826],{"type":259,"value":81827},"Getting started",{"type":254,"tag":255,"props":81829,"children":81830},{},[81831],{"type":259,"value":81832},"There are a lot of other features in REST Client that I didn't talk about (generate code snippet, request history ...) so don't hesitate to give it a try. You can use it on any API you like (there is even basic support for SOAP) the same way you would use Postman or other tools.",{"type":254,"tag":255,"props":81834,"children":81835},{},[81836,81838,81843],{"type":259,"value":81837},"If you quickly want to convert some of your Postman queries, there is a ",{"type":254,"tag":19837,"props":81839,"children":81840},{},[81841],{"type":259,"value":81842},"Code",{"type":259,"value":81844}," button in Postman that allows you to see the HTTP request code that you can just copy and paste in vscode to use it with REST Client.",{"type":254,"tag":255,"props":81846,"children":81847},{},[81848],{"type":259,"value":81849},"Or if you prefer you can get started by testing the Star Wars API, you will find below the requests I used. Enjoy :)",{"type":254,"tag":612,"props":81851,"children":81854},{"className":1184,"code":81852,"filename":81853,"language":1186,"meta":205,"style":205},"#### List of start wars planets\nGET https://swapi.co/api/planets/ HTTP/1.1\n\n### Get Luke Skywalker\n# @name lukeRequest\nGET https://swapi.co/api/people/?search=Luke HTTP/1.1\n\n### Get Luke Skywalker home planet\nGET {{lukeRequest.response.body.results[0].homeworld}} HTTP/1.1\n\n### List available resources\nGET https://swapi.co/api/ HTTP/1.1\n\n### Search planet with a specific name \n@planetName = Naboo\nGET https://swapi.co/api/planets/?search={{planetName}} HTTP/1.1\n\n### Search planet with a specific name in wookie encoding\nGET https://swapi.co/api/planets/?search={{planetName}}&format=wookiee HTTP/1.1\n\n### List starships\nGET https://swapi.co/api/starships HTTP/1.1\n","swapi.http",[81855],{"type":254,"tag":389,"props":81856,"children":81857},{"__ignoreMap":205},[81858,81866,81890,81897,81904,81927,81950,81957,81965,81989,81996,82004,82028,82035,82043,82060,82084,82091,82099,82123,82130,82138],{"type":254,"tag":622,"props":81859,"children":81860},{"class":624,"line":625},[81861],{"type":254,"tag":622,"props":81862,"children":81863},{"style":3195},[81864],{"type":259,"value":81865},"#### List of start wars planets\n",{"type":254,"tag":622,"props":81867,"children":81868},{"class":624,"line":635},[81869,81873,81878,81882,81886],{"type":254,"tag":622,"props":81870,"children":81871},{"style":1196},[81872],{"type":259,"value":1199},{"type":254,"tag":622,"props":81874,"children":81875},{"style":1202},[81876],{"type":259,"value":81877}," https://swapi.co/api/planets/ ",{"type":254,"tag":622,"props":81879,"children":81880},{"style":741},[81881],{"type":259,"value":246},{"type":254,"tag":622,"props":81883,"children":81884},{"style":1202},[81885],{"type":259,"value":9768},{"type":254,"tag":622,"props":81887,"children":81888},{"style":741},[81889],{"type":259,"value":9773},{"type":254,"tag":622,"props":81891,"children":81892},{"class":624,"line":679},[81893],{"type":254,"tag":622,"props":81894,"children":81895},{"emptyLinePlaceholder":1699},[81896],{"type":259,"value":1702},{"type":254,"tag":622,"props":81898,"children":81899},{"class":624,"line":705},[81900],{"type":254,"tag":622,"props":81901,"children":81902},{"style":3195},[81903],{"type":259,"value":40292},{"type":254,"tag":622,"props":81905,"children":81906},{"class":624,"line":732},[81907,81912,81916,81921],{"type":254,"tag":622,"props":81908,"children":81909},{"style":3195},[81910],{"type":259,"value":81911},"# ",{"type":254,"tag":622,"props":81913,"children":81914},{"style":1196},[81915],{"type":259,"value":10788},{"type":254,"tag":622,"props":81917,"children":81919},{"style":81918},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA;--shiki-light-font-style:italic;--shiki-default-font-style:italic;--shiki-dark-font-style:italic",[81920],{"type":259,"value":16394},{"type":254,"tag":622,"props":81922,"children":81924},{"style":81923},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B;--shiki-light-font-style:italic;--shiki-default-font-style:italic;--shiki-dark-font-style:italic",[81925],{"type":259,"value":81926}," lukeRequest\n",{"type":254,"tag":622,"props":81928,"children":81929},{"class":624,"line":759},[81930,81934,81938,81942,81946],{"type":254,"tag":622,"props":81931,"children":81932},{"style":1196},[81933],{"type":259,"value":1199},{"type":254,"tag":622,"props":81935,"children":81936},{"style":1202},[81937],{"type":259,"value":40304},{"type":254,"tag":622,"props":81939,"children":81940},{"style":741},[81941],{"type":259,"value":246},{"type":254,"tag":622,"props":81943,"children":81944},{"style":1202},[81945],{"type":259,"value":9768},{"type":254,"tag":622,"props":81947,"children":81948},{"style":741},[81949],{"type":259,"value":9773},{"type":254,"tag":622,"props":81951,"children":81952},{"class":624,"line":798},[81953],{"type":254,"tag":622,"props":81954,"children":81955},{"emptyLinePlaceholder":1699},[81956],{"type":259,"value":1702},{"type":254,"tag":622,"props":81958,"children":81959},{"class":624,"line":836},[81960],{"type":254,"tag":622,"props":81961,"children":81962},{"style":3195},[81963],{"type":259,"value":81964},"### Get Luke Skywalker home planet\n",{"type":254,"tag":622,"props":81966,"children":81967},{"class":624,"line":862},[81968,81972,81977,81981,81985],{"type":254,"tag":622,"props":81969,"children":81970},{"style":1196},[81971],{"type":259,"value":1199},{"type":254,"tag":622,"props":81973,"children":81974},{"style":1202},[81975],{"type":259,"value":81976}," {{lukeRequest.response.body.results[0].homeworld}} ",{"type":254,"tag":622,"props":81978,"children":81979},{"style":741},[81980],{"type":259,"value":246},{"type":254,"tag":622,"props":81982,"children":81983},{"style":1202},[81984],{"type":259,"value":9768},{"type":254,"tag":622,"props":81986,"children":81987},{"style":741},[81988],{"type":259,"value":9773},{"type":254,"tag":622,"props":81990,"children":81991},{"class":624,"line":900},[81992],{"type":254,"tag":622,"props":81993,"children":81994},{"emptyLinePlaceholder":1699},[81995],{"type":259,"value":1702},{"type":254,"tag":622,"props":81997,"children":81998},{"class":624,"line":938},[81999],{"type":254,"tag":622,"props":82000,"children":82001},{"style":3195},[82002],{"type":259,"value":82003},"### List available resources\n",{"type":254,"tag":622,"props":82005,"children":82006},{"class":624,"line":976},[82007,82011,82016,82020,82024],{"type":254,"tag":622,"props":82008,"children":82009},{"style":1196},[82010],{"type":259,"value":1199},{"type":254,"tag":622,"props":82012,"children":82013},{"style":1202},[82014],{"type":259,"value":82015}," https://swapi.co/api/ ",{"type":254,"tag":622,"props":82017,"children":82018},{"style":741},[82019],{"type":259,"value":246},{"type":254,"tag":622,"props":82021,"children":82022},{"style":1202},[82023],{"type":259,"value":9768},{"type":254,"tag":622,"props":82025,"children":82026},{"style":741},[82027],{"type":259,"value":9773},{"type":254,"tag":622,"props":82029,"children":82030},{"class":624,"line":1014},[82031],{"type":254,"tag":622,"props":82032,"children":82033},{"emptyLinePlaceholder":1699},[82034],{"type":259,"value":1702},{"type":254,"tag":622,"props":82036,"children":82037},{"class":624,"line":1052},[82038],{"type":254,"tag":622,"props":82039,"children":82040},{"style":3195},[82041],{"type":259,"value":82042},"### Search planet with a specific name \n",{"type":254,"tag":622,"props":82044,"children":82045},{"class":624,"line":1078},[82046,82050,82055],{"type":254,"tag":622,"props":82047,"children":82048},{"style":741},[82049],{"type":259,"value":10788},{"type":254,"tag":622,"props":82051,"children":82052},{"style":1202},[82053],{"type":259,"value":82054},"planetName = ",{"type":254,"tag":622,"props":82056,"children":82057},{"style":664},[82058],{"type":259,"value":82059},"Naboo\n",{"type":254,"tag":622,"props":82061,"children":82062},{"class":624,"line":1087},[82063,82067,82072,82076,82080],{"type":254,"tag":622,"props":82064,"children":82065},{"style":1196},[82066],{"type":259,"value":1199},{"type":254,"tag":622,"props":82068,"children":82069},{"style":1202},[82070],{"type":259,"value":82071}," https://swapi.co/api/planets/?search={{planetName}} ",{"type":254,"tag":622,"props":82073,"children":82074},{"style":741},[82075],{"type":259,"value":246},{"type":254,"tag":622,"props":82077,"children":82078},{"style":1202},[82079],{"type":259,"value":9768},{"type":254,"tag":622,"props":82081,"children":82082},{"style":741},[82083],{"type":259,"value":9773},{"type":254,"tag":622,"props":82085,"children":82086},{"class":624,"line":1096},[82087],{"type":254,"tag":622,"props":82088,"children":82089},{"emptyLinePlaceholder":1699},[82090],{"type":259,"value":1702},{"type":254,"tag":622,"props":82092,"children":82093},{"class":624,"line":1105},[82094],{"type":254,"tag":622,"props":82095,"children":82096},{"style":3195},[82097],{"type":259,"value":82098},"### Search planet with a specific name in wookie encoding\n",{"type":254,"tag":622,"props":82100,"children":82101},{"class":624,"line":4131},[82102,82106,82111,82115,82119],{"type":254,"tag":622,"props":82103,"children":82104},{"style":1196},[82105],{"type":259,"value":1199},{"type":254,"tag":622,"props":82107,"children":82108},{"style":1202},[82109],{"type":259,"value":82110}," https://swapi.co/api/planets/?search={{planetName}}&format=wookiee ",{"type":254,"tag":622,"props":82112,"children":82113},{"style":741},[82114],{"type":259,"value":246},{"type":254,"tag":622,"props":82116,"children":82117},{"style":1202},[82118],{"type":259,"value":9768},{"type":254,"tag":622,"props":82120,"children":82121},{"style":741},[82122],{"type":259,"value":9773},{"type":254,"tag":622,"props":82124,"children":82125},{"class":624,"line":4183},[82126],{"type":254,"tag":622,"props":82127,"children":82128},{"emptyLinePlaceholder":1699},[82129],{"type":259,"value":1702},{"type":254,"tag":622,"props":82131,"children":82132},{"class":624,"line":4208},[82133],{"type":254,"tag":622,"props":82134,"children":82135},{"style":3195},[82136],{"type":259,"value":82137},"### List starships\n",{"type":254,"tag":622,"props":82139,"children":82140},{"class":624,"line":4216},[82141,82145,82150,82154,82158],{"type":254,"tag":622,"props":82142,"children":82143},{"style":1196},[82144],{"type":259,"value":1199},{"type":254,"tag":622,"props":82146,"children":82147},{"style":1202},[82148],{"type":259,"value":82149}," https://swapi.co/api/starships ",{"type":254,"tag":622,"props":82151,"children":82152},{"style":741},[82153],{"type":259,"value":246},{"type":254,"tag":622,"props":82155,"children":82156},{"style":1202},[82157],{"type":259,"value":9768},{"type":254,"tag":622,"props":82159,"children":82160},{"style":741},[82161],{"type":259,"value":9773},{"type":254,"tag":1260,"props":82163,"children":82164},{},[82165],{"type":259,"value":1264},{"title":205,"searchDepth":635,"depth":635,"links":82167},[82168,82169,82170,82171,82172],{"id":81321,"depth":635,"text":81324},{"id":81357,"depth":635,"text":81360},{"id":81459,"depth":635,"text":81462},{"id":81803,"depth":635,"text":81806},{"id":81824,"depth":635,"text":81827},"content:1.posts:1.testing-your-api-with-rest-client.md","1.posts/1.testing-your-api-with-rest-client.md",1716749595137]