[{"data":1,"prerenderedAt":25452},["Reactive",2],{"navigation":3,"aAII9Cz3yR":204,"tags-IaC":397},[4,192,200],{"title":5,"_path":6,"children":7,"icon":191},"Blog","/posts",[8,11,14,17,20,23,26,29,32,35,38,41,44,47,50,53,56,59,62,65,68,71,74,77,80,83,86,89,92,95,98,101,104,107,110,113,116,119,122,125,128,131,134,137,140,143,146,149,152,155,158,161,164,167,170,173,176,179,182,185,188],{"title":9,"_path":10},"Testing your API with REST Client","/posts/testing-your-api-with-rest-client",{"title":12,"_path":13},"HTML templating in Xamarin","/posts/html-templating-in-xamarin",{"title":15,"_path":16},"Goodbye Azure Portal, Welcome Azure CLI","/posts/welcome-azure-cli",{"title":18,"_path":19},"Coming across Gitpod","/posts/gitpod",{"title":21,"_path":22},"Handle token retrieval while querying an API","/posts/delegating-handler",{"title":24,"_path":25},"Clean up your local git branches.","/posts/cleaning-git-branches",{"title":27,"_path":28},"Automate configuration of Teams Tab SSO with PowerShell.","/posts/teams-sso-powershell",{"title":30,"_path":31},"How to do a technology watch? - Part 1","/posts/technology-watch-part1",{"title":33,"_path":34},"How to do a technology watch? - Part 2","/posts/technology-watch-part2",{"title":36,"_path":37},"You almost no longer need Key Vault references for Azure Functions.","/posts/azure-functions-custom-configuration",{"title":39,"_path":40},"How to do a technology watch? - Part 3","/posts/technology-watch-part3",{"title":42,"_path":43},"Forget DevOps, the future is already here!","/posts/devops-future",{"title":45,"_path":46},"Week 9, 2021 - Tips I learned this week","/posts/w09-2021-tips-learned-this-week",{"title":48,"_path":49},"Week 12, 2021 - Tips I learned this week","/posts/w12-2021-tips-learned-this-week",{"title":51,"_path":52},"Week 14, 2021 - Tips I learned this week","/posts/w14-2021-tips-learned-this-week",{"title":54,"_path":55},"Once upon a time in .NET","/posts/once-upon-a-time-in-dotnet",{"title":57,"_path":58},"Install your applications with winget","/posts/winget-import",{"title":60,"_path":61},"Customize your applications when installing them with winget","/posts/winget-override",{"title":63,"_path":64},"Week 22, 2021 - Tips I learned this week","/posts/w22-2021-tips-learned-this-week",{"title":66,"_path":67},"How to connect to an Azure SQL Database from C# using Azure AD","/posts/sqlclient-active-directory-authent",{"title":69,"_path":70},"Producing packages for Windows Package Manager","/posts/wingetcreate",{"title":72,"_path":73},"4 tips about GitHub Actions environment variables and contexts","/posts/github-actions-var-and-context",{"title":75,"_path":76},"AzureWebJobsStorage, the secret you don't need in your Function App.","/posts/azure-functions-without-azurewebjobsstorage",{"title":78,"_path":79},"ASP.NET Core - Lost in configuration","/posts/lost-in-configuration",{"title":81,"_path":82},"Week 39, 2021 - Tips I learned this week","/posts/w39-2021-tips-learned-this-week",{"title":84,"_path":85},"Week 41, 2021 - Tips I learned this week","/posts/w41-2021-tips-learned-this-week",{"title":87,"_path":88},"Migrating and open-sourcing my blog","/posts/migrating-blog",{"title":90,"_path":91},"Week 45, 2021 - Tips I learned this week","/posts/w45-2021-tips-learned-this-week",{"title":93,"_path":94},"Organize your GitHub stars with Astral","/posts/astral",{"title":96,"_path":97},"Pulumi with an Azure Blob Storage backend","/posts/pulumi-azure-backend",{"title":99,"_path":100},"IaC Hot Reload with Pulumi Watch","/posts/pulumi-watch",{"title":102,"_path":103},"Week 2, 2022 - Tips I learned this week","/posts/w02-2022-tips-learned-this-week",{"title":105,"_path":106},"Week 3, 2022 - Tips I learned this week","/posts/w03-2022-tips-learned-this-week",{"title":108,"_path":109},"Week 5, 2022 - Tips I learned this week","/posts/w05-2022-tips-learned-this-week",{"title":111,"_path":112},"How to provision an Azure SQL Database with Active Directory authentication","/posts/sqldatabase-active-directory-authent",{"title":114,"_path":115},"Why will I choose Pulumi over Terraform for my next project?","/posts/pulumi-vs-terraform",{"title":117,"_path":118},"Week 19, 2022 - Tips I learned this week","/posts/w19-2022-tips-learned-this-week",{"title":120,"_path":121},"Week 20, 2022 - Tips I learned this week","/posts/w20-2022-tips-learned-this-week",{"title":123,"_path":124},"Keeping secrets secure when using API Clients","/posts/http-clients-secrets",{"title":126,"_path":127},"What made me want to be a developer?","/posts/be-a-developer",{"title":129,"_path":130},"What can we do when stuck with a programming problem?","/posts/get-unstuck",{"title":132,"_path":133},"How did I automate the setup of my developer Windows laptop?","/posts/automate-developer-machine",{"title":135,"_path":136},"Discussion about API clients","/posts/http-clients",{"title":138,"_path":139},"Week 46, 2022 - Tips I learned this week","/posts/w46-2022-tips-learned-this-week",{"title":141,"_path":142},"When Pulumi met Nuke: a .NET love story","/posts/when-pulumi-met-nuke",{"title":144,"_path":145},"A year of learning and sharing - Dev Retro 2022","/posts/2022-retro",{"title":147,"_path":148},"Perform Dynamic Execution of an npm Package","/posts/pnpm-dlx",{"title":150,"_path":151},"Manage multiple Node.js versions","/posts/pnpm-env",{"title":153,"_path":154},"Introducing the Vue.js CI/CD series","/posts/vuecicd-introduction",{"title":156,"_path":157},"Execute commands using your project dependencies","/posts/pnpm-exec",{"title":159,"_path":160},"Vue.js CI/CD: Continuous Integration","/posts/vuecicd-ci",{"title":162,"_path":163},"Who is using pnpm?","/posts/pnpm-who-is-using",{"title":165,"_path":166},"Create an Azure-Ready GitHub Repository using Pulumi","/posts/azure-ready-github-repository",{"title":168,"_path":169},"Deploying to Azure from Azure DevOps without secrets","/posts/ado-workload-identity-federation",{"title":171,"_path":172},"Effortlessly Configure GitHub Repositories for Azure Deployment via OIDC","/posts/scripting-azure-ready-github-repository",{"title":174,"_path":175},"Playing with the .NET 8 Web API template","/posts/playing-with-dotnet8",{"title":177,"_path":178},"Another year of sharing and learning - Dev Retro 2023","/posts/2023-retro",{"title":180,"_path":181},"Week 4, 2024 - Tips I learned this week","/posts/w04-2024-tips-learned-this-week",{"title":183,"_path":184},"Using dependency injection with Azure .NET SDK","/posts/azure-sdk-di",{"title":186,"_path":187},"Having Fun With IT Event Calendars","/posts/it-event-calendars",{"title":189,"_path":190},"Call your Azure AD B2C protected API with authenticated HTTP requests from your JetBrains IDE","/posts/http-clients-oauth2","i-heroicons-newspaper",{"title":193,"_path":194,"children":195,"icon":199},"Goodies","/goodies",[196],{"title":197,"_path":198},"My Git Cheat Sheet","/goodies/gitcheatsheet","i-heroicons-gift-solid",{"title":201,"_path":202,"icon":203},"About","/about","i-heroicons-user-circle-solid",[205,207,209,211,214,217,220,223,226,229,231,234,237,240,242,244,247,250,253,255,258,261,264,267,270,273,276,279,282,285,287,289,292,294,297,300,303,305,308,310,313,316,319,322,325,327,329,332,335,338,341,344,347,350,353,356,359,361,363,366,369,372,375,377,380,383,385,388,391,394],[206,206],"tooling",[208,208],"vscode",[210,210],"rest",[212,213],"http","HTTP",[215,216],"razor","Razor",[218,219],"xamarin","Xamarin",[221,222],"templating","Templating",[224,225],"azure-cli","Azure CLI",[227,228],"azure","Azure",[230,230],"shell",[232,233],"github","GitHub",[235,236],"asp-net-core","ASP.NET Core",[238,239],"net",".NET",[241,241],"git",[243,243],"nushell",[245,246],"microsoft-teams","Microsoft Teams",[248,249],"powershell","PowerShell",[251,252],"azure-active-directory","Azure Active Directory",[254,254],"learning",[256,257],"azure-functions","Azure Functions",[259,260],"azure-key-vault","Azure Key Vault",[262,263],"configuration","Configuration",[265,266],"devops","DevOps",[268,269],"it","IT",[271,272],"tips-learned-this-week","tips learned this week",[274,275],"windows-terminal","Windows Terminal",[277,278],"azure-pipelines","Azure Pipelines",[280,281],"application-insights","Application Insights",[283,284],"azure-iot","Azure IoT",[286,286],"records",[288,288],"refit",[290,291],"development-box-setup","development box setup",[293,293],"winget",[295,296],"package-manager","package manager",[298,299],"azure-sql-database","Azure SQL Database",[301,302],"azure-sdk","Azure SDK",[304,304],"wingetcreate",[306,307],"github-actions","GitHub Actions",[309,309],"jq",[311,312],"pulumi","Pulumi",[314,315],"iac","IaC",[317,318],"azure-storage","Azure Storage",[320,321],"azure-signalr","Azure SignalR",[323,324],"visio","Visio",[326,326],"csharp",[328,328],"jest",[330,331],"statiq","Statiq",[333,334],"open-source","open source",[336,337],"visual-studio","Visual Studio",[339,340],"vue-js","Vue.js",[342,343],"azure-devops","Azure DevOps",[345,346],"vite","Vite",[348,349],"code-analysis","Code analysis",[351,352],"diagram","Diagram",[354,355],"terraform","Terraform",[357,358],"typescript","TypeScript",[360,360],"thoughts",[362,362],"pnpm",[364,365],"nuke","Nuke",[367,368],"pipelines","Pipelines",[370,371],"cicd","CI/CD",[373,374],"openid-connect","OpenID Connect",[376,376],"security",[378,379],"github-cli","GitHub CLI",[381,382],"microsoft-entra-id","Microsoft Entra ID",[384,384],"advent",[386,387],"finops","FinOps",[389,390],"anglesharp","AngleSharp",[392,393],"oauth2","OAuth2",[395,396],"azure-ad-b2c","Azure AD B2C",[398,4696,12168,16522,17799,20671,20957,23514],{"_path":169,"_dir":399,"_draft":400,"_partial":400,"_locale":401,"title":168,"description":402,"lead":403,"date":404,"image":405,"badge":407,"tags":408,"body":409,"_type":4691,"_id":4692,"_source":4693,"_file":4694,"_extension":4695},"posts",false,"","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":406},"/images/azuredevopsoidc.webp",{"label":266},[228,233,307,374,312,315,376,239],{"type":410,"children":411,"toc":4678},"root",[412,419,426,442,457,462,483,495,514,526,531,545,550,555,564,573,578,583,593,599,606,611,685,697,756,761,800,805,850,856,861,900,914,950,974,979,1353,1358,1372,1484,1489,1755,1796,1802,1816,2106,2119,2277,2282,2304,2532,2538,2543,2561,2566,3051,3066,3071,3392,3405,3411,3416,3429,3668,3673,3928,3933,4204,4209,4394,4399,4563,4576,4608,4617,4622,4628,4641,4646,4651,4656,4672],{"type":413,"tag":414,"props":415,"children":416},"element","p",{},[417],{"type":418,"value":402},"text",{"type":413,"tag":420,"props":421,"children":423},"h2",{"id":422},"why-should-you-use-workload-identity-federation-for-your-deployment-pipelines",[424],{"type":418,"value":425},"Why should you use Workload Identity Federation for your deployment pipelines?",{"type":413,"tag":414,"props":427,"children":428},{},[429,431,440],{"type":418,"value":430},"I already wrote about the ",{"type":413,"tag":432,"props":433,"children":437},"a",{"href":434,"rel":435},"https://www.techwatching.dev/posts/azure-ready-github-repository#the-problem-with-secret-credentials",[436],"nofollow",[438],{"type":418,"value":439},"problem of secret credentials",{"type":418,"value":441},", but let me remind you 2 reasons why I think you should always avoid using secrets in your deployment pipelines:",{"type":413,"tag":443,"props":444,"children":445},"ul",{},[446,452],{"type":413,"tag":447,"props":448,"children":449},"li",{},[450],{"type":418,"value":451},"It's more secure if you don't need a secret to authenticate to Azure",{"type":413,"tag":447,"props":453,"children":454},{},[455],{"type":418,"value":456},"It's more practical if you don't need to handle secret rotation when secrets expire",{"type":413,"tag":414,"props":458,"children":459},{},[460],{"type":418,"value":461},"This is true whatever the CI/CD platform you are using.",{"type":413,"tag":414,"props":463,"children":464},{},[465,472,474,481],{"type":413,"tag":432,"props":466,"children":469},{"href":467,"rel":468},"https://learn.microsoft.com/en-us/azure/active-directory/workload-identities/workload-identity-federation",[436],[470],{"type":418,"value":471},"Workload identity federation",{"type":418,"value":473}," leverages OpenID Connect to solve these problems and avoid using secrets in your pipelines to authenticate to Azure. I previously published ",{"type":413,"tag":432,"props":475,"children":478},{"href":476,"rel":477},"https://www.techwatching.dev/posts/azure-ready-github-repository",[436],[479],{"type":418,"value":480},"an article about using Azure OpenID Connect with Pulumi in GitHub Actions",{"type":418,"value":482},", but that also works with Azure Pipelines.",{"type":413,"tag":414,"props":484,"children":485},{},[486],{"type":413,"tag":487,"props":488,"children":494},"img",{"alt":489,"className":490,"src":493},"Workload Identity Federation for Azure DevOps",[491,492],"rounded-lg","mx-auto","/posts/images/azuredevopsoidc_schema_1.webp",[],{"type":413,"tag":496,"props":497,"children":499},"callout",{"icon":498},"i-heroicons-information-circle",[500],{"type":413,"tag":414,"props":501,"children":502},{},[503,505,512],{"type":418,"value":504},"Microsoft has announced the ",{"type":413,"tag":432,"props":506,"children":509},{"href":507,"rel":508},"https://devblogs.microsoft.com/devops/public-preview-of-workload-identity-federation-for-azure-pipelines/",[436],[510],{"type":418,"value":511},"public preview of Workload identity federation for Azure Pipelines",{"type":418,"value":513}," on the 11th September 2023.",{"type":413,"tag":420,"props":515,"children":517},{"id":516},"how-can-you-use-workload-identity-federation-to-deploy-to-azure-from-azure-pipelines",[518,520],{"type":418,"value":519},"How can you use ",{"type":413,"tag":521,"props":522,"children":523},"strong",{},[524],{"type":418,"value":525},"Workload Identity Federation to deploy to Azure from Azure Pipelines?",{"type":413,"tag":414,"props":527,"children":528},{},[529],{"type":418,"value":530},"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":413,"tag":414,"props":532,"children":533},{},[534,536,543],{"type":418,"value":535},"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":413,"tag":432,"props":537,"children":540},{"href":538,"rel":539},"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",[436],[541],{"type":418,"value":542},"here",{"type":418,"value":544},").",{"type":413,"tag":414,"props":546,"children":547},{},[548],{"type":418,"value":549},"Or ... you can automate that using Infrastructure as Code 😉.",{"type":413,"tag":414,"props":551,"children":552},{},[553],{"type":418,"value":554},"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":413,"tag":496,"props":556,"children":558},{"icon":557},"i-heroicons-chat-bubble-left-20-solid",[559],{"type":413,"tag":414,"props":560,"children":561},{},[562],{"type":418,"value":563},"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":413,"tag":414,"props":565,"children":566},{},[567],{"type":413,"tag":487,"props":568,"children":572},{"alt":569,"className":570,"src":571},"Diagram to deploy from Azure Pipelines to Azure",[491,492],"/posts/images/azuredevopsoidc_schema_2.webp",[],{"type":413,"tag":414,"props":574,"children":575},{},[576],{"type":418,"value":577},"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":413,"tag":414,"props":579,"children":580},{},[581],{"type":418,"value":582},"Here is the complete solution to implement:",{"type":413,"tag":414,"props":584,"children":585},{},[586],{"type":413,"tag":487,"props":587,"children":592},{"alt":588,"className":589,"src":590,"width":591},"Schema of the complete solution",[491,492],"/posts/images/azuredevopsoidc_schema_3.webp",1000,[],{"type":413,"tag":420,"props":594,"children":596},{"id":595},"automate-the-configuration-of-workload-identity-federation-in-azure-devops",[597],{"type":418,"value":598},"Automate the configuration of Workload identity federation in Azure DevOps",{"type":413,"tag":600,"props":601,"children":603},"h3",{"id":602},"create-the-pulumi-net-project",[604],{"type":418,"value":605},"Create the Pulumi .NET project",{"type":413,"tag":414,"props":607,"children":608},{},[609],{"type":418,"value":610},"Let's start by scaffolding a new Pulumi project using .NET:",{"type":413,"tag":612,"props":613,"children":617},"pre",{"className":614,"code":615,"language":616,"meta":401,"style":401},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pulumi new csharp -n AzureDevOpsWorkloadIdentity -s dev -d \"A program to set up an Azure-Ready Azure DevOps repository\"\n","bash",[618],{"type":413,"tag":619,"props":620,"children":621},"code",{"__ignoreMap":401},[622],{"type":413,"tag":623,"props":624,"children":627},"span",{"class":625,"line":626},"line",1,[628,633,639,644,649,654,659,664,669,675,680],{"type":413,"tag":623,"props":629,"children":631},{"style":630},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[632],{"type":418,"value":311},{"type":413,"tag":623,"props":634,"children":636},{"style":635},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[637],{"type":418,"value":638}," new",{"type":413,"tag":623,"props":640,"children":641},{"style":635},[642],{"type":418,"value":643}," csharp",{"type":413,"tag":623,"props":645,"children":646},{"style":635},[647],{"type":418,"value":648}," -n",{"type":413,"tag":623,"props":650,"children":651},{"style":635},[652],{"type":418,"value":653}," AzureDevOpsWorkloadIdentity",{"type":413,"tag":623,"props":655,"children":656},{"style":635},[657],{"type":418,"value":658}," -s",{"type":413,"tag":623,"props":660,"children":661},{"style":635},[662],{"type":418,"value":663}," dev",{"type":413,"tag":623,"props":665,"children":666},{"style":635},[667],{"type":418,"value":668}," -d",{"type":413,"tag":623,"props":670,"children":672},{"style":671},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[673],{"type":418,"value":674}," \"",{"type":413,"tag":623,"props":676,"children":677},{"style":635},[678],{"type":418,"value":679},"A program to set up an Azure-Ready Azure DevOps repository",{"type":413,"tag":623,"props":681,"children":682},{"style":671},[683],{"type":418,"value":684},"\"\n",{"type":413,"tag":414,"props":686,"children":687},{},[688,690,695],{"type":418,"value":689},"This command creates a new pulumi project and stack from the ",{"type":413,"tag":619,"props":691,"children":693},{"className":692},[],[694],{"type":418,"value":326},{"type":418,"value":696}," template:",{"type":413,"tag":443,"props":698,"children":699},{},[700,721,738],{"type":413,"tag":447,"props":701,"children":702},{},[703,705,711,713,719],{"type":418,"value":704},"The name of the project \"",{"type":413,"tag":706,"props":707,"children":708},"em",{},[709],{"type":418,"value":710},"AzureDevOpsWorkloadIdentity",{"type":418,"value":712},"\" is specified using the ",{"type":413,"tag":619,"props":714,"children":716},{"className":715},[],[717],{"type":418,"value":718},"-n",{"type":418,"value":720}," option",{"type":413,"tag":447,"props":722,"children":723},{},[724,726,730,731,737],{"type":418,"value":725},"The description of the project \"",{"type":413,"tag":706,"props":727,"children":728},{},[729],{"type":418,"value":679},{"type":418,"value":712},{"type":413,"tag":619,"props":732,"children":734},{"className":733},[],[735],{"type":418,"value":736},"-d",{"type":418,"value":720},{"type":413,"tag":447,"props":739,"children":740},{},[741,743,748,749,755],{"type":418,"value":742},"The stack of the project \"",{"type":413,"tag":706,"props":744,"children":745},{},[746],{"type":418,"value":747},"dev",{"type":418,"value":712},{"type":413,"tag":619,"props":750,"children":752},{"className":751},[],[753],{"type":418,"value":754},"-s",{"type":418,"value":720},{"type":413,"tag":414,"props":757,"children":758},{},[759],{"type":418,"value":760},"This project will need 3 different providers:",{"type":413,"tag":443,"props":762,"children":763},{},[764,776,789],{"type":413,"tag":447,"props":765,"children":766},{},[767,769],{"type":418,"value":768},"the ",{"type":413,"tag":432,"props":770,"children":773},{"href":771,"rel":772},"https://www.pulumi.com/registry/packages/azure-native/",[436],[774],{"type":418,"value":775},"Azure Native provider",{"type":413,"tag":447,"props":777,"children":778},{},[779,780,787],{"type":418,"value":768},{"type":413,"tag":432,"props":781,"children":784},{"href":782,"rel":783},"https://www.pulumi.com/registry/packages/azuread/",[436],[785],{"type":418,"value":786},"Azure Active Directory provider",{"type":418,"value":788}," (provider for Microsoft Entra ID)",{"type":413,"tag":447,"props":790,"children":791},{},[792,793],{"type":418,"value":768},{"type":413,"tag":432,"props":794,"children":797},{"href":795,"rel":796},"https://www.pulumi.com/registry/packages/azuredevops/",[436],[798],{"type":418,"value":799},"Azure DevOps provider",{"type":413,"tag":414,"props":801,"children":802},{},[803],{"type":418,"value":804},"So we can add the following Nuget packages to our project:",{"type":413,"tag":443,"props":806,"children":807},{},[808,822,836],{"type":413,"tag":447,"props":809,"children":810},{},[811],{"type":413,"tag":432,"props":812,"children":815},{"href":813,"rel":814},"https://www.nuget.org/packages/Pulumi.AzureNative",[436],[816],{"type":413,"tag":619,"props":817,"children":819},{"className":818},[],[820],{"type":418,"value":821},"Pulumi.AzureNative",{"type":413,"tag":447,"props":823,"children":824},{},[825],{"type":413,"tag":432,"props":826,"children":829},{"href":827,"rel":828},"https://www.nuget.org/packages/Pulumi.AzureAD",[436],[830],{"type":413,"tag":619,"props":831,"children":833},{"className":832},[],[834],{"type":418,"value":835},"Pulumi.AzureAD",{"type":413,"tag":447,"props":837,"children":838},{},[839],{"type":413,"tag":432,"props":840,"children":843},{"href":841,"rel":842},"https://www.nuget.org/packages/Pulumi.AzureDevOps",[436],[844],{"type":413,"tag":619,"props":845,"children":847},{"className":846},[],[848],{"type":418,"value":849},"Pulumi.AzureDevOps",{"type":413,"tag":600,"props":851,"children":853},{"id":852},"create-the-azure-devops-project",[854],{"type":418,"value":855},"Create the Azure DevOps project",{"type":413,"tag":414,"props":857,"children":858},{},[859],{"type":418,"value":860},"First, we must select the Azure DevOps organization where we wish to create a project and set its URL in our Pulumi configuration.",{"type":413,"tag":612,"props":862,"children":864},{"className":614,"code":863,"language":616,"meta":401,"style":401},"pulumi config set azuredevops:orgServiceUrl XXXXXXXXXXXXXX --secret\n",[865],{"type":413,"tag":619,"props":866,"children":867},{"__ignoreMap":401},[868],{"type":413,"tag":623,"props":869,"children":870},{"class":625,"line":626},[871,875,880,885,890,895],{"type":413,"tag":623,"props":872,"children":873},{"style":630},[874],{"type":418,"value":311},{"type":413,"tag":623,"props":876,"children":877},{"style":635},[878],{"type":418,"value":879}," config",{"type":413,"tag":623,"props":881,"children":882},{"style":635},[883],{"type":418,"value":884}," set",{"type":413,"tag":623,"props":886,"children":887},{"style":635},[888],{"type":418,"value":889}," azuredevops:orgServiceUrl",{"type":413,"tag":623,"props":891,"children":892},{"style":635},[893],{"type":418,"value":894}," XXXXXXXXXXXXXX",{"type":413,"tag":623,"props":896,"children":897},{"style":635},[898],{"type":418,"value":899}," --secret\n",{"type":413,"tag":414,"props":901,"children":902},{},[903,905,912],{"type":418,"value":904},"Second, we need to supply the necessary Azure DevOps credentials. For that, we can ",{"type":413,"tag":432,"props":906,"children":909},{"href":907,"rel":908},"https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=Windows#create-a-pat",[436],[910],{"type":418,"value":911},"create a personal access token",{"type":418,"value":913}," and add it to our Pulumi configuration.",{"type":413,"tag":612,"props":915,"children":917},{"className":614,"code":916,"language":616,"meta":401,"style":401},"pulumi config set azuredevops:personalAccessToken YYYYYYYYYYYYYY --secret\n",[918],{"type":413,"tag":619,"props":919,"children":920},{"__ignoreMap":401},[921],{"type":413,"tag":623,"props":922,"children":923},{"class":625,"line":626},[924,928,932,936,941,946],{"type":413,"tag":623,"props":925,"children":926},{"style":630},[927],{"type":418,"value":311},{"type":413,"tag":623,"props":929,"children":930},{"style":635},[931],{"type":418,"value":879},{"type":413,"tag":623,"props":933,"children":934},{"style":635},[935],{"type":418,"value":884},{"type":413,"tag":623,"props":937,"children":938},{"style":635},[939],{"type":418,"value":940}," azuredevops:personalAccessToken",{"type":413,"tag":623,"props":942,"children":943},{"style":635},[944],{"type":418,"value":945}," YYYYYYYYYYYYYY",{"type":413,"tag":623,"props":947,"children":948},{"style":635},[949],{"type":418,"value":899},{"type":413,"tag":496,"props":951,"children":953},{"icon":952},"i-fluent-emoji-flat-locked-with-key",[954],{"type":413,"tag":414,"props":955,"children":956},{},[957,959,965,967,972],{"type":418,"value":958},"I followed the documentation but to be honest, I don't think it's necessary to include the ",{"type":413,"tag":619,"props":960,"children":962},{"className":961},[],[963],{"type":418,"value":964},"--secret",{"type":418,"value":966}," option for the organization URL as it's not really a sensitive value that needs to be encrypted. However, ",{"type":413,"tag":521,"props":968,"children":969},{},[970],{"type":418,"value":971},"it's mandatory to include it for the access token",{"type":418,"value":973}," so that we can safely commit the configuration files without creating security risks.",{"type":413,"tag":414,"props":975,"children":976},{},[977],{"type":418,"value":978},"Third, we can create the DevOps project:",{"type":413,"tag":612,"props":980,"children":983},{"className":981,"code":982,"language":326,"meta":401,"style":401},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","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",[984],{"type":413,"tag":619,"props":985,"children":986},{"__ignoreMap":401},[987,1043,1052,1085,1102,1111,1159,1205,1249,1294,1335,1344],{"type":413,"tag":623,"props":988,"children":989},{"class":625,"line":626},[990,995,1000,1005,1009,1014,1019,1024,1029,1033,1038],{"type":413,"tag":623,"props":991,"children":992},{"style":630},[993],{"type":418,"value":994},"var",{"type":413,"tag":623,"props":996,"children":997},{"style":630},[998],{"type":418,"value":999}," project",{"type":413,"tag":623,"props":1001,"children":1002},{"style":671},[1003],{"type":418,"value":1004}," =",{"type":413,"tag":623,"props":1006,"children":1007},{"style":671},[1008],{"type":418,"value":638},{"type":413,"tag":623,"props":1010,"children":1011},{"style":630},[1012],{"type":418,"value":1013}," Project",{"type":413,"tag":623,"props":1015,"children":1016},{"style":671},[1017],{"type":418,"value":1018},"(",{"type":413,"tag":623,"props":1020,"children":1021},{"style":671},[1022],{"type":418,"value":1023},"\"",{"type":413,"tag":623,"props":1025,"children":1026},{"style":635},[1027],{"type":418,"value":1028},"AzureReadyADOProject",{"type":413,"tag":623,"props":1030,"children":1031},{"style":671},[1032],{"type":418,"value":1023},{"type":413,"tag":623,"props":1034,"children":1035},{"style":671},[1036],{"type":418,"value":1037},",",{"type":413,"tag":623,"props":1039,"children":1040},{"style":671},[1041],{"type":418,"value":1042}," new()\n",{"type":413,"tag":623,"props":1044,"children":1046},{"class":625,"line":1045},2,[1047],{"type":413,"tag":623,"props":1048,"children":1049},{"style":671},[1050],{"type":418,"value":1051},"{\n",{"type":413,"tag":623,"props":1053,"children":1055},{"class":625,"line":1054},3,[1056,1062,1067,1071,1076,1080],{"type":413,"tag":623,"props":1057,"children":1059},{"style":1058},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[1060],{"type":418,"value":1061},"    Description ",{"type":413,"tag":623,"props":1063,"children":1064},{"style":671},[1065],{"type":418,"value":1066},"=",{"type":413,"tag":623,"props":1068,"children":1069},{"style":671},[1070],{"type":418,"value":674},{"type":413,"tag":623,"props":1072,"children":1073},{"style":635},[1074],{"type":418,"value":1075},"New project with everything correctly configured to provision Azure resources or deploy applications to Azure",{"type":413,"tag":623,"props":1077,"children":1078},{"style":671},[1079],{"type":418,"value":1023},{"type":413,"tag":623,"props":1081,"children":1082},{"style":671},[1083],{"type":418,"value":1084},",\n",{"type":413,"tag":623,"props":1086,"children":1088},{"class":625,"line":1087},4,[1089,1094,1098],{"type":413,"tag":623,"props":1090,"children":1091},{"style":1058},[1092],{"type":418,"value":1093},"    Features ",{"type":413,"tag":623,"props":1095,"children":1096},{"style":671},[1097],{"type":418,"value":1066},{"type":413,"tag":623,"props":1099,"children":1100},{"style":671},[1101],{"type":418,"value":1042},{"type":413,"tag":623,"props":1103,"children":1105},{"class":625,"line":1104},5,[1106],{"type":413,"tag":623,"props":1107,"children":1108},{"style":671},[1109],{"type":418,"value":1110},"    {\n",{"type":413,"tag":623,"props":1112,"children":1114},{"class":625,"line":1113},6,[1115,1120,1124,1129,1133,1138,1142,1146,1151,1155],{"type":413,"tag":623,"props":1116,"children":1117},{"style":671},[1118],{"type":418,"value":1119},"        [",{"type":413,"tag":623,"props":1121,"children":1122},{"style":671},[1123],{"type":418,"value":1023},{"type":413,"tag":623,"props":1125,"children":1126},{"style":635},[1127],{"type":418,"value":1128},"boards",{"type":413,"tag":623,"props":1130,"children":1131},{"style":671},[1132],{"type":418,"value":1023},{"type":413,"tag":623,"props":1134,"children":1135},{"style":671},[1136],{"type":418,"value":1137},"]",{"type":413,"tag":623,"props":1139,"children":1140},{"style":671},[1141],{"type":418,"value":1004},{"type":413,"tag":623,"props":1143,"children":1144},{"style":671},[1145],{"type":418,"value":674},{"type":413,"tag":623,"props":1147,"children":1148},{"style":635},[1149],{"type":418,"value":1150},"disabled",{"type":413,"tag":623,"props":1152,"children":1153},{"style":671},[1154],{"type":418,"value":1023},{"type":413,"tag":623,"props":1156,"children":1157},{"style":671},[1158],{"type":418,"value":1084},{"type":413,"tag":623,"props":1160,"children":1162},{"class":625,"line":1161},7,[1163,1167,1171,1176,1180,1184,1188,1192,1197,1201],{"type":413,"tag":623,"props":1164,"children":1165},{"style":671},[1166],{"type":418,"value":1119},{"type":413,"tag":623,"props":1168,"children":1169},{"style":671},[1170],{"type":418,"value":1023},{"type":413,"tag":623,"props":1172,"children":1173},{"style":635},[1174],{"type":418,"value":1175},"repositories",{"type":413,"tag":623,"props":1177,"children":1178},{"style":671},[1179],{"type":418,"value":1023},{"type":413,"tag":623,"props":1181,"children":1182},{"style":671},[1183],{"type":418,"value":1137},{"type":413,"tag":623,"props":1185,"children":1186},{"style":671},[1187],{"type":418,"value":1004},{"type":413,"tag":623,"props":1189,"children":1190},{"style":671},[1191],{"type":418,"value":674},{"type":413,"tag":623,"props":1193,"children":1194},{"style":635},[1195],{"type":418,"value":1196},"enabled",{"type":413,"tag":623,"props":1198,"children":1199},{"style":671},[1200],{"type":418,"value":1023},{"type":413,"tag":623,"props":1202,"children":1203},{"style":671},[1204],{"type":418,"value":1084},{"type":413,"tag":623,"props":1206,"children":1208},{"class":625,"line":1207},8,[1209,1213,1217,1221,1225,1229,1233,1237,1241,1245],{"type":413,"tag":623,"props":1210,"children":1211},{"style":671},[1212],{"type":418,"value":1119},{"type":413,"tag":623,"props":1214,"children":1215},{"style":671},[1216],{"type":418,"value":1023},{"type":413,"tag":623,"props":1218,"children":1219},{"style":635},[1220],{"type":418,"value":367},{"type":413,"tag":623,"props":1222,"children":1223},{"style":671},[1224],{"type":418,"value":1023},{"type":413,"tag":623,"props":1226,"children":1227},{"style":671},[1228],{"type":418,"value":1137},{"type":413,"tag":623,"props":1230,"children":1231},{"style":671},[1232],{"type":418,"value":1004},{"type":413,"tag":623,"props":1234,"children":1235},{"style":671},[1236],{"type":418,"value":674},{"type":413,"tag":623,"props":1238,"children":1239},{"style":635},[1240],{"type":418,"value":1196},{"type":413,"tag":623,"props":1242,"children":1243},{"style":671},[1244],{"type":418,"value":1023},{"type":413,"tag":623,"props":1246,"children":1247},{"style":671},[1248],{"type":418,"value":1084},{"type":413,"tag":623,"props":1250,"children":1252},{"class":625,"line":1251},9,[1253,1257,1261,1266,1270,1274,1278,1282,1286,1290],{"type":413,"tag":623,"props":1254,"children":1255},{"style":671},[1256],{"type":418,"value":1119},{"type":413,"tag":623,"props":1258,"children":1259},{"style":671},[1260],{"type":418,"value":1023},{"type":413,"tag":623,"props":1262,"children":1263},{"style":635},[1264],{"type":418,"value":1265},"testplans",{"type":413,"tag":623,"props":1267,"children":1268},{"style":671},[1269],{"type":418,"value":1023},{"type":413,"tag":623,"props":1271,"children":1272},{"style":671},[1273],{"type":418,"value":1137},{"type":413,"tag":623,"props":1275,"children":1276},{"style":671},[1277],{"type":418,"value":1004},{"type":413,"tag":623,"props":1279,"children":1280},{"style":671},[1281],{"type":418,"value":674},{"type":413,"tag":623,"props":1283,"children":1284},{"style":635},[1285],{"type":418,"value":1150},{"type":413,"tag":623,"props":1287,"children":1288},{"style":671},[1289],{"type":418,"value":1023},{"type":413,"tag":623,"props":1291,"children":1292},{"style":671},[1293],{"type":418,"value":1084},{"type":413,"tag":623,"props":1295,"children":1297},{"class":625,"line":1296},10,[1298,1302,1306,1311,1315,1319,1323,1327,1331],{"type":413,"tag":623,"props":1299,"children":1300},{"style":671},[1301],{"type":418,"value":1119},{"type":413,"tag":623,"props":1303,"children":1304},{"style":671},[1305],{"type":418,"value":1023},{"type":413,"tag":623,"props":1307,"children":1308},{"style":635},[1309],{"type":418,"value":1310},"artifacts",{"type":413,"tag":623,"props":1312,"children":1313},{"style":671},[1314],{"type":418,"value":1023},{"type":413,"tag":623,"props":1316,"children":1317},{"style":671},[1318],{"type":418,"value":1137},{"type":413,"tag":623,"props":1320,"children":1321},{"style":671},[1322],{"type":418,"value":1004},{"type":413,"tag":623,"props":1324,"children":1325},{"style":671},[1326],{"type":418,"value":674},{"type":413,"tag":623,"props":1328,"children":1329},{"style":635},[1330],{"type":418,"value":1150},{"type":413,"tag":623,"props":1332,"children":1333},{"style":671},[1334],{"type":418,"value":684},{"type":413,"tag":623,"props":1336,"children":1338},{"class":625,"line":1337},11,[1339],{"type":413,"tag":623,"props":1340,"children":1341},{"style":671},[1342],{"type":418,"value":1343},"    },\n",{"type":413,"tag":623,"props":1345,"children":1347},{"class":625,"line":1346},12,[1348],{"type":413,"tag":623,"props":1349,"children":1350},{"style":671},[1351],{"type":418,"value":1352},"});\n",{"type":413,"tag":414,"props":1354,"children":1355},{},[1356],{"type":418,"value":1357},"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":413,"tag":414,"props":1359,"children":1360},{},[1361,1363,1370],{"type":418,"value":1362},"By default, when we create an Azure DevOps project, a ",{"type":413,"tag":432,"props":1364,"children":1367},{"href":1365,"rel":1366},"https://www.pulumi.com/registry/packages/azuredevops/api-docs/git/",[436],[1368],{"type":418,"value":1369},"Git repository",{"type":418,"value":1371}," is created for us with the same name as the project. This repository can be retrieved using the following code:",{"type":413,"tag":612,"props":1373,"children":1375},{"className":981,"code":1374,"language":326,"meta":401,"style":401},"var repository = GetGitRepository.Invoke(new()\n{\n    ProjectId = project.Id,\n    Name = project.Name\n});\n",[1376],{"type":413,"tag":619,"props":1377,"children":1378},{"__ignoreMap":401},[1379,1416,1423,1452,1477],{"type":413,"tag":623,"props":1380,"children":1381},{"class":625,"line":626},[1382,1386,1391,1395,1400,1405,1411],{"type":413,"tag":623,"props":1383,"children":1384},{"style":630},[1385],{"type":418,"value":994},{"type":413,"tag":623,"props":1387,"children":1388},{"style":630},[1389],{"type":418,"value":1390}," repository",{"type":413,"tag":623,"props":1392,"children":1393},{"style":671},[1394],{"type":418,"value":1004},{"type":413,"tag":623,"props":1396,"children":1397},{"style":1058},[1398],{"type":418,"value":1399}," GetGitRepository",{"type":413,"tag":623,"props":1401,"children":1402},{"style":671},[1403],{"type":418,"value":1404},".",{"type":413,"tag":623,"props":1406,"children":1408},{"style":1407},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[1409],{"type":418,"value":1410},"Invoke",{"type":413,"tag":623,"props":1412,"children":1413},{"style":671},[1414],{"type":418,"value":1415},"(new()\n",{"type":413,"tag":623,"props":1417,"children":1418},{"class":625,"line":1045},[1419],{"type":413,"tag":623,"props":1420,"children":1421},{"style":671},[1422],{"type":418,"value":1051},{"type":413,"tag":623,"props":1424,"children":1425},{"class":625,"line":1054},[1426,1431,1435,1439,1443,1448],{"type":413,"tag":623,"props":1427,"children":1428},{"style":1058},[1429],{"type":418,"value":1430},"    ProjectId ",{"type":413,"tag":623,"props":1432,"children":1433},{"style":671},[1434],{"type":418,"value":1066},{"type":413,"tag":623,"props":1436,"children":1437},{"style":1058},[1438],{"type":418,"value":999},{"type":413,"tag":623,"props":1440,"children":1441},{"style":671},[1442],{"type":418,"value":1404},{"type":413,"tag":623,"props":1444,"children":1445},{"style":1058},[1446],{"type":418,"value":1447},"Id",{"type":413,"tag":623,"props":1449,"children":1450},{"style":671},[1451],{"type":418,"value":1084},{"type":413,"tag":623,"props":1453,"children":1454},{"class":625,"line":1087},[1455,1460,1464,1468,1472],{"type":413,"tag":623,"props":1456,"children":1457},{"style":1058},[1458],{"type":418,"value":1459},"    Name ",{"type":413,"tag":623,"props":1461,"children":1462},{"style":671},[1463],{"type":418,"value":1066},{"type":413,"tag":623,"props":1465,"children":1466},{"style":1058},[1467],{"type":418,"value":999},{"type":413,"tag":623,"props":1469,"children":1470},{"style":671},[1471],{"type":418,"value":1404},{"type":413,"tag":623,"props":1473,"children":1474},{"style":1058},[1475],{"type":418,"value":1476},"Name\n",{"type":413,"tag":623,"props":1478,"children":1479},{"class":625,"line":1104},[1480],{"type":413,"tag":623,"props":1481,"children":1482},{"style":671},[1483],{"type":418,"value":1352},{"type":413,"tag":414,"props":1485,"children":1486},{},[1487],{"type":418,"value":1488},"We can also choose to create a new Git repository like this:",{"type":413,"tag":612,"props":1490,"children":1492},{"className":981,"code":1491,"language":326,"meta":401,"style":401},"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",[1493],{"type":413,"tag":619,"props":1494,"children":1495},{"__ignoreMap":401},[1496,1545,1552,1579,1605,1612,1641,1670,1699,1716,1723,1748],{"type":413,"tag":623,"props":1497,"children":1498},{"class":625,"line":626},[1499,1503,1507,1511,1515,1520,1524,1528,1533,1537,1541],{"type":413,"tag":623,"props":1500,"children":1501},{"style":630},[1502],{"type":418,"value":994},{"type":413,"tag":623,"props":1504,"children":1505},{"style":630},[1506],{"type":418,"value":1390},{"type":413,"tag":623,"props":1508,"children":1509},{"style":671},[1510],{"type":418,"value":1004},{"type":413,"tag":623,"props":1512,"children":1513},{"style":671},[1514],{"type":418,"value":638},{"type":413,"tag":623,"props":1516,"children":1517},{"style":630},[1518],{"type":418,"value":1519}," Git",{"type":413,"tag":623,"props":1521,"children":1522},{"style":671},[1523],{"type":418,"value":1018},{"type":413,"tag":623,"props":1525,"children":1526},{"style":671},[1527],{"type":418,"value":1023},{"type":413,"tag":623,"props":1529,"children":1530},{"style":635},[1531],{"type":418,"value":1532},"AzureReadyADORepository",{"type":413,"tag":623,"props":1534,"children":1535},{"style":671},[1536],{"type":418,"value":1023},{"type":413,"tag":623,"props":1538,"children":1539},{"style":671},[1540],{"type":418,"value":1037},{"type":413,"tag":623,"props":1542,"children":1543},{"style":671},[1544],{"type":418,"value":1042},{"type":413,"tag":623,"props":1546,"children":1547},{"class":625,"line":1045},[1548],{"type":413,"tag":623,"props":1549,"children":1550},{"style":671},[1551],{"type":418,"value":1051},{"type":413,"tag":623,"props":1553,"children":1554},{"class":625,"line":1054},[1555,1559,1563,1567,1571,1575],{"type":413,"tag":623,"props":1556,"children":1557},{"style":1058},[1558],{"type":418,"value":1430},{"type":413,"tag":623,"props":1560,"children":1561},{"style":671},[1562],{"type":418,"value":1066},{"type":413,"tag":623,"props":1564,"children":1565},{"style":1058},[1566],{"type":418,"value":999},{"type":413,"tag":623,"props":1568,"children":1569},{"style":671},[1570],{"type":418,"value":1404},{"type":413,"tag":623,"props":1572,"children":1573},{"style":1058},[1574],{"type":418,"value":1447},{"type":413,"tag":623,"props":1576,"children":1577},{"style":671},[1578],{"type":418,"value":1084},{"type":413,"tag":623,"props":1580,"children":1581},{"class":625,"line":1087},[1582,1587,1591,1595,1600],{"type":413,"tag":623,"props":1583,"children":1584},{"style":1058},[1585],{"type":418,"value":1586},"    Initialization ",{"type":413,"tag":623,"props":1588,"children":1589},{"style":671},[1590],{"type":418,"value":1066},{"type":413,"tag":623,"props":1592,"children":1593},{"style":671},[1594],{"type":418,"value":638},{"type":413,"tag":623,"props":1596,"children":1597},{"style":630},[1598],{"type":418,"value":1599}," GitInitializationArgs",{"type":413,"tag":623,"props":1601,"children":1602},{"style":671},[1603],{"type":418,"value":1604},"()\n",{"type":413,"tag":623,"props":1606,"children":1607},{"class":625,"line":1104},[1608],{"type":413,"tag":623,"props":1609,"children":1610},{"style":671},[1611],{"type":418,"value":1110},{"type":413,"tag":623,"props":1613,"children":1614},{"class":625,"line":1113},[1615,1620,1624,1628,1633,1637],{"type":413,"tag":623,"props":1616,"children":1617},{"style":1058},[1618],{"type":418,"value":1619},"        InitType ",{"type":413,"tag":623,"props":1621,"children":1622},{"style":671},[1623],{"type":418,"value":1066},{"type":413,"tag":623,"props":1625,"children":1626},{"style":671},[1627],{"type":418,"value":674},{"type":413,"tag":623,"props":1629,"children":1630},{"style":635},[1631],{"type":418,"value":1632},"Clean",{"type":413,"tag":623,"props":1634,"children":1635},{"style":671},[1636],{"type":418,"value":1023},{"type":413,"tag":623,"props":1638,"children":1639},{"style":671},[1640],{"type":418,"value":1084},{"type":413,"tag":623,"props":1642,"children":1643},{"class":625,"line":1161},[1644,1649,1653,1657,1662,1666],{"type":413,"tag":623,"props":1645,"children":1646},{"style":1058},[1647],{"type":418,"value":1648},"        SourceType ",{"type":413,"tag":623,"props":1650,"children":1651},{"style":671},[1652],{"type":418,"value":1066},{"type":413,"tag":623,"props":1654,"children":1655},{"style":671},[1656],{"type":418,"value":674},{"type":413,"tag":623,"props":1658,"children":1659},{"style":635},[1660],{"type":418,"value":1661},"Git",{"type":413,"tag":623,"props":1663,"children":1664},{"style":671},[1665],{"type":418,"value":1023},{"type":413,"tag":623,"props":1667,"children":1668},{"style":671},[1669],{"type":418,"value":1084},{"type":413,"tag":623,"props":1671,"children":1672},{"class":625,"line":1207},[1673,1678,1682,1686,1691,1695],{"type":413,"tag":623,"props":1674,"children":1675},{"style":1058},[1676],{"type":418,"value":1677},"        SourceUrl ",{"type":413,"tag":623,"props":1679,"children":1680},{"style":671},[1681],{"type":418,"value":1066},{"type":413,"tag":623,"props":1683,"children":1684},{"style":671},[1685],{"type":418,"value":674},{"type":413,"tag":623,"props":1687,"children":1688},{"style":635},[1689],{"type":418,"value":1690},"https://repo.com",{"type":413,"tag":623,"props":1692,"children":1693},{"style":671},[1694],{"type":418,"value":1023},{"type":413,"tag":623,"props":1696,"children":1697},{"style":671},[1698],{"type":418,"value":1084},{"type":413,"tag":623,"props":1700,"children":1701},{"class":625,"line":1251},[1702,1707,1711],{"type":413,"tag":623,"props":1703,"children":1704},{"style":1058},[1705],{"type":418,"value":1706},"        ServiceConnectionId ",{"type":413,"tag":623,"props":1708,"children":1709},{"style":671},[1710],{"type":418,"value":1066},{"type":413,"tag":623,"props":1712,"children":1713},{"style":671},[1714],{"type":418,"value":1715}," \"\"\n",{"type":413,"tag":623,"props":1717,"children":1718},{"class":625,"line":1296},[1719],{"type":413,"tag":623,"props":1720,"children":1721},{"style":671},[1722],{"type":418,"value":1343},{"type":413,"tag":623,"props":1724,"children":1725},{"class":625,"line":1337},[1726,1731,1735,1739,1744],{"type":413,"tag":623,"props":1727,"children":1728},{"style":1058},[1729],{"type":418,"value":1730},"    DefaultBranch ",{"type":413,"tag":623,"props":1732,"children":1733},{"style":671},[1734],{"type":418,"value":1066},{"type":413,"tag":623,"props":1736,"children":1737},{"style":671},[1738],{"type":418,"value":674},{"type":413,"tag":623,"props":1740,"children":1741},{"style":635},[1742],{"type":418,"value":1743},"refs/heads/main",{"type":413,"tag":623,"props":1745,"children":1746},{"style":671},[1747],{"type":418,"value":684},{"type":413,"tag":623,"props":1749,"children":1750},{"class":625,"line":1346},[1751],{"type":413,"tag":623,"props":1752,"children":1753},{"style":671},[1754],{"type":418,"value":1352},{"type":413,"tag":496,"props":1756,"children":1757},{"icon":498},[1758],{"type":413,"tag":414,"props":1759,"children":1760},{},[1761,1763,1769,1771,1777,1779,1785,1787,1794],{"type":418,"value":1762},"We should not have to set the ",{"type":413,"tag":619,"props":1764,"children":1766},{"className":1765},[],[1767],{"type":418,"value":1768},"SourceType",{"type":418,"value":1770},", ",{"type":413,"tag":619,"props":1772,"children":1774},{"className":1773},[],[1775],{"type":418,"value":1776},"SourceUrl",{"type":418,"value":1778}," and ",{"type":413,"tag":619,"props":1780,"children":1782},{"className":1781},[],[1783],{"type":418,"value":1784},"ServiceConnectionId",{"type":418,"value":1786}," properties as we are initializing a clean Git repository, not importing one, but it's a workaround because of this ",{"type":413,"tag":432,"props":1788,"children":1791},{"href":1789,"rel":1790},"https://github.com/pulumi/pulumi-azuredevops/issues/66",[436],[1792],{"type":418,"value":1793},"issue",{"type":418,"value":1795}," on the provider.",{"type":413,"tag":600,"props":1797,"children":1799},{"id":1798},"configure-the-arm-service-connection-in-azure-devops",[1800],{"type":418,"value":1801},"Configure the ARM Service Connection in Azure DevOps",{"type":413,"tag":414,"props":1803,"children":1804},{},[1805,1807,1814],{"type":418,"value":1806},"In the Azure DevOps provider, the Azure Resource Manager service connection is called a ",{"type":413,"tag":432,"props":1808,"children":1811},{"href":1809,"rel":1810},"https://www.pulumi.com/registry/packages/azuredevops/api-docs/serviceendpointazurerm/#workload-identity-federation-manual-azurerm-service-endpoint-subscription-scoped",[436],[1812],{"type":418,"value":1813},"ServiceEndpointAzureRM",{"type":418,"value":1815},". We can create such a resource like this:",{"type":413,"tag":612,"props":1817,"children":1819},{"className":981,"code":1818,"language":326,"meta":401,"style":401},"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",[1820],{"type":413,"tag":619,"props":1821,"children":1822},{"__ignoreMap":401},[1823,1873,1880,1907,1936,1965,1986,2007,2028,2053,2060,2090,2098],{"type":413,"tag":623,"props":1824,"children":1825},{"class":625,"line":626},[1826,1830,1835,1839,1843,1848,1852,1856,1861,1865,1869],{"type":413,"tag":623,"props":1827,"children":1828},{"style":630},[1829],{"type":418,"value":994},{"type":413,"tag":623,"props":1831,"children":1832},{"style":630},[1833],{"type":418,"value":1834}," serviceConnection",{"type":413,"tag":623,"props":1836,"children":1837},{"style":671},[1838],{"type":418,"value":1004},{"type":413,"tag":623,"props":1840,"children":1841},{"style":671},[1842],{"type":418,"value":638},{"type":413,"tag":623,"props":1844,"children":1845},{"style":630},[1846],{"type":418,"value":1847}," ServiceEndpointAzureRM",{"type":413,"tag":623,"props":1849,"children":1850},{"style":671},[1851],{"type":418,"value":1018},{"type":413,"tag":623,"props":1853,"children":1854},{"style":671},[1855],{"type":418,"value":1023},{"type":413,"tag":623,"props":1857,"children":1858},{"style":635},[1859],{"type":418,"value":1860},"AzureServiceConnection",{"type":413,"tag":623,"props":1862,"children":1863},{"style":671},[1864],{"type":418,"value":1023},{"type":413,"tag":623,"props":1866,"children":1867},{"style":671},[1868],{"type":418,"value":1037},{"type":413,"tag":623,"props":1870,"children":1871},{"style":671},[1872],{"type":418,"value":1042},{"type":413,"tag":623,"props":1874,"children":1875},{"class":625,"line":1045},[1876],{"type":413,"tag":623,"props":1877,"children":1878},{"style":671},[1879],{"type":418,"value":1051},{"type":413,"tag":623,"props":1881,"children":1882},{"class":625,"line":1054},[1883,1887,1891,1895,1899,1903],{"type":413,"tag":623,"props":1884,"children":1885},{"style":1058},[1886],{"type":418,"value":1430},{"type":413,"tag":623,"props":1888,"children":1889},{"style":671},[1890],{"type":418,"value":1066},{"type":413,"tag":623,"props":1892,"children":1893},{"style":1058},[1894],{"type":418,"value":999},{"type":413,"tag":623,"props":1896,"children":1897},{"style":671},[1898],{"type":418,"value":1404},{"type":413,"tag":623,"props":1900,"children":1901},{"style":1058},[1902],{"type":418,"value":1447},{"type":413,"tag":623,"props":1904,"children":1905},{"style":671},[1906],{"type":418,"value":1084},{"type":413,"tag":623,"props":1908,"children":1909},{"class":625,"line":1087},[1910,1915,1919,1923,1928,1932],{"type":413,"tag":623,"props":1911,"children":1912},{"style":1058},[1913],{"type":418,"value":1914},"    ServiceEndpointName ",{"type":413,"tag":623,"props":1916,"children":1917},{"style":671},[1918],{"type":418,"value":1066},{"type":413,"tag":623,"props":1920,"children":1921},{"style":671},[1922],{"type":418,"value":674},{"type":413,"tag":623,"props":1924,"children":1925},{"style":635},[1926],{"type":418,"value":1927},"azure-with-oidc",{"type":413,"tag":623,"props":1929,"children":1930},{"style":671},[1931],{"type":418,"value":1023},{"type":413,"tag":623,"props":1933,"children":1934},{"style":671},[1935],{"type":418,"value":1084},{"type":413,"tag":623,"props":1937,"children":1938},{"class":625,"line":1104},[1939,1944,1948,1952,1957,1961],{"type":413,"tag":623,"props":1940,"children":1941},{"style":1058},[1942],{"type":418,"value":1943},"    ServiceEndpointAuthenticationScheme ",{"type":413,"tag":623,"props":1945,"children":1946},{"style":671},[1947],{"type":418,"value":1066},{"type":413,"tag":623,"props":1949,"children":1950},{"style":671},[1951],{"type":418,"value":674},{"type":413,"tag":623,"props":1953,"children":1954},{"style":635},[1955],{"type":418,"value":1956},"WorkloadIdentityFederation",{"type":413,"tag":623,"props":1958,"children":1959},{"style":671},[1960],{"type":418,"value":1023},{"type":413,"tag":623,"props":1962,"children":1963},{"style":671},[1964],{"type":418,"value":1084},{"type":413,"tag":623,"props":1966,"children":1967},{"class":625,"line":1113},[1968,1973,1977,1982],{"type":413,"tag":623,"props":1969,"children":1970},{"style":1058},[1971],{"type":418,"value":1972},"    AzurermSpnTenantid ",{"type":413,"tag":623,"props":1974,"children":1975},{"style":671},[1976],{"type":418,"value":1066},{"type":413,"tag":623,"props":1978,"children":1979},{"style":1058},[1980],{"type":418,"value":1981}," tenantId",{"type":413,"tag":623,"props":1983,"children":1984},{"style":671},[1985],{"type":418,"value":1084},{"type":413,"tag":623,"props":1987,"children":1988},{"class":625,"line":1161},[1989,1994,1998,2003],{"type":413,"tag":623,"props":1990,"children":1991},{"style":1058},[1992],{"type":418,"value":1993},"    AzurermSubscriptionId ",{"type":413,"tag":623,"props":1995,"children":1996},{"style":671},[1997],{"type":418,"value":1066},{"type":413,"tag":623,"props":1999,"children":2000},{"style":1058},[2001],{"type":418,"value":2002}," subscriptionId",{"type":413,"tag":623,"props":2004,"children":2005},{"style":671},[2006],{"type":418,"value":1084},{"type":413,"tag":623,"props":2008,"children":2009},{"class":625,"line":1207},[2010,2015,2019,2024],{"type":413,"tag":623,"props":2011,"children":2012},{"style":1058},[2013],{"type":418,"value":2014},"    AzurermSubscriptionName ",{"type":413,"tag":623,"props":2016,"children":2017},{"style":671},[2018],{"type":418,"value":1066},{"type":413,"tag":623,"props":2020,"children":2021},{"style":1058},[2022],{"type":418,"value":2023}," subscriptionName",{"type":413,"tag":623,"props":2025,"children":2026},{"style":671},[2027],{"type":418,"value":1084},{"type":413,"tag":623,"props":2029,"children":2030},{"class":625,"line":1251},[2031,2036,2040,2044,2049],{"type":413,"tag":623,"props":2032,"children":2033},{"style":1058},[2034],{"type":418,"value":2035},"    Credentials ",{"type":413,"tag":623,"props":2037,"children":2038},{"style":671},[2039],{"type":418,"value":1066},{"type":413,"tag":623,"props":2041,"children":2042},{"style":671},[2043],{"type":418,"value":638},{"type":413,"tag":623,"props":2045,"children":2046},{"style":630},[2047],{"type":418,"value":2048}," ServiceEndpointAzureRMCredentialsArgs",{"type":413,"tag":623,"props":2050,"children":2051},{"style":671},[2052],{"type":418,"value":1604},{"type":413,"tag":623,"props":2054,"children":2055},{"class":625,"line":1296},[2056],{"type":413,"tag":623,"props":2057,"children":2058},{"style":671},[2059],{"type":418,"value":1110},{"type":413,"tag":623,"props":2061,"children":2062},{"class":625,"line":1337},[2063,2068,2072,2077,2081,2086],{"type":413,"tag":623,"props":2064,"children":2065},{"style":1058},[2066],{"type":418,"value":2067},"        Serviceprincipalid ",{"type":413,"tag":623,"props":2069,"children":2070},{"style":671},[2071],{"type":418,"value":1066},{"type":413,"tag":623,"props":2073,"children":2074},{"style":1058},[2075],{"type":418,"value":2076}," servicePrincipal",{"type":413,"tag":623,"props":2078,"children":2079},{"style":671},[2080],{"type":418,"value":1404},{"type":413,"tag":623,"props":2082,"children":2083},{"style":1058},[2084],{"type":418,"value":2085},"ApplicationId",{"type":413,"tag":623,"props":2087,"children":2088},{"style":671},[2089],{"type":418,"value":1084},{"type":413,"tag":623,"props":2091,"children":2092},{"class":625,"line":1346},[2093],{"type":413,"tag":623,"props":2094,"children":2095},{"style":671},[2096],{"type":418,"value":2097},"    }\n",{"type":413,"tag":623,"props":2099,"children":2101},{"class":625,"line":2100},13,[2102],{"type":413,"tag":623,"props":2103,"children":2104},{"style":671},[2105],{"type":418,"value":1352},{"type":413,"tag":414,"props":2107,"children":2108},{},[2109,2111,2117],{"type":418,"value":2110},"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":413,"tag":619,"props":2112,"children":2114},{"className":2113},[],[2115],{"type":418,"value":2116},"GetClientConfig.Invoke",{"type":418,"value":2118}," function):",{"type":413,"tag":612,"props":2120,"children":2122},{"className":981,"code":2121,"language":326,"meta":401,"style":401},"var azureConfig = GetClientConfig.Invoke();\nvar tenantId = azureConfig.Apply(c => c.tenantId);\nvar subscriptionId = azureConfig.Apply(c => c.SubscriptionId);\n",[2123],{"type":413,"tag":619,"props":2124,"children":2125},{"__ignoreMap":401},[2126,2160,2221],{"type":413,"tag":623,"props":2127,"children":2128},{"class":625,"line":626},[2129,2133,2138,2142,2147,2151,2155],{"type":413,"tag":623,"props":2130,"children":2131},{"style":630},[2132],{"type":418,"value":994},{"type":413,"tag":623,"props":2134,"children":2135},{"style":630},[2136],{"type":418,"value":2137}," azureConfig",{"type":413,"tag":623,"props":2139,"children":2140},{"style":671},[2141],{"type":418,"value":1004},{"type":413,"tag":623,"props":2143,"children":2144},{"style":1058},[2145],{"type":418,"value":2146}," GetClientConfig",{"type":413,"tag":623,"props":2148,"children":2149},{"style":671},[2150],{"type":418,"value":1404},{"type":413,"tag":623,"props":2152,"children":2153},{"style":1407},[2154],{"type":418,"value":1410},{"type":413,"tag":623,"props":2156,"children":2157},{"style":671},[2158],{"type":418,"value":2159},"();\n",{"type":413,"tag":623,"props":2161,"children":2162},{"class":625,"line":1045},[2163,2167,2171,2175,2179,2183,2188,2192,2197,2202,2207,2211,2216],{"type":413,"tag":623,"props":2164,"children":2165},{"style":630},[2166],{"type":418,"value":994},{"type":413,"tag":623,"props":2168,"children":2169},{"style":630},[2170],{"type":418,"value":1981},{"type":413,"tag":623,"props":2172,"children":2173},{"style":671},[2174],{"type":418,"value":1004},{"type":413,"tag":623,"props":2176,"children":2177},{"style":1058},[2178],{"type":418,"value":2137},{"type":413,"tag":623,"props":2180,"children":2181},{"style":671},[2182],{"type":418,"value":1404},{"type":413,"tag":623,"props":2184,"children":2185},{"style":1407},[2186],{"type":418,"value":2187},"Apply",{"type":413,"tag":623,"props":2189,"children":2190},{"style":671},[2191],{"type":418,"value":1018},{"type":413,"tag":623,"props":2193,"children":2194},{"style":630},[2195],{"type":418,"value":2196},"c",{"type":413,"tag":623,"props":2198,"children":2199},{"style":671},[2200],{"type":418,"value":2201}," =>",{"type":413,"tag":623,"props":2203,"children":2204},{"style":1058},[2205],{"type":418,"value":2206}," c",{"type":413,"tag":623,"props":2208,"children":2209},{"style":671},[2210],{"type":418,"value":1404},{"type":413,"tag":623,"props":2212,"children":2213},{"style":1058},[2214],{"type":418,"value":2215},"tenantId",{"type":413,"tag":623,"props":2217,"children":2218},{"style":671},[2219],{"type":418,"value":2220},");\n",{"type":413,"tag":623,"props":2222,"children":2223},{"class":625,"line":1054},[2224,2228,2232,2236,2240,2244,2248,2252,2256,2260,2264,2268,2273],{"type":413,"tag":623,"props":2225,"children":2226},{"style":630},[2227],{"type":418,"value":994},{"type":413,"tag":623,"props":2229,"children":2230},{"style":630},[2231],{"type":418,"value":2002},{"type":413,"tag":623,"props":2233,"children":2234},{"style":671},[2235],{"type":418,"value":1004},{"type":413,"tag":623,"props":2237,"children":2238},{"style":1058},[2239],{"type":418,"value":2137},{"type":413,"tag":623,"props":2241,"children":2242},{"style":671},[2243],{"type":418,"value":1404},{"type":413,"tag":623,"props":2245,"children":2246},{"style":1407},[2247],{"type":418,"value":2187},{"type":413,"tag":623,"props":2249,"children":2250},{"style":671},[2251],{"type":418,"value":1018},{"type":413,"tag":623,"props":2253,"children":2254},{"style":630},[2255],{"type":418,"value":2196},{"type":413,"tag":623,"props":2257,"children":2258},{"style":671},[2259],{"type":418,"value":2201},{"type":413,"tag":623,"props":2261,"children":2262},{"style":1058},[2263],{"type":418,"value":2206},{"type":413,"tag":623,"props":2265,"children":2266},{"style":671},[2267],{"type":418,"value":1404},{"type":413,"tag":623,"props":2269,"children":2270},{"style":1058},[2271],{"type":418,"value":2272},"SubscriptionId",{"type":413,"tag":623,"props":2274,"children":2275},{"style":671},[2276],{"type":418,"value":2220},{"type":413,"tag":414,"props":2278,"children":2279},{},[2280],{"type":418,"value":2281},"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":413,"tag":414,"props":2283,"children":2284},{},[2285,2287,2294,2296,2302],{"type":418,"value":2286},"The Azure Classic provider offers a ",{"type":413,"tag":432,"props":2288,"children":2291},{"href":2289,"rel":2290},"https://www.pulumi.com/registry/packages/azure/api-docs/core/getsubscription/#azure-core-getsubscription",[436],[2292],{"type":418,"value":2293},"function",{"type":418,"value":2295}," 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":413,"tag":432,"props":2297,"children":2300},{"href":2298,"rel":2299},"https://www.nuget.org/packages/Azure.ResourceManager",[436],[2301],{"type":418,"value":302},{"type":418,"value":2303}," in this case.",{"type":413,"tag":612,"props":2305,"children":2307},{"className":981,"code":2306,"language":326,"meta":401,"style":401},"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",[2308],{"type":413,"tag":619,"props":2309,"children":2310},{"__ignoreMap":401},[2311,2352,2359,2400,2480,2525],{"type":413,"tag":623,"props":2312,"children":2313},{"class":625,"line":626},[2314,2318,2322,2326,2330,2334,2338,2342,2347],{"type":413,"tag":623,"props":2315,"children":2316},{"style":630},[2317],{"type":418,"value":994},{"type":413,"tag":623,"props":2319,"children":2320},{"style":630},[2321],{"type":418,"value":2023},{"type":413,"tag":623,"props":2323,"children":2324},{"style":671},[2325],{"type":418,"value":1004},{"type":413,"tag":623,"props":2327,"children":2328},{"style":1058},[2329],{"type":418,"value":2002},{"type":413,"tag":623,"props":2331,"children":2332},{"style":671},[2333],{"type":418,"value":1404},{"type":413,"tag":623,"props":2335,"children":2336},{"style":1407},[2337],{"type":418,"value":2187},{"type":413,"tag":623,"props":2339,"children":2340},{"style":671},[2341],{"type":418,"value":1018},{"type":413,"tag":623,"props":2343,"children":2344},{"style":630},[2345],{"type":418,"value":2346},"s",{"type":413,"tag":623,"props":2348,"children":2349},{"style":671},[2350],{"type":418,"value":2351}," =>\n",{"type":413,"tag":623,"props":2353,"children":2354},{"class":625,"line":1045},[2355],{"type":413,"tag":623,"props":2356,"children":2357},{"style":671},[2358],{"type":418,"value":1051},{"type":413,"tag":623,"props":2360,"children":2361},{"class":625,"line":1054},[2362,2367,2372,2376,2380,2385,2390,2395],{"type":413,"tag":623,"props":2363,"children":2364},{"style":630},[2365],{"type":418,"value":2366},"    var",{"type":413,"tag":623,"props":2368,"children":2369},{"style":630},[2370],{"type":418,"value":2371}," armClient",{"type":413,"tag":623,"props":2373,"children":2374},{"style":671},[2375],{"type":418,"value":1004},{"type":413,"tag":623,"props":2377,"children":2378},{"style":671},[2379],{"type":418,"value":638},{"type":413,"tag":623,"props":2381,"children":2382},{"style":630},[2383],{"type":418,"value":2384}," ArmClient",{"type":413,"tag":623,"props":2386,"children":2387},{"style":671},[2388],{"type":418,"value":2389},"(new",{"type":413,"tag":623,"props":2391,"children":2392},{"style":630},[2393],{"type":418,"value":2394}," DefaultAzureCredential",{"type":413,"tag":623,"props":2396,"children":2397},{"style":671},[2398],{"type":418,"value":2399},"());\n",{"type":413,"tag":623,"props":2401,"children":2402},{"class":625,"line":1087},[2403,2407,2412,2416,2420,2424,2429,2433,2438,2442,2447,2452,2457,2461,2466,2471,2476],{"type":413,"tag":623,"props":2404,"children":2405},{"style":630},[2406],{"type":418,"value":2366},{"type":413,"tag":623,"props":2408,"children":2409},{"style":630},[2410],{"type":418,"value":2411}," subscription",{"type":413,"tag":623,"props":2413,"children":2414},{"style":671},[2415],{"type":418,"value":1004},{"type":413,"tag":623,"props":2417,"children":2418},{"style":1058},[2419],{"type":418,"value":2371},{"type":413,"tag":623,"props":2421,"children":2422},{"style":671},[2423],{"type":418,"value":1404},{"type":413,"tag":623,"props":2425,"children":2426},{"style":1407},[2427],{"type":418,"value":2428},"GetSubscriptionResource",{"type":413,"tag":623,"props":2430,"children":2431},{"style":671},[2432],{"type":418,"value":2389},{"type":413,"tag":623,"props":2434,"children":2435},{"style":630},[2436],{"type":418,"value":2437}," ResourceIdentifier",{"type":413,"tag":623,"props":2439,"children":2440},{"style":671},[2441],{"type":418,"value":1018},{"type":413,"tag":623,"props":2443,"children":2444},{"style":671},[2445],{"type":418,"value":2446},"$\"",{"type":413,"tag":623,"props":2448,"children":2449},{"style":635},[2450],{"type":418,"value":2451},"/subscriptions/",{"type":413,"tag":623,"props":2453,"children":2454},{"style":671},[2455],{"type":418,"value":2456},"{",{"type":413,"tag":623,"props":2458,"children":2459},{"style":1058},[2460],{"type":418,"value":2346},{"type":413,"tag":623,"props":2462,"children":2463},{"style":671},[2464],{"type":418,"value":2465},"}\"",{"type":413,"tag":623,"props":2467,"children":2468},{"style":671},[2469],{"type":418,"value":2470},")).",{"type":413,"tag":623,"props":2472,"children":2473},{"style":1407},[2474],{"type":418,"value":2475},"Get",{"type":413,"tag":623,"props":2477,"children":2478},{"style":671},[2479],{"type":418,"value":2159},{"type":413,"tag":623,"props":2481,"children":2482},{"class":625,"line":1104},[2483,2489,2493,2497,2502,2506,2511,2515,2520],{"type":413,"tag":623,"props":2484,"children":2486},{"style":2485},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF;--shiki-light-font-style:italic;--shiki-default-font-style:italic;--shiki-dark-font-style:italic",[2487],{"type":418,"value":2488},"    return",{"type":413,"tag":623,"props":2490,"children":2491},{"style":1058},[2492],{"type":418,"value":2411},{"type":413,"tag":623,"props":2494,"children":2495},{"style":671},[2496],{"type":418,"value":1404},{"type":413,"tag":623,"props":2498,"children":2499},{"style":1058},[2500],{"type":418,"value":2501},"Value",{"type":413,"tag":623,"props":2503,"children":2504},{"style":671},[2505],{"type":418,"value":1404},{"type":413,"tag":623,"props":2507,"children":2508},{"style":1058},[2509],{"type":418,"value":2510},"Data",{"type":413,"tag":623,"props":2512,"children":2513},{"style":671},[2514],{"type":418,"value":1404},{"type":413,"tag":623,"props":2516,"children":2517},{"style":1058},[2518],{"type":418,"value":2519},"DisplayName",{"type":413,"tag":623,"props":2521,"children":2522},{"style":671},[2523],{"type":418,"value":2524},";\n",{"type":413,"tag":623,"props":2526,"children":2527},{"class":625,"line":1113},[2528],{"type":413,"tag":623,"props":2529,"children":2530},{"style":671},[2531],{"type":418,"value":1352},{"type":413,"tag":600,"props":2533,"children":2535},{"id":2534},"set-up-the-necessary-microsoft-entra-id-resources",[2536],{"type":418,"value":2537},"Set up the necessary Microsoft Entra ID resources",{"type":413,"tag":414,"props":2539,"children":2540},{},[2541],{"type":418,"value":2542},"We need to set up the following resources in Microsoft Entra ID:",{"type":413,"tag":443,"props":2544,"children":2545},{},[2546,2551,2556],{"type":413,"tag":447,"props":2547,"children":2548},{},[2549],{"type":418,"value":2550},"an Application that represents the Azure DevOps service connection identity",{"type":413,"tag":447,"props":2552,"children":2553},{},[2554],{"type":418,"value":2555},"a Service Principal (related to the application above) that has the contributor role on the Azure subscription",{"type":413,"tag":447,"props":2557,"children":2558},{},[2559],{"type":418,"value":2560},"credentials for the CI/CD pipeline to authenticate to Azure on behalf of this Microsoft Entra ID application",{"type":413,"tag":414,"props":2562,"children":2563},{},[2564],{"type":418,"value":2565},"Let's take care of the first 2 points:",{"type":413,"tag":612,"props":2567,"children":2569},{"className":981,"code":2568,"language":326,"meta":401,"style":401},"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",[2570],{"type":413,"tag":619,"props":2571,"children":2572},{"__ignoreMap":401},[2573,2604,2654,2661,2686,2693,2743,2750,2778,2785,2794,2849,2888,2895,2924,2955,2986,3043],{"type":413,"tag":623,"props":2574,"children":2575},{"class":625,"line":626},[2576,2580,2584,2588,2592,2596,2600],{"type":413,"tag":623,"props":2577,"children":2578},{"style":630},[2579],{"type":418,"value":994},{"type":413,"tag":623,"props":2581,"children":2582},{"style":630},[2583],{"type":418,"value":2137},{"type":413,"tag":623,"props":2585,"children":2586},{"style":671},[2587],{"type":418,"value":1004},{"type":413,"tag":623,"props":2589,"children":2590},{"style":1058},[2591],{"type":418,"value":2146},{"type":413,"tag":623,"props":2593,"children":2594},{"style":671},[2595],{"type":418,"value":1404},{"type":413,"tag":623,"props":2597,"children":2598},{"style":1407},[2599],{"type":418,"value":1410},{"type":413,"tag":623,"props":2601,"children":2602},{"style":671},[2603],{"type":418,"value":2159},{"type":413,"tag":623,"props":2605,"children":2606},{"class":625,"line":1045},[2607,2611,2616,2620,2624,2629,2633,2637,2642,2646,2650],{"type":413,"tag":623,"props":2608,"children":2609},{"style":630},[2610],{"type":418,"value":994},{"type":413,"tag":623,"props":2612,"children":2613},{"style":630},[2614],{"type":418,"value":2615}," aadApplication",{"type":413,"tag":623,"props":2617,"children":2618},{"style":671},[2619],{"type":418,"value":1004},{"type":413,"tag":623,"props":2621,"children":2622},{"style":671},[2623],{"type":418,"value":638},{"type":413,"tag":623,"props":2625,"children":2626},{"style":630},[2627],{"type":418,"value":2628}," Application",{"type":413,"tag":623,"props":2630,"children":2631},{"style":671},[2632],{"type":418,"value":1018},{"type":413,"tag":623,"props":2634,"children":2635},{"style":671},[2636],{"type":418,"value":1023},{"type":413,"tag":623,"props":2638,"children":2639},{"style":635},[2640],{"type":418,"value":2641},"ADOAzureReadyApp",{"type":413,"tag":623,"props":2643,"children":2644},{"style":671},[2645],{"type":418,"value":1023},{"type":413,"tag":623,"props":2647,"children":2648},{"style":671},[2649],{"type":418,"value":1037},{"type":413,"tag":623,"props":2651,"children":2652},{"style":671},[2653],{"type":418,"value":1042},{"type":413,"tag":623,"props":2655,"children":2656},{"class":625,"line":1054},[2657],{"type":413,"tag":623,"props":2658,"children":2659},{"style":671},[2660],{"type":418,"value":1051},{"type":413,"tag":623,"props":2662,"children":2663},{"class":625,"line":1087},[2664,2669,2673,2677,2682],{"type":413,"tag":623,"props":2665,"children":2666},{"style":1058},[2667],{"type":418,"value":2668},"    DisplayName ",{"type":413,"tag":623,"props":2670,"children":2671},{"style":671},[2672],{"type":418,"value":1066},{"type":413,"tag":623,"props":2674,"children":2675},{"style":671},[2676],{"type":418,"value":674},{"type":413,"tag":623,"props":2678,"children":2679},{"style":635},[2680],{"type":418,"value":2681},"ADO Azure Ready App",{"type":413,"tag":623,"props":2683,"children":2684},{"style":671},[2685],{"type":418,"value":684},{"type":413,"tag":623,"props":2687,"children":2688},{"class":625,"line":1104},[2689],{"type":413,"tag":623,"props":2690,"children":2691},{"style":671},[2692],{"type":418,"value":1352},{"type":413,"tag":623,"props":2694,"children":2695},{"class":625,"line":1113},[2696,2700,2704,2709,2713,2718,2722,2726,2731,2735,2739],{"type":413,"tag":623,"props":2697,"children":2698},{"style":630},[2699],{"type":418,"value":994},{"type":413,"tag":623,"props":2701,"children":2702},{"style":630},[2703],{"type":418,"value":2076},{"type":413,"tag":623,"props":2705,"children":2706},{"style":671},[2707],{"type":418,"value":2708},"  =",{"type":413,"tag":623,"props":2710,"children":2711},{"style":671},[2712],{"type":418,"value":638},{"type":413,"tag":623,"props":2714,"children":2715},{"style":630},[2716],{"type":418,"value":2717}," ServicePrincipal",{"type":413,"tag":623,"props":2719,"children":2720},{"style":671},[2721],{"type":418,"value":1018},{"type":413,"tag":623,"props":2723,"children":2724},{"style":671},[2725],{"type":418,"value":1023},{"type":413,"tag":623,"props":2727,"children":2728},{"style":635},[2729],{"type":418,"value":2730},"AzureReadyServicePrincipal",{"type":413,"tag":623,"props":2732,"children":2733},{"style":671},[2734],{"type":418,"value":1023},{"type":413,"tag":623,"props":2736,"children":2737},{"style":671},[2738],{"type":418,"value":1037},{"type":413,"tag":623,"props":2740,"children":2741},{"style":671},[2742],{"type":418,"value":1042},{"type":413,"tag":623,"props":2744,"children":2745},{"class":625,"line":1161},[2746],{"type":413,"tag":623,"props":2747,"children":2748},{"style":671},[2749],{"type":418,"value":1051},{"type":413,"tag":623,"props":2751,"children":2752},{"class":625,"line":1207},[2753,2758,2762,2766,2770,2774],{"type":413,"tag":623,"props":2754,"children":2755},{"style":1058},[2756],{"type":418,"value":2757},"    ApplicationId ",{"type":413,"tag":623,"props":2759,"children":2760},{"style":671},[2761],{"type":418,"value":1066},{"type":413,"tag":623,"props":2763,"children":2764},{"style":1058},[2765],{"type":418,"value":2615},{"type":413,"tag":623,"props":2767,"children":2768},{"style":671},[2769],{"type":418,"value":1404},{"type":413,"tag":623,"props":2771,"children":2772},{"style":1058},[2773],{"type":418,"value":2085},{"type":413,"tag":623,"props":2775,"children":2776},{"style":671},[2777],{"type":418,"value":1084},{"type":413,"tag":623,"props":2779,"children":2780},{"class":625,"line":1251},[2781],{"type":413,"tag":623,"props":2782,"children":2783},{"style":671},[2784],{"type":418,"value":1352},{"type":413,"tag":623,"props":2786,"children":2787},{"class":625,"line":1296},[2788],{"type":413,"tag":623,"props":2789,"children":2791},{"emptyLinePlaceholder":2790},true,[2792],{"type":418,"value":2793},"\n",{"type":413,"tag":623,"props":2795,"children":2796},{"class":625,"line":1337},[2797,2801,2805,2809,2813,2817,2821,2825,2829,2833,2837,2841,2845],{"type":413,"tag":623,"props":2798,"children":2799},{"style":630},[2800],{"type":418,"value":994},{"type":413,"tag":623,"props":2802,"children":2803},{"style":630},[2804],{"type":418,"value":2002},{"type":413,"tag":623,"props":2806,"children":2807},{"style":671},[2808],{"type":418,"value":1004},{"type":413,"tag":623,"props":2810,"children":2811},{"style":1058},[2812],{"type":418,"value":2137},{"type":413,"tag":623,"props":2814,"children":2815},{"style":671},[2816],{"type":418,"value":1404},{"type":413,"tag":623,"props":2818,"children":2819},{"style":1407},[2820],{"type":418,"value":2187},{"type":413,"tag":623,"props":2822,"children":2823},{"style":671},[2824],{"type":418,"value":1018},{"type":413,"tag":623,"props":2826,"children":2827},{"style":630},[2828],{"type":418,"value":2196},{"type":413,"tag":623,"props":2830,"children":2831},{"style":671},[2832],{"type":418,"value":2201},{"type":413,"tag":623,"props":2834,"children":2835},{"style":1058},[2836],{"type":418,"value":2206},{"type":413,"tag":623,"props":2838,"children":2839},{"style":671},[2840],{"type":418,"value":1404},{"type":413,"tag":623,"props":2842,"children":2843},{"style":1058},[2844],{"type":418,"value":2272},{"type":413,"tag":623,"props":2846,"children":2847},{"style":671},[2848],{"type":418,"value":2220},{"type":413,"tag":623,"props":2850,"children":2851},{"class":625,"line":1346},[2852,2858,2863,2867,2871,2876,2880,2884],{"type":413,"tag":623,"props":2853,"children":2855},{"style":2854},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[2856],{"type":418,"value":2857},"new",{"type":413,"tag":623,"props":2859,"children":2860},{"style":1407},[2861],{"type":418,"value":2862}," RoleAssignment",{"type":413,"tag":623,"props":2864,"children":2865},{"style":671},[2866],{"type":418,"value":1018},{"type":413,"tag":623,"props":2868,"children":2869},{"style":671},[2870],{"type":418,"value":1023},{"type":413,"tag":623,"props":2872,"children":2873},{"style":635},[2874],{"type":418,"value":2875},"contributor",{"type":413,"tag":623,"props":2877,"children":2878},{"style":671},[2879],{"type":418,"value":1023},{"type":413,"tag":623,"props":2881,"children":2882},{"style":671},[2883],{"type":418,"value":1037},{"type":413,"tag":623,"props":2885,"children":2886},{"style":671},[2887],{"type":418,"value":1042},{"type":413,"tag":623,"props":2889,"children":2890},{"class":625,"line":2100},[2891],{"type":413,"tag":623,"props":2892,"children":2893},{"style":671},[2894],{"type":418,"value":1051},{"type":413,"tag":623,"props":2896,"children":2898},{"class":625,"line":2897},14,[2899,2904,2908,2912,2916,2920],{"type":413,"tag":623,"props":2900,"children":2901},{"style":1058},[2902],{"type":418,"value":2903},"    PrincipalId",{"type":413,"tag":623,"props":2905,"children":2906},{"style":671},[2907],{"type":418,"value":1066},{"type":413,"tag":623,"props":2909,"children":2910},{"style":1058},[2911],{"type":418,"value":2076},{"type":413,"tag":623,"props":2913,"children":2914},{"style":671},[2915],{"type":418,"value":1404},{"type":413,"tag":623,"props":2917,"children":2918},{"style":1058},[2919],{"type":418,"value":1447},{"type":413,"tag":623,"props":2921,"children":2922},{"style":671},[2923],{"type":418,"value":1084},{"type":413,"tag":623,"props":2925,"children":2927},{"class":625,"line":2926},15,[2928,2933,2937,2942,2946,2951],{"type":413,"tag":623,"props":2929,"children":2930},{"style":1058},[2931],{"type":418,"value":2932},"    PrincipalType",{"type":413,"tag":623,"props":2934,"children":2935},{"style":671},[2936],{"type":418,"value":1066},{"type":413,"tag":623,"props":2938,"children":2939},{"style":1058},[2940],{"type":418,"value":2941}," PrincipalType",{"type":413,"tag":623,"props":2943,"children":2944},{"style":671},[2945],{"type":418,"value":1404},{"type":413,"tag":623,"props":2947,"children":2948},{"style":1058},[2949],{"type":418,"value":2950},"ServicePrincipal",{"type":413,"tag":623,"props":2952,"children":2953},{"style":671},[2954],{"type":418,"value":1084},{"type":413,"tag":623,"props":2956,"children":2958},{"class":625,"line":2957},16,[2959,2964,2968,2973,2977,2982],{"type":413,"tag":623,"props":2960,"children":2961},{"style":1058},[2962],{"type":418,"value":2963},"    RoleDefinitionId ",{"type":413,"tag":623,"props":2965,"children":2966},{"style":671},[2967],{"type":418,"value":1066},{"type":413,"tag":623,"props":2969,"children":2970},{"style":1058},[2971],{"type":418,"value":2972}," AzureBuiltInRoles",{"type":413,"tag":623,"props":2974,"children":2975},{"style":671},[2976],{"type":418,"value":1404},{"type":413,"tag":623,"props":2978,"children":2979},{"style":1058},[2980],{"type":418,"value":2981},"Contributor",{"type":413,"tag":623,"props":2983,"children":2984},{"style":671},[2985],{"type":418,"value":1084},{"type":413,"tag":623,"props":2987,"children":2989},{"class":625,"line":2988},17,[2990,2995,2999,3004,3008,3013,3017,3021,3025,3029,3034,3038],{"type":413,"tag":623,"props":2991,"children":2992},{"style":1058},[2993],{"type":418,"value":2994},"    Scope ",{"type":413,"tag":623,"props":2996,"children":2997},{"style":671},[2998],{"type":418,"value":1066},{"type":413,"tag":623,"props":3000,"children":3001},{"style":1058},[3002],{"type":418,"value":3003}," Output",{"type":413,"tag":623,"props":3005,"children":3006},{"style":671},[3007],{"type":418,"value":1404},{"type":413,"tag":623,"props":3009,"children":3010},{"style":1407},[3011],{"type":418,"value":3012},"Format",{"type":413,"tag":623,"props":3014,"children":3015},{"style":671},[3016],{"type":418,"value":1018},{"type":413,"tag":623,"props":3018,"children":3019},{"style":671},[3020],{"type":418,"value":2446},{"type":413,"tag":623,"props":3022,"children":3023},{"style":635},[3024],{"type":418,"value":2451},{"type":413,"tag":623,"props":3026,"children":3027},{"style":671},[3028],{"type":418,"value":2456},{"type":413,"tag":623,"props":3030,"children":3031},{"style":1058},[3032],{"type":418,"value":3033},"subscriptionId",{"type":413,"tag":623,"props":3035,"children":3036},{"style":671},[3037],{"type":418,"value":2465},{"type":413,"tag":623,"props":3039,"children":3040},{"style":671},[3041],{"type":418,"value":3042},")\n",{"type":413,"tag":623,"props":3044,"children":3046},{"class":625,"line":3045},18,[3047],{"type":413,"tag":623,"props":3048,"children":3049},{"style":671},[3050],{"type":418,"value":1352},{"type":413,"tag":496,"props":3052,"children":3053},{"icon":498},[3054],{"type":413,"tag":414,"props":3055,"children":3056},{},[3057,3059],{"type":418,"value":3058},"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":413,"tag":432,"props":3060,"children":3063},{"href":3061,"rel":3062},"https://www.pulumi.com/registry/packages/azure-native/api-docs/managedidentity/userassignedidentity/",[436],[3064],{"type":418,"value":3065},"User Assigned Identity",{"type":413,"tag":414,"props":3067,"children":3068},{},[3069],{"type":418,"value":3070},"Now that everything is created, we can create the Federated identity credentials:",{"type":413,"tag":612,"props":3072,"children":3074},{"className":981,"code":3073,"language":326,"meta":401,"style":401},"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",[3075],{"type":413,"tag":619,"props":3076,"children":3077},{"__ignoreMap":401},[3078,3121,3128,3157,3185,3213,3248,3277,3385],{"type":413,"tag":623,"props":3079,"children":3080},{"class":625,"line":626},[3081,3085,3090,3094,3098,3103,3107,3111,3116],{"type":413,"tag":623,"props":3082,"children":3083},{"style":2854},[3084],{"type":418,"value":2857},{"type":413,"tag":623,"props":3086,"children":3087},{"style":1407},[3088],{"type":418,"value":3089}," ApplicationFederatedIdentityCredential",{"type":413,"tag":623,"props":3091,"children":3092},{"style":671},[3093],{"type":418,"value":1018},{"type":413,"tag":623,"props":3095,"children":3096},{"style":671},[3097],{"type":418,"value":1023},{"type":413,"tag":623,"props":3099,"children":3100},{"style":635},[3101],{"type":418,"value":3102},"ADOAzureReadyAppFederatedIdentityCredential",{"type":413,"tag":623,"props":3104,"children":3105},{"style":671},[3106],{"type":418,"value":1023},{"type":413,"tag":623,"props":3108,"children":3109},{"style":671},[3110],{"type":418,"value":1037},{"type":413,"tag":623,"props":3112,"children":3113},{"style":671},[3114],{"type":418,"value":3115}," new()",{"type":413,"tag":623,"props":3117,"children":3118},{"style":1058},[3119],{"type":418,"value":3120}," \n",{"type":413,"tag":623,"props":3122,"children":3123},{"class":625,"line":1045},[3124],{"type":413,"tag":623,"props":3125,"children":3126},{"style":671},[3127],{"type":418,"value":1051},{"type":413,"tag":623,"props":3129,"children":3130},{"class":625,"line":1054},[3131,3136,3140,3144,3148,3153],{"type":413,"tag":623,"props":3132,"children":3133},{"style":1058},[3134],{"type":418,"value":3135},"    ApplicationObjectId ",{"type":413,"tag":623,"props":3137,"children":3138},{"style":671},[3139],{"type":418,"value":1066},{"type":413,"tag":623,"props":3141,"children":3142},{"style":1058},[3143],{"type":418,"value":2615},{"type":413,"tag":623,"props":3145,"children":3146},{"style":671},[3147],{"type":418,"value":1404},{"type":413,"tag":623,"props":3149,"children":3150},{"style":1058},[3151],{"type":418,"value":3152},"ObjectId",{"type":413,"tag":623,"props":3154,"children":3155},{"style":671},[3156],{"type":418,"value":1084},{"type":413,"tag":623,"props":3158,"children":3159},{"class":625,"line":1087},[3160,3164,3168,3172,3177,3181],{"type":413,"tag":623,"props":3161,"children":3162},{"style":1058},[3163],{"type":418,"value":2668},{"type":413,"tag":623,"props":3165,"children":3166},{"style":671},[3167],{"type":418,"value":1066},{"type":413,"tag":623,"props":3169,"children":3170},{"style":671},[3171],{"type":418,"value":674},{"type":413,"tag":623,"props":3173,"children":3174},{"style":635},[3175],{"type":418,"value":3176},"AzureReadyDeploys",{"type":413,"tag":623,"props":3178,"children":3179},{"style":671},[3180],{"type":418,"value":1023},{"type":413,"tag":623,"props":3182,"children":3183},{"style":671},[3184],{"type":418,"value":1084},{"type":413,"tag":623,"props":3186,"children":3187},{"class":625,"line":1104},[3188,3192,3196,3200,3205,3209],{"type":413,"tag":623,"props":3189,"children":3190},{"style":1058},[3191],{"type":418,"value":1061},{"type":413,"tag":623,"props":3193,"children":3194},{"style":671},[3195],{"type":418,"value":1066},{"type":413,"tag":623,"props":3197,"children":3198},{"style":671},[3199],{"type":418,"value":674},{"type":413,"tag":623,"props":3201,"children":3202},{"style":635},[3203],{"type":418,"value":3204},"Deployments for azure-ready-repository",{"type":413,"tag":623,"props":3206,"children":3207},{"style":671},[3208],{"type":418,"value":1023},{"type":413,"tag":623,"props":3210,"children":3211},{"style":671},[3212],{"type":418,"value":1084},{"type":413,"tag":623,"props":3214,"children":3215},{"class":625,"line":1113},[3216,3221,3225,3230,3234,3239,3243],{"type":413,"tag":623,"props":3217,"children":3218},{"style":1058},[3219],{"type":418,"value":3220},"    Audiences ",{"type":413,"tag":623,"props":3222,"children":3223},{"style":671},[3224],{"type":418,"value":1066},{"type":413,"tag":623,"props":3226,"children":3227},{"style":671},[3228],{"type":418,"value":3229}," new(){",{"type":413,"tag":623,"props":3231,"children":3232},{"style":671},[3233],{"type":418,"value":1023},{"type":413,"tag":623,"props":3235,"children":3236},{"style":635},[3237],{"type":418,"value":3238},"api://AzureADTokenExchange",{"type":413,"tag":623,"props":3240,"children":3241},{"style":671},[3242],{"type":418,"value":1023},{"type":413,"tag":623,"props":3244,"children":3245},{"style":671},[3246],{"type":418,"value":3247}," },\n",{"type":413,"tag":623,"props":3249,"children":3250},{"class":625,"line":1161},[3251,3256,3260,3264,3268,3273],{"type":413,"tag":623,"props":3252,"children":3253},{"style":1058},[3254],{"type":418,"value":3255},"    Issuer ",{"type":413,"tag":623,"props":3257,"children":3258},{"style":671},[3259],{"type":418,"value":1066},{"type":413,"tag":623,"props":3261,"children":3262},{"style":1058},[3263],{"type":418,"value":1834},{"type":413,"tag":623,"props":3265,"children":3266},{"style":671},[3267],{"type":418,"value":1404},{"type":413,"tag":623,"props":3269,"children":3270},{"style":1058},[3271],{"type":418,"value":3272},"WorkloadIdentityFederationIssuer",{"type":413,"tag":623,"props":3274,"children":3275},{"style":671},[3276],{"type":418,"value":1084},{"type":413,"tag":623,"props":3278,"children":3279},{"class":625,"line":1207},[3280,3285,3289,3293,3297,3301,3305,3309,3314,3318,3323,3328,3333,3337,3342,3346,3351,3355,3359,3363,3368,3372,3377,3381],{"type":413,"tag":623,"props":3281,"children":3282},{"style":1058},[3283],{"type":418,"value":3284},"    Subject ",{"type":413,"tag":623,"props":3286,"children":3287},{"style":671},[3288],{"type":418,"value":1066},{"type":413,"tag":623,"props":3290,"children":3291},{"style":1058},[3292],{"type":418,"value":3003},{"type":413,"tag":623,"props":3294,"children":3295},{"style":671},[3296],{"type":418,"value":1404},{"type":413,"tag":623,"props":3298,"children":3299},{"style":1407},[3300],{"type":418,"value":3012},{"type":413,"tag":623,"props":3302,"children":3303},{"style":671},[3304],{"type":418,"value":1018},{"type":413,"tag":623,"props":3306,"children":3307},{"style":671},[3308],{"type":418,"value":2446},{"type":413,"tag":623,"props":3310,"children":3311},{"style":635},[3312],{"type":418,"value":3313},"sc://",{"type":413,"tag":623,"props":3315,"children":3316},{"style":671},[3317],{"type":418,"value":2456},{"type":413,"tag":623,"props":3319,"children":3320},{"style":1058},[3321],{"type":418,"value":3322},"organisationName",{"type":413,"tag":623,"props":3324,"children":3325},{"style":671},[3326],{"type":418,"value":3327},"}",{"type":413,"tag":623,"props":3329,"children":3330},{"style":635},[3331],{"type":418,"value":3332},"/",{"type":413,"tag":623,"props":3334,"children":3335},{"style":671},[3336],{"type":418,"value":2456},{"type":413,"tag":623,"props":3338,"children":3339},{"style":1058},[3340],{"type":418,"value":3341},"project",{"type":413,"tag":623,"props":3343,"children":3344},{"style":671},[3345],{"type":418,"value":1404},{"type":413,"tag":623,"props":3347,"children":3348},{"style":1058},[3349],{"type":418,"value":3350},"Name",{"type":413,"tag":623,"props":3352,"children":3353},{"style":671},[3354],{"type":418,"value":3327},{"type":413,"tag":623,"props":3356,"children":3357},{"style":635},[3358],{"type":418,"value":3332},{"type":413,"tag":623,"props":3360,"children":3361},{"style":671},[3362],{"type":418,"value":2456},{"type":413,"tag":623,"props":3364,"children":3365},{"style":1058},[3366],{"type":418,"value":3367},"serviceConnection",{"type":413,"tag":623,"props":3369,"children":3370},{"style":671},[3371],{"type":418,"value":1404},{"type":413,"tag":623,"props":3373,"children":3374},{"style":1058},[3375],{"type":418,"value":3376},"ServiceEndpointName",{"type":413,"tag":623,"props":3378,"children":3379},{"style":671},[3380],{"type":418,"value":2465},{"type":413,"tag":623,"props":3382,"children":3383},{"style":671},[3384],{"type":418,"value":3042},{"type":413,"tag":623,"props":3386,"children":3387},{"class":625,"line":1251},[3388],{"type":413,"tag":623,"props":3389,"children":3390},{"style":671},[3391],{"type":418,"value":1352},{"type":413,"tag":414,"props":3393,"children":3394},{},[3395,3397,3403],{"type":418,"value":3396},"You can observe that the federation subject adheres to a particular format (",{"type":413,"tag":619,"props":3398,"children":3400},{"className":3399},[],[3401],{"type":418,"value":3402},"sc://\u003Corg>/\u003Cproject>/\u003Cservice connection name>",{"type":418,"value":3404},"), which identifies the service connection authorized for authentication with Azure.",{"type":413,"tag":600,"props":3406,"children":3408},{"id":3407},"create-the-deployment-pipeline",[3409],{"type":418,"value":3410},"Create the deployment pipeline",{"type":413,"tag":414,"props":3412,"children":3413},{},[3414],{"type":418,"value":3415},"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":413,"tag":414,"props":3417,"children":3418},{},[3419,3421,3427],{"type":418,"value":3420},"For this purpose, I have written a very simple YAML pipeline that runs the ",{"type":413,"tag":619,"props":3422,"children":3424},{"className":3423},[],[3425],{"type":418,"value":3426},"AzureCLI",{"type":418,"value":3428}," task to show information about the Azure subscription associated with the previously created service connection.",{"type":413,"tag":612,"props":3430,"children":3432},{"className":981,"code":3431,"language":326,"meta":401,"style":401},"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",[3433],{"type":413,"tag":619,"props":3434,"children":3435},{"__ignoreMap":401},[3436,3449,3462,3469,3481,3509,3516,3528,3555,3567,3593,3618,3643],{"type":413,"tag":623,"props":3437,"children":3438},{"class":625,"line":626},[3439,3444],{"type":413,"tag":623,"props":3440,"children":3441},{"style":630},[3442],{"type":418,"value":3443},"trigger",{"type":413,"tag":623,"props":3445,"children":3446},{"style":671},[3447],{"type":418,"value":3448},":\n",{"type":413,"tag":623,"props":3450,"children":3451},{"class":625,"line":1045},[3452,3457],{"type":413,"tag":623,"props":3453,"children":3454},{"style":671},[3455],{"type":418,"value":3456},"  -",{"type":413,"tag":623,"props":3458,"children":3459},{"style":1058},[3460],{"type":418,"value":3461}," main\n",{"type":413,"tag":623,"props":3463,"children":3464},{"class":625,"line":1054},[3465],{"type":413,"tag":623,"props":3466,"children":3467},{"emptyLinePlaceholder":2790},[3468],{"type":418,"value":2793},{"type":413,"tag":623,"props":3470,"children":3471},{"class":625,"line":1087},[3472,3477],{"type":413,"tag":623,"props":3473,"children":3474},{"style":630},[3475],{"type":418,"value":3476},"pool",{"type":413,"tag":623,"props":3478,"children":3479},{"style":671},[3480],{"type":418,"value":3448},{"type":413,"tag":623,"props":3482,"children":3483},{"class":625,"line":1104},[3484,3489,3494,3499,3504],{"type":413,"tag":623,"props":3485,"children":3486},{"style":630},[3487],{"type":418,"value":3488},"  vmImage",{"type":413,"tag":623,"props":3490,"children":3491},{"style":671},[3492],{"type":418,"value":3493},":",{"type":413,"tag":623,"props":3495,"children":3496},{"style":1058},[3497],{"type":418,"value":3498}," ubuntu",{"type":413,"tag":623,"props":3500,"children":3501},{"style":671},[3502],{"type":418,"value":3503},"-",{"type":413,"tag":623,"props":3505,"children":3506},{"style":1058},[3507],{"type":418,"value":3508},"latest\n",{"type":413,"tag":623,"props":3510,"children":3511},{"class":625,"line":1113},[3512],{"type":413,"tag":623,"props":3513,"children":3514},{"emptyLinePlaceholder":2790},[3515],{"type":418,"value":2793},{"type":413,"tag":623,"props":3517,"children":3518},{"class":625,"line":1161},[3519,3524],{"type":413,"tag":623,"props":3520,"children":3521},{"style":630},[3522],{"type":418,"value":3523},"steps",{"type":413,"tag":623,"props":3525,"children":3526},{"style":671},[3527],{"type":418,"value":3448},{"type":413,"tag":623,"props":3529,"children":3530},{"class":625,"line":1207},[3531,3535,3540,3544,3549],{"type":413,"tag":623,"props":3532,"children":3533},{"style":671},[3534],{"type":418,"value":3456},{"type":413,"tag":623,"props":3536,"children":3537},{"style":630},[3538],{"type":418,"value":3539}," task",{"type":413,"tag":623,"props":3541,"children":3542},{"style":671},[3543],{"type":418,"value":3493},{"type":413,"tag":623,"props":3545,"children":3546},{"style":1058},[3547],{"type":418,"value":3548}," AzureCLI@",{"type":413,"tag":623,"props":3550,"children":3552},{"style":3551},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[3553],{"type":418,"value":3554},"2\n",{"type":413,"tag":623,"props":3556,"children":3557},{"class":625,"line":1251},[3558,3563],{"type":413,"tag":623,"props":3559,"children":3560},{"style":630},[3561],{"type":418,"value":3562},"    inputs",{"type":413,"tag":623,"props":3564,"children":3565},{"style":671},[3566],{"type":418,"value":3448},{"type":413,"tag":623,"props":3568,"children":3569},{"class":625,"line":1296},[3570,3575,3579,3584,3588],{"type":413,"tag":623,"props":3571,"children":3572},{"style":630},[3573],{"type":418,"value":3574},"      azureSubscription",{"type":413,"tag":623,"props":3576,"children":3577},{"style":671},[3578],{"type":418,"value":3493},{"type":413,"tag":623,"props":3580,"children":3581},{"style":671},[3582],{"type":418,"value":3583}," '",{"type":413,"tag":623,"props":3585,"children":3586},{"style":635},[3587],{"type":418,"value":1927},{"type":413,"tag":623,"props":3589,"children":3590},{"style":671},[3591],{"type":418,"value":3592},"'\n",{"type":413,"tag":623,"props":3594,"children":3595},{"class":625,"line":1337},[3596,3601,3605,3609,3614],{"type":413,"tag":623,"props":3597,"children":3598},{"style":630},[3599],{"type":418,"value":3600},"      scriptType",{"type":413,"tag":623,"props":3602,"children":3603},{"style":671},[3604],{"type":418,"value":3493},{"type":413,"tag":623,"props":3606,"children":3607},{"style":671},[3608],{"type":418,"value":3583},{"type":413,"tag":623,"props":3610,"children":3611},{"style":635},[3612],{"type":418,"value":3613},"pscore",{"type":413,"tag":623,"props":3615,"children":3616},{"style":671},[3617],{"type":418,"value":3592},{"type":413,"tag":623,"props":3619,"children":3620},{"class":625,"line":1346},[3621,3626,3630,3634,3639],{"type":413,"tag":623,"props":3622,"children":3623},{"style":630},[3624],{"type":418,"value":3625},"      scriptLocation",{"type":413,"tag":623,"props":3627,"children":3628},{"style":671},[3629],{"type":418,"value":3493},{"type":413,"tag":623,"props":3631,"children":3632},{"style":671},[3633],{"type":418,"value":3583},{"type":413,"tag":623,"props":3635,"children":3636},{"style":635},[3637],{"type":418,"value":3638},"inlineScript",{"type":413,"tag":623,"props":3640,"children":3641},{"style":671},[3642],{"type":418,"value":3592},{"type":413,"tag":623,"props":3644,"children":3645},{"class":625,"line":2100},[3646,3651,3655,3659,3664],{"type":413,"tag":623,"props":3647,"children":3648},{"style":630},[3649],{"type":418,"value":3650},"      inlineScript",{"type":413,"tag":623,"props":3652,"children":3653},{"style":671},[3654],{"type":418,"value":3493},{"type":413,"tag":623,"props":3656,"children":3657},{"style":671},[3658],{"type":418,"value":3583},{"type":413,"tag":623,"props":3660,"children":3661},{"style":635},[3662],{"type":418,"value":3663},"az account show --query id -o tsv",{"type":413,"tag":623,"props":3665,"children":3666},{"style":671},[3667],{"type":418,"value":3592},{"type":413,"tag":414,"props":3669,"children":3670},{},[3671],{"type":418,"value":3672},"We can add this file in the Git repository:",{"type":413,"tag":612,"props":3674,"children":3676},{"className":981,"code":3675,"language":326,"meta":401,"style":401},"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",[3677],{"type":413,"tag":619,"props":3678,"children":3679},{"__ignoreMap":401},[3680,3730,3737,3766,3821,3850,3897,3921],{"type":413,"tag":623,"props":3681,"children":3682},{"class":625,"line":626},[3683,3687,3692,3696,3700,3705,3709,3713,3718,3722,3726],{"type":413,"tag":623,"props":3684,"children":3685},{"style":630},[3686],{"type":418,"value":994},{"type":413,"tag":623,"props":3688,"children":3689},{"style":630},[3690],{"type":418,"value":3691}," pipelineFile",{"type":413,"tag":623,"props":3693,"children":3694},{"style":671},[3695],{"type":418,"value":1004},{"type":413,"tag":623,"props":3697,"children":3698},{"style":671},[3699],{"type":418,"value":638},{"type":413,"tag":623,"props":3701,"children":3702},{"style":630},[3703],{"type":418,"value":3704}," GitRepositoryFile",{"type":413,"tag":623,"props":3706,"children":3707},{"style":671},[3708],{"type":418,"value":1018},{"type":413,"tag":623,"props":3710,"children":3711},{"style":671},[3712],{"type":418,"value":1023},{"type":413,"tag":623,"props":3714,"children":3715},{"style":635},[3716],{"type":418,"value":3717},"AzurePipeline",{"type":413,"tag":623,"props":3719,"children":3720},{"style":671},[3721],{"type":418,"value":1023},{"type":413,"tag":623,"props":3723,"children":3724},{"style":671},[3725],{"type":418,"value":1037},{"type":413,"tag":623,"props":3727,"children":3728},{"style":671},[3729],{"type":418,"value":1042},{"type":413,"tag":623,"props":3731,"children":3732},{"class":625,"line":1045},[3733],{"type":413,"tag":623,"props":3734,"children":3735},{"style":671},[3736],{"type":418,"value":1051},{"type":413,"tag":623,"props":3738,"children":3739},{"class":625,"line":1054},[3740,3745,3749,3753,3758,3762],{"type":413,"tag":623,"props":3741,"children":3742},{"style":1058},[3743],{"type":418,"value":3744},"    File ",{"type":413,"tag":623,"props":3746,"children":3747},{"style":671},[3748],{"type":418,"value":1066},{"type":413,"tag":623,"props":3750,"children":3751},{"style":671},[3752],{"type":418,"value":674},{"type":413,"tag":623,"props":3754,"children":3755},{"style":635},[3756],{"type":418,"value":3757},"azure-pipelines.yaml",{"type":413,"tag":623,"props":3759,"children":3760},{"style":671},[3761],{"type":418,"value":1023},{"type":413,"tag":623,"props":3763,"children":3764},{"style":671},[3765],{"type":418,"value":1084},{"type":413,"tag":623,"props":3767,"children":3768},{"class":625,"line":1087},[3769,3774,3778,3782,3786,3790,3794,3799,3803,3808,3812,3816],{"type":413,"tag":623,"props":3770,"children":3771},{"style":1058},[3772],{"type":418,"value":3773},"    RepositoryId ",{"type":413,"tag":623,"props":3775,"children":3776},{"style":671},[3777],{"type":418,"value":1066},{"type":413,"tag":623,"props":3779,"children":3780},{"style":1058},[3781],{"type":418,"value":1390},{"type":413,"tag":623,"props":3783,"children":3784},{"style":671},[3785],{"type":418,"value":1404},{"type":413,"tag":623,"props":3787,"children":3788},{"style":1407},[3789],{"type":418,"value":2187},{"type":413,"tag":623,"props":3791,"children":3792},{"style":671},[3793],{"type":418,"value":1018},{"type":413,"tag":623,"props":3795,"children":3796},{"style":630},[3797],{"type":418,"value":3798},"r",{"type":413,"tag":623,"props":3800,"children":3801},{"style":671},[3802],{"type":418,"value":2201},{"type":413,"tag":623,"props":3804,"children":3805},{"style":1058},[3806],{"type":418,"value":3807}," r",{"type":413,"tag":623,"props":3809,"children":3810},{"style":671},[3811],{"type":418,"value":1404},{"type":413,"tag":623,"props":3813,"children":3814},{"style":1058},[3815],{"type":418,"value":1447},{"type":413,"tag":623,"props":3817,"children":3818},{"style":671},[3819],{"type":418,"value":3820},"),\n",{"type":413,"tag":623,"props":3822,"children":3823},{"class":625,"line":1104},[3824,3829,3833,3837,3842,3846],{"type":413,"tag":623,"props":3825,"children":3826},{"style":1058},[3827],{"type":418,"value":3828},"    CommitMessage ",{"type":413,"tag":623,"props":3830,"children":3831},{"style":671},[3832],{"type":418,"value":1066},{"type":413,"tag":623,"props":3834,"children":3835},{"style":671},[3836],{"type":418,"value":674},{"type":413,"tag":623,"props":3838,"children":3839},{"style":635},[3840],{"type":418,"value":3841},"Add preconfigured pipeline file",{"type":413,"tag":623,"props":3843,"children":3844},{"style":671},[3845],{"type":418,"value":1023},{"type":413,"tag":623,"props":3847,"children":3848},{"style":671},[3849],{"type":418,"value":1084},{"type":413,"tag":623,"props":3851,"children":3852},{"class":625,"line":1113},[3853,3858,3862,3867,3871,3876,3880,3884,3889,3893],{"type":413,"tag":623,"props":3854,"children":3855},{"style":1058},[3856],{"type":418,"value":3857},"    Content ",{"type":413,"tag":623,"props":3859,"children":3860},{"style":671},[3861],{"type":418,"value":1066},{"type":413,"tag":623,"props":3863,"children":3864},{"style":1058},[3865],{"type":418,"value":3866}," File",{"type":413,"tag":623,"props":3868,"children":3869},{"style":671},[3870],{"type":418,"value":1404},{"type":413,"tag":623,"props":3872,"children":3873},{"style":1407},[3874],{"type":418,"value":3875},"ReadAllText",{"type":413,"tag":623,"props":3877,"children":3878},{"style":671},[3879],{"type":418,"value":1018},{"type":413,"tag":623,"props":3881,"children":3882},{"style":671},[3883],{"type":418,"value":1023},{"type":413,"tag":623,"props":3885,"children":3886},{"style":635},[3887],{"type":418,"value":3888},"azure-pipelines.yml",{"type":413,"tag":623,"props":3890,"children":3891},{"style":671},[3892],{"type":418,"value":1023},{"type":413,"tag":623,"props":3894,"children":3895},{"style":671},[3896],{"type":418,"value":3820},{"type":413,"tag":623,"props":3898,"children":3899},{"class":625,"line":1161},[3900,3905,3909,3913,3917],{"type":413,"tag":623,"props":3901,"children":3902},{"style":1058},[3903],{"type":418,"value":3904},"    Branch ",{"type":413,"tag":623,"props":3906,"children":3907},{"style":671},[3908],{"type":418,"value":1066},{"type":413,"tag":623,"props":3910,"children":3911},{"style":671},[3912],{"type":418,"value":674},{"type":413,"tag":623,"props":3914,"children":3915},{"style":635},[3916],{"type":418,"value":1743},{"type":413,"tag":623,"props":3918,"children":3919},{"style":671},[3920],{"type":418,"value":684},{"type":413,"tag":623,"props":3922,"children":3923},{"class":625,"line":1207},[3924],{"type":413,"tag":623,"props":3925,"children":3926},{"style":671},[3927],{"type":418,"value":1352},{"type":413,"tag":414,"props":3929,"children":3930},{},[3931],{"type":418,"value":3932},"Now, we have to create the pipeline itself:",{"type":413,"tag":612,"props":3934,"children":3936},{"className":981,"code":3935,"language":326,"meta":401,"style":401},"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",[3937],{"type":413,"tag":619,"props":3938,"children":3939},{"__ignoreMap":401},[3940,3990,3997,4024,4049,4056,4108,4136,4165,4190,4197],{"type":413,"tag":623,"props":3941,"children":3942},{"class":625,"line":626},[3943,3947,3952,3956,3960,3965,3969,3973,3978,3982,3986],{"type":413,"tag":623,"props":3944,"children":3945},{"style":630},[3946],{"type":418,"value":994},{"type":413,"tag":623,"props":3948,"children":3949},{"style":630},[3950],{"type":418,"value":3951}," pipeline",{"type":413,"tag":623,"props":3953,"children":3954},{"style":671},[3955],{"type":418,"value":1004},{"type":413,"tag":623,"props":3957,"children":3958},{"style":671},[3959],{"type":418,"value":638},{"type":413,"tag":623,"props":3961,"children":3962},{"style":630},[3963],{"type":418,"value":3964}," BuildDefinition",{"type":413,"tag":623,"props":3966,"children":3967},{"style":671},[3968],{"type":418,"value":1018},{"type":413,"tag":623,"props":3970,"children":3971},{"style":671},[3972],{"type":418,"value":1023},{"type":413,"tag":623,"props":3974,"children":3975},{"style":635},[3976],{"type":418,"value":3977},"deployToAzure",{"type":413,"tag":623,"props":3979,"children":3980},{"style":671},[3981],{"type":418,"value":1023},{"type":413,"tag":623,"props":3983,"children":3984},{"style":671},[3985],{"type":418,"value":1037},{"type":413,"tag":623,"props":3987,"children":3988},{"style":671},[3989],{"type":418,"value":1042},{"type":413,"tag":623,"props":3991,"children":3992},{"class":625,"line":1045},[3993],{"type":413,"tag":623,"props":3994,"children":3995},{"style":671},[3996],{"type":418,"value":1051},{"type":413,"tag":623,"props":3998,"children":3999},{"class":625,"line":1054},[4000,4004,4008,4012,4016,4020],{"type":413,"tag":623,"props":4001,"children":4002},{"style":1058},[4003],{"type":418,"value":1430},{"type":413,"tag":623,"props":4005,"children":4006},{"style":671},[4007],{"type":418,"value":1066},{"type":413,"tag":623,"props":4009,"children":4010},{"style":1058},[4011],{"type":418,"value":999},{"type":413,"tag":623,"props":4013,"children":4014},{"style":671},[4015],{"type":418,"value":1404},{"type":413,"tag":623,"props":4017,"children":4018},{"style":1058},[4019],{"type":418,"value":1447},{"type":413,"tag":623,"props":4021,"children":4022},{"style":671},[4023],{"type":418,"value":1084},{"type":413,"tag":623,"props":4025,"children":4026},{"class":625,"line":1087},[4027,4032,4036,4040,4045],{"type":413,"tag":623,"props":4028,"children":4029},{"style":1058},[4030],{"type":418,"value":4031},"    Repository ",{"type":413,"tag":623,"props":4033,"children":4034},{"style":671},[4035],{"type":418,"value":1066},{"type":413,"tag":623,"props":4037,"children":4038},{"style":671},[4039],{"type":418,"value":638},{"type":413,"tag":623,"props":4041,"children":4042},{"style":630},[4043],{"type":418,"value":4044}," BuildDefinitionRepositoryArgs",{"type":413,"tag":623,"props":4046,"children":4047},{"style":671},[4048],{"type":418,"value":1604},{"type":413,"tag":623,"props":4050,"children":4051},{"class":625,"line":1104},[4052],{"type":413,"tag":623,"props":4053,"children":4054},{"style":671},[4055],{"type":418,"value":1110},{"type":413,"tag":623,"props":4057,"children":4058},{"class":625,"line":1113},[4059,4064,4068,4072,4076,4080,4084,4088,4092,4096,4100,4104],{"type":413,"tag":623,"props":4060,"children":4061},{"style":1058},[4062],{"type":418,"value":4063},"        RepoId ",{"type":413,"tag":623,"props":4065,"children":4066},{"style":671},[4067],{"type":418,"value":1066},{"type":413,"tag":623,"props":4069,"children":4070},{"style":1058},[4071],{"type":418,"value":1390},{"type":413,"tag":623,"props":4073,"children":4074},{"style":671},[4075],{"type":418,"value":1404},{"type":413,"tag":623,"props":4077,"children":4078},{"style":1407},[4079],{"type":418,"value":2187},{"type":413,"tag":623,"props":4081,"children":4082},{"style":671},[4083],{"type":418,"value":1018},{"type":413,"tag":623,"props":4085,"children":4086},{"style":630},[4087],{"type":418,"value":3798},{"type":413,"tag":623,"props":4089,"children":4090},{"style":671},[4091],{"type":418,"value":2201},{"type":413,"tag":623,"props":4093,"children":4094},{"style":1058},[4095],{"type":418,"value":3807},{"type":413,"tag":623,"props":4097,"children":4098},{"style":671},[4099],{"type":418,"value":1404},{"type":413,"tag":623,"props":4101,"children":4102},{"style":1058},[4103],{"type":418,"value":1447},{"type":413,"tag":623,"props":4105,"children":4106},{"style":671},[4107],{"type":418,"value":3820},{"type":413,"tag":623,"props":4109,"children":4110},{"class":625,"line":1161},[4111,4116,4120,4124,4128,4132],{"type":413,"tag":623,"props":4112,"children":4113},{"style":1058},[4114],{"type":418,"value":4115},"        BranchName ",{"type":413,"tag":623,"props":4117,"children":4118},{"style":671},[4119],{"type":418,"value":1066},{"type":413,"tag":623,"props":4121,"children":4122},{"style":671},[4123],{"type":418,"value":674},{"type":413,"tag":623,"props":4125,"children":4126},{"style":635},[4127],{"type":418,"value":1743},{"type":413,"tag":623,"props":4129,"children":4130},{"style":671},[4131],{"type":418,"value":1023},{"type":413,"tag":623,"props":4133,"children":4134},{"style":671},[4135],{"type":418,"value":1084},{"type":413,"tag":623,"props":4137,"children":4138},{"class":625,"line":1207},[4139,4144,4148,4152,4156,4161],{"type":413,"tag":623,"props":4140,"children":4141},{"style":1058},[4142],{"type":418,"value":4143},"        YmlPath ",{"type":413,"tag":623,"props":4145,"children":4146},{"style":671},[4147],{"type":418,"value":1066},{"type":413,"tag":623,"props":4149,"children":4150},{"style":1058},[4151],{"type":418,"value":3691},{"type":413,"tag":623,"props":4153,"children":4154},{"style":671},[4155],{"type":418,"value":1404},{"type":413,"tag":623,"props":4157,"children":4158},{"style":1058},[4159],{"type":418,"value":4160},"File",{"type":413,"tag":623,"props":4162,"children":4163},{"style":671},[4164],{"type":418,"value":1084},{"type":413,"tag":623,"props":4166,"children":4167},{"class":625,"line":1251},[4168,4173,4177,4181,4186],{"type":413,"tag":623,"props":4169,"children":4170},{"style":1058},[4171],{"type":418,"value":4172},"        RepoType ",{"type":413,"tag":623,"props":4174,"children":4175},{"style":671},[4176],{"type":418,"value":1066},{"type":413,"tag":623,"props":4178,"children":4179},{"style":671},[4180],{"type":418,"value":674},{"type":413,"tag":623,"props":4182,"children":4183},{"style":635},[4184],{"type":418,"value":4185},"TfsGit",{"type":413,"tag":623,"props":4187,"children":4188},{"style":671},[4189],{"type":418,"value":684},{"type":413,"tag":623,"props":4191,"children":4192},{"class":625,"line":1296},[4193],{"type":413,"tag":623,"props":4194,"children":4195},{"style":671},[4196],{"type":418,"value":2097},{"type":413,"tag":623,"props":4198,"children":4199},{"class":625,"line":1337},[4200],{"type":413,"tag":623,"props":4201,"children":4202},{"style":671},[4203],{"type":418,"value":1352},{"type":413,"tag":414,"props":4205,"children":4206},{},[4207],{"type":418,"value":4208},"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":413,"tag":612,"props":4210,"children":4212},{"className":981,"code":4211,"language":326,"meta":401,"style":401},"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",[4213],{"type":413,"tag":619,"props":4214,"children":4215},{"__ignoreMap":401},[4216,4253,4260,4287,4316,4362,4387],{"type":413,"tag":623,"props":4217,"children":4218},{"class":625,"line":626},[4219,4223,4228,4232,4236,4241,4245,4249],{"type":413,"tag":623,"props":4220,"children":4221},{"style":2854},[4222],{"type":418,"value":2857},{"type":413,"tag":623,"props":4224,"children":4225},{"style":1407},[4226],{"type":418,"value":4227}," PipelineAuthorization",{"type":413,"tag":623,"props":4229,"children":4230},{"style":671},[4231],{"type":418,"value":1018},{"type":413,"tag":623,"props":4233,"children":4234},{"style":671},[4235],{"type":418,"value":1023},{"type":413,"tag":623,"props":4237,"children":4238},{"style":635},[4239],{"type":418,"value":4240},"azureOidcPipelineAuthorization",{"type":413,"tag":623,"props":4242,"children":4243},{"style":671},[4244],{"type":418,"value":1023},{"type":413,"tag":623,"props":4246,"children":4247},{"style":671},[4248],{"type":418,"value":1037},{"type":413,"tag":623,"props":4250,"children":4251},{"style":671},[4252],{"type":418,"value":1042},{"type":413,"tag":623,"props":4254,"children":4255},{"class":625,"line":1045},[4256],{"type":413,"tag":623,"props":4257,"children":4258},{"style":671},[4259],{"type":418,"value":1051},{"type":413,"tag":623,"props":4261,"children":4262},{"class":625,"line":1054},[4263,4267,4271,4275,4279,4283],{"type":413,"tag":623,"props":4264,"children":4265},{"style":1058},[4266],{"type":418,"value":1430},{"type":413,"tag":623,"props":4268,"children":4269},{"style":671},[4270],{"type":418,"value":1066},{"type":413,"tag":623,"props":4272,"children":4273},{"style":1058},[4274],{"type":418,"value":999},{"type":413,"tag":623,"props":4276,"children":4277},{"style":671},[4278],{"type":418,"value":1404},{"type":413,"tag":623,"props":4280,"children":4281},{"style":1058},[4282],{"type":418,"value":1447},{"type":413,"tag":623,"props":4284,"children":4285},{"style":671},[4286],{"type":418,"value":1084},{"type":413,"tag":623,"props":4288,"children":4289},{"class":625,"line":1087},[4290,4295,4299,4303,4308,4312],{"type":413,"tag":623,"props":4291,"children":4292},{"style":1058},[4293],{"type":418,"value":4294},"    Type ",{"type":413,"tag":623,"props":4296,"children":4297},{"style":671},[4298],{"type":418,"value":1066},{"type":413,"tag":623,"props":4300,"children":4301},{"style":671},[4302],{"type":418,"value":674},{"type":413,"tag":623,"props":4304,"children":4305},{"style":635},[4306],{"type":418,"value":4307},"endpoint",{"type":413,"tag":623,"props":4309,"children":4310},{"style":671},[4311],{"type":418,"value":1023},{"type":413,"tag":623,"props":4313,"children":4314},{"style":671},[4315],{"type":418,"value":1084},{"type":413,"tag":623,"props":4317,"children":4318},{"class":625,"line":1104},[4319,4324,4328,4332,4336,4340,4344,4348,4353,4358],{"type":413,"tag":623,"props":4320,"children":4321},{"style":1058},[4322],{"type":418,"value":4323},"    PipelineId ",{"type":413,"tag":623,"props":4325,"children":4326},{"style":671},[4327],{"type":418,"value":1066},{"type":413,"tag":623,"props":4329,"children":4330},{"style":1058},[4331],{"type":418,"value":3951},{"type":413,"tag":623,"props":4333,"children":4334},{"style":671},[4335],{"type":418,"value":1404},{"type":413,"tag":623,"props":4337,"children":4338},{"style":1058},[4339],{"type":418,"value":1447},{"type":413,"tag":623,"props":4341,"children":4342},{"style":671},[4343],{"type":418,"value":1404},{"type":413,"tag":623,"props":4345,"children":4346},{"style":1407},[4347],{"type":418,"value":2187},{"type":413,"tag":623,"props":4349,"children":4350},{"style":671},[4351],{"type":418,"value":4352},"(int.",{"type":413,"tag":623,"props":4354,"children":4355},{"style":1058},[4356],{"type":418,"value":4357},"Parse",{"type":413,"tag":623,"props":4359,"children":4360},{"style":671},[4361],{"type":418,"value":3820},{"type":413,"tag":623,"props":4363,"children":4364},{"class":625,"line":1113},[4365,4370,4374,4378,4382],{"type":413,"tag":623,"props":4366,"children":4367},{"style":1058},[4368],{"type":418,"value":4369},"    ResourceId ",{"type":413,"tag":623,"props":4371,"children":4372},{"style":671},[4373],{"type":418,"value":1066},{"type":413,"tag":623,"props":4375,"children":4376},{"style":1058},[4377],{"type":418,"value":1834},{"type":413,"tag":623,"props":4379,"children":4380},{"style":671},[4381],{"type":418,"value":1404},{"type":413,"tag":623,"props":4383,"children":4384},{"style":1058},[4385],{"type":418,"value":4386},"Id\n",{"type":413,"tag":623,"props":4388,"children":4389},{"class":625,"line":1161},[4390],{"type":413,"tag":623,"props":4391,"children":4392},{"style":671},[4393],{"type":418,"value":1352},{"type":413,"tag":414,"props":4395,"children":4396},{},[4397],{"type":418,"value":4398},"The last thing we can do is create a stack output to expose the URL of the created pipeline:",{"type":413,"tag":612,"props":4400,"children":4402},{"className":981,"code":4401,"language":326,"meta":401,"style":401},"return new Dictionary\u003Cstring, object?>\n{\n    [\"pipelineUrl\"] = Output.Format($\"{organizationUrl}{project.Name}/_build?definitionId={pipeline.Id}\")\n};\n",[4403],{"type":413,"tag":619,"props":4404,"children":4405},{"__ignoreMap":401},[4406,4442,4449,4555],{"type":413,"tag":623,"props":4407,"children":4408},{"class":625,"line":626},[4409,4414,4418,4423,4428,4433,4437],{"type":413,"tag":623,"props":4410,"children":4411},{"style":2485},[4412],{"type":418,"value":4413},"return",{"type":413,"tag":623,"props":4415,"children":4416},{"style":671},[4417],{"type":418,"value":638},{"type":413,"tag":623,"props":4419,"children":4420},{"style":630},[4421],{"type":418,"value":4422}," Dictionary",{"type":413,"tag":623,"props":4424,"children":4425},{"style":671},[4426],{"type":418,"value":4427},"\u003C",{"type":413,"tag":623,"props":4429,"children":4430},{"style":671},[4431],{"type":418,"value":4432},"string",{"type":413,"tag":623,"props":4434,"children":4435},{"style":671},[4436],{"type":418,"value":1037},{"type":413,"tag":623,"props":4438,"children":4439},{"style":671},[4440],{"type":418,"value":4441}," object?>\n",{"type":413,"tag":623,"props":4443,"children":4444},{"class":625,"line":1045},[4445],{"type":413,"tag":623,"props":4446,"children":4447},{"style":671},[4448],{"type":418,"value":1051},{"type":413,"tag":623,"props":4450,"children":4451},{"class":625,"line":1054},[4452,4457,4461,4466,4470,4474,4478,4482,4486,4490,4494,4499,4504,4509,4513,4517,4521,4525,4530,4534,4539,4543,4547,4551],{"type":413,"tag":623,"props":4453,"children":4454},{"style":671},[4455],{"type":418,"value":4456},"    [",{"type":413,"tag":623,"props":4458,"children":4459},{"style":671},[4460],{"type":418,"value":1023},{"type":413,"tag":623,"props":4462,"children":4463},{"style":635},[4464],{"type":418,"value":4465},"pipelineUrl",{"type":413,"tag":623,"props":4467,"children":4468},{"style":671},[4469],{"type":418,"value":1023},{"type":413,"tag":623,"props":4471,"children":4472},{"style":671},[4473],{"type":418,"value":1137},{"type":413,"tag":623,"props":4475,"children":4476},{"style":671},[4477],{"type":418,"value":1004},{"type":413,"tag":623,"props":4479,"children":4480},{"style":1058},[4481],{"type":418,"value":3003},{"type":413,"tag":623,"props":4483,"children":4484},{"style":671},[4485],{"type":418,"value":1404},{"type":413,"tag":623,"props":4487,"children":4488},{"style":1407},[4489],{"type":418,"value":3012},{"type":413,"tag":623,"props":4491,"children":4492},{"style":671},[4493],{"type":418,"value":1018},{"type":413,"tag":623,"props":4495,"children":4496},{"style":671},[4497],{"type":418,"value":4498},"$\"{",{"type":413,"tag":623,"props":4500,"children":4501},{"style":1058},[4502],{"type":418,"value":4503},"organizationUrl",{"type":413,"tag":623,"props":4505,"children":4506},{"style":671},[4507],{"type":418,"value":4508},"}{",{"type":413,"tag":623,"props":4510,"children":4511},{"style":1058},[4512],{"type":418,"value":3341},{"type":413,"tag":623,"props":4514,"children":4515},{"style":671},[4516],{"type":418,"value":1404},{"type":413,"tag":623,"props":4518,"children":4519},{"style":1058},[4520],{"type":418,"value":3350},{"type":413,"tag":623,"props":4522,"children":4523},{"style":671},[4524],{"type":418,"value":3327},{"type":413,"tag":623,"props":4526,"children":4527},{"style":635},[4528],{"type":418,"value":4529},"/_build?definitionId=",{"type":413,"tag":623,"props":4531,"children":4532},{"style":671},[4533],{"type":418,"value":2456},{"type":413,"tag":623,"props":4535,"children":4536},{"style":1058},[4537],{"type":418,"value":4538},"pipeline",{"type":413,"tag":623,"props":4540,"children":4541},{"style":671},[4542],{"type":418,"value":1404},{"type":413,"tag":623,"props":4544,"children":4545},{"style":1058},[4546],{"type":418,"value":1447},{"type":413,"tag":623,"props":4548,"children":4549},{"style":671},[4550],{"type":418,"value":2465},{"type":413,"tag":623,"props":4552,"children":4553},{"style":671},[4554],{"type":418,"value":3042},{"type":413,"tag":623,"props":4556,"children":4557},{"class":625,"line":1087},[4558],{"type":413,"tag":623,"props":4559,"children":4560},{"style":671},[4561],{"type":418,"value":4562},"};\n",{"type":413,"tag":414,"props":4564,"children":4565},{},[4566,4568,4574],{"type":418,"value":4567},"Now we can execute the ",{"type":413,"tag":619,"props":4569,"children":4571},{"className":4570},[],[4572],{"type":418,"value":4573},"pulumi up",{"type":418,"value":4575}," command to provision all these resources and then open the pipeline page in our browser to test the pipeline.",{"type":413,"tag":496,"props":4577,"children":4579},{"icon":4578},"i-heroicons-light-bulb",[4580],{"type":413,"tag":414,"props":4581,"children":4582},{},[4583,4585,4591,4593,4600,4602],{"type":418,"value":4584},"On Windows, you can use the ",{"type":413,"tag":619,"props":4586,"children":4588},{"className":4587},[],[4589],{"type":418,"value":4590},"start $(pulumi stack output pipelineUrl)",{"type":418,"value":4592}," command to directly open the browser on the pipeline page. If you are using ",{"type":413,"tag":432,"props":4594,"children":4597},{"href":4595,"rel":4596},"https://www.nushell.sh/",[436],[4598],{"type":418,"value":4599},"Nushell",{"type":418,"value":4601}," the command will be ",{"type":413,"tag":619,"props":4603,"children":4605},{"className":4604},[],[4606],{"type":418,"value":4607},"pulumi stack output pipelineUrl | start $in",{"type":413,"tag":414,"props":4609,"children":4610},{},[4611],{"type":413,"tag":487,"props":4612,"children":4616},{"alt":4613,"className":4614,"src":4615},"Results of the pipeline run in Azure DevOps",[491,492],"/posts/images/azuredevopsoidc_portal.webp",[],{"type":413,"tag":414,"props":4618,"children":4619},{},[4620],{"type":418,"value":4621},"Everything is working as expected.",{"type":413,"tag":420,"props":4623,"children":4625},{"id":4624},"to-conclude",[4626],{"type":418,"value":4627},"To conclude",{"type":413,"tag":414,"props":4629,"children":4630},{},[4631,4633,4639],{"type":418,"value":4632},"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":413,"tag":432,"props":4634,"children":4636},{"href":476,"rel":4635},[436],[4637],{"type":418,"value":4638},"what can be done for GitHub",{"type":418,"value":4640}," but with the specificities of Azure DevOps.",{"type":413,"tag":414,"props":4642,"children":4643},{},[4644],{"type":418,"value":4645},"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":413,"tag":414,"props":4647,"children":4648},{},[4649],{"type":418,"value":4650},"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":413,"tag":414,"props":4652,"children":4653},{},[4654],{"type":418,"value":4655},"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":413,"tag":414,"props":4657,"children":4658},{},[4659,4661,4671],{"type":418,"value":4660},"You can find the complete source code used for this article ",{"type":413,"tag":432,"props":4662,"children":4665},{"href":4663,"rel":4664},"https://github.com/TechWatching/AzureDevOpsWorkloadIdentity",[436],[4666],{"type":413,"tag":521,"props":4667,"children":4668},{},[4669],{"type":418,"value":4670},"in this GitHub repository",{"type":418,"value":1404},{"type":413,"tag":4673,"props":4674,"children":4675},"style",{},[4676],{"type":418,"value":4677},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":401,"searchDepth":1045,"depth":1045,"links":4679},[4680,4681,4683,4690],{"id":422,"depth":1045,"text":425},{"id":516,"depth":1045,"text":4682},"How can you use Workload Identity Federation to deploy to Azure from Azure Pipelines?",{"id":595,"depth":1045,"text":598,"children":4684},[4685,4686,4687,4688,4689],{"id":602,"depth":1054,"text":605},{"id":852,"depth":1054,"text":855},{"id":1798,"depth":1054,"text":1801},{"id":2534,"depth":1054,"text":2537},{"id":3407,"depth":1054,"text":3410},{"id":4624,"depth":1045,"text":4627},"markdown","content:1.posts:54.ado-workload-identity-federation.md","content","1.posts/54.ado-workload-identity-federation.md","md",{"_path":166,"_dir":399,"_draft":400,"_partial":400,"_locale":401,"title":165,"description":4697,"lead":4698,"date":4699,"image":4700,"badge":4702,"tags":4703,"body":4704,"_type":4691,"_id":12166,"_source":4693,"_file":12167,"_extension":4695},"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":4701},"/images/azureOIDC.webp",{"label":266},[228,233,307,374,312,315,376],{"type":410,"children":4705,"toc":12141},[4706,4710,4715,4727,4733,4743,4752,4758,4769,4803,4812,4818,4830,4868,4877,4895,4901,4906,4911,4919,4924,4932,4937,4958,4976,4997,5003,5012,5018,5023,5047,5052,5075,5080,5086,5092,5097,5153,5158,5208,5247,5251,5283,5296,5314,5320,5343,5368,5383,5388,5646,5668,5690,5722,5734,5739,5989,6005,6010,6300,6327,6332,6346,6355,6361,6366,6371,6671,6683,6785,6790,6879,6900,6963,6971,6977,6982,6987,7006,7011,7162,7167,7666,7683,7689,7694,7699,8043,8056,8675,8688,8713,8721,8750,8783,8876,8898,9098,9103,9492,9497,9529,9552,9558,9570,9579,9584,9593,9598,9637,9642,9647,9697,9709,9722,9739,9760,9769,9774,9778,9784,9789,9794,9800,9805,9823,9828,9834,9839,9925,9931,9936,9941,12121,12132,12137],{"type":413,"tag":414,"props":4707,"children":4708},{},[4709],{"type":418,"value":4697},{"type":413,"tag":414,"props":4711,"children":4712},{},[4713],{"type":418,"value":4714},"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":413,"tag":414,"props":4716,"children":4717},{},[4718,4720,4725],{"type":418,"value":4719},"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":413,"tag":706,"props":4721,"children":4722},{},[4723],{"type":418,"value":4724},"Azure-Ready GitHub repository",{"type":418,"value":4726},"\".",{"type":413,"tag":420,"props":4728,"children":4730},{"id":4729},"whats-an-azure-ready-github-repository",[4731],{"type":418,"value":4732},"What's an Azure-Ready GitHub repository?",{"type":413,"tag":414,"props":4734,"children":4735},{},[4736,4737,4741],{"type":418,"value":1023},{"type":413,"tag":706,"props":4738,"children":4739},{},[4740],{"type":418,"value":4724},{"type":418,"value":4742},"\" 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":413,"tag":414,"props":4744,"children":4745},{},[4746],{"type":413,"tag":487,"props":4747,"children":4751},{"alt":4748,"className":4749,"src":4750},"Diagram of a GitHub repository interacting with Azure.",[491,492],"/posts/images/azurereadygithub_overview_1.webp",[],{"type":413,"tag":600,"props":4753,"children":4755},{"id":4754},"the-github-part",[4756],{"type":418,"value":4757},"The GitHub part",{"type":413,"tag":414,"props":4759,"children":4760},{},[4761,4763,4767],{"type":418,"value":4762},"On the GitHub side, to have an ",{"type":413,"tag":706,"props":4764,"children":4765},{},[4766],{"type":418,"value":4724},{"type":418,"value":4768},", we need:",{"type":413,"tag":443,"props":4770,"children":4771},{},[4772,4785,4790],{"type":413,"tag":447,"props":4773,"children":4774},{},[4775,4777,4783],{"type":418,"value":4776},"the GitHub repository itself (already initialized with a ",{"type":413,"tag":619,"props":4778,"children":4780},{"className":4779},[],[4781],{"type":418,"value":4782},"main",{"type":418,"value":4784}," branch)",{"type":413,"tag":447,"props":4786,"children":4787},{},[4788],{"type":418,"value":4789},"the necessary GitHub Actions variables/secrets to authenticate to the correct Azure subscription",{"type":413,"tag":447,"props":4791,"children":4792},{},[4793,4795,4801],{"type":418,"value":4794},"a YAML file located in the ",{"type":413,"tag":619,"props":4796,"children":4798},{"className":4797},[],[4799],{"type":418,"value":4800},".github/workflows/",{"type":418,"value":4802}," folder that contains the CI/CD pipeline that provisions resources in Azure",{"type":413,"tag":414,"props":4804,"children":4805},{},[4806],{"type":413,"tag":487,"props":4807,"children":4811},{"alt":4808,"className":4809,"src":4810},"A diagram of the GitHub repository to create.",[491,492],"/posts/images/azurereadygithub_github_1.webp",[],{"type":413,"tag":600,"props":4813,"children":4815},{"id":4814},"the-azure-part",[4816],{"type":418,"value":4817},"The Azure part",{"type":413,"tag":414,"props":4819,"children":4820},{},[4821,4823,4828],{"type":418,"value":4822},"On the Azure side, to have an ",{"type":413,"tag":706,"props":4824,"children":4825},{},[4826],{"type":418,"value":4827},"Azure-Ready GitHub repository,",{"type":418,"value":4829}," we need:",{"type":413,"tag":443,"props":4831,"children":4832},{},[4833,4838],{"type":413,"tag":447,"props":4834,"children":4835},{},[4836],{"type":418,"value":4837},"the existing Azure subscription to which resources are deployed",{"type":413,"tag":447,"props":4839,"children":4840},{},[4841,4843,4848,4850],{"type":418,"value":4842},"an ",{"type":413,"tag":706,"props":4844,"children":4845},{},[4846],{"type":418,"value":4847},"identity",{"type":418,"value":4849}," 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":413,"tag":443,"props":4851,"children":4852},{},[4853,4858,4863],{"type":413,"tag":447,"props":4854,"children":4855},{},[4856],{"type":418,"value":4857},"an Azure AD application that represents the GitHub Actions pipeline identity",{"type":413,"tag":447,"props":4859,"children":4860},{},[4861],{"type":418,"value":4862},"a Service Principal (related to the Azure AD application) that has the contributor role on the Azure subscription",{"type":413,"tag":447,"props":4864,"children":4865},{},[4866],{"type":418,"value":4867},"credentials for the CI/CD pipeline to authenticate to Azure on behalf of this Azure AD application",{"type":413,"tag":414,"props":4869,"children":4870},{},[4871],{"type":413,"tag":487,"props":4872,"children":4876},{"alt":4873,"className":4874,"src":4875},"A diagram of the resources to configure in Azure.",[491,492],"/posts/images/azurereadygithub_azure_1.webp",[],{"type":413,"tag":496,"props":4878,"children":4879},{"icon":498},[4880],{"type":413,"tag":414,"props":4881,"children":4882},{},[4883,4887,4889,4893],{"type":413,"tag":706,"props":4884,"children":4885},{},[4886],{"type":418,"value":252},{"type":418,"value":4888}," has recently been renamed ",{"type":413,"tag":706,"props":4890,"children":4891},{},[4892],{"type":418,"value":382},{"type":418,"value":4894}," (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":413,"tag":600,"props":4896,"children":4898},{"id":4897},"the-problem-with-secret-credentials",[4899],{"type":418,"value":4900},"The problem with secret credentials",{"type":413,"tag":414,"props":4902,"children":4903},{},[4904],{"type":418,"value":4905},"People tend to use secret credentials to authenticate their pipeline to Azure and that's not the best thing to do.",{"type":413,"tag":414,"props":4907,"children":4908},{},[4909],{"type":418,"value":4910},"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":413,"tag":496,"props":4912,"children":4913},{"icon":952},[4914],{"type":413,"tag":414,"props":4915,"children":4916},{},[4917],{"type":418,"value":4918},"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":413,"tag":414,"props":4920,"children":4921},{},[4922],{"type":418,"value":4923},"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":413,"tag":496,"props":4925,"children":4926},{"icon":557},[4927],{"type":413,"tag":414,"props":4928,"children":4929},{},[4930],{"type":418,"value":4931},"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":413,"tag":414,"props":4933,"children":4934},{},[4935],{"type":418,"value":4936},"So what can we do about that?",{"type":413,"tag":414,"props":4938,"children":4939},{},[4940,4942,4947,4949,4956],{"type":418,"value":4941},"👉 We can stop using secret credentials and use ",{"type":413,"tag":432,"props":4943,"children":4945},{"href":467,"rel":4944},[436],[4946],{"type":418,"value":471},{"type":418,"value":4948}," instead. I suggest you have a look at this ",{"type":413,"tag":432,"props":4950,"children":4953},{"href":4951,"rel":4952},"https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect",[436],[4954],{"type":418,"value":4955},"GitHub documentation page",{"type":418,"value":4957}," as well to better understand how it works but basically, you can remember the following:",{"type":413,"tag":443,"props":4959,"children":4960},{},[4961,4966,4971],{"type":413,"tag":447,"props":4962,"children":4963},{},[4964],{"type":418,"value":4965},"this mechanism relies on Open ID Connect and trust between Azure and GitHub",{"type":413,"tag":447,"props":4967,"children":4968},{},[4969],{"type":418,"value":4970},"the GitHub pipeline does not need an Azure AD application secret anymore to authenticate to Azure",{"type":413,"tag":447,"props":4972,"children":4973},{},[4974],{"type":418,"value":4975},"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":413,"tag":414,"props":4977,"children":4978},{},[4979,4981,4986,4988,4995],{"type":418,"value":4980},"To establish the trust relationship between the Azure AD application and the GitHub repository, a ",{"type":413,"tag":706,"props":4982,"children":4983},{},[4984],{"type":418,"value":4985},"Federated Identity Credential",{"type":418,"value":4987}," must be created in the Azure Active Directory. You can find how to do that manually from the portal in the ",{"type":413,"tag":432,"props":4989,"children":4992},{"href":4990,"rel":4991},"https://learn.microsoft.com/en-us/azure/active-directory/workload-identities/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-azp",[436],[4993],{"type":418,"value":4994},"documentation",{"type":418,"value":4996}," but we are going to directly automate that 😉.",{"type":413,"tag":600,"props":4998,"children":5000},{"id":4999},"the-complete-solution-to-implement",[5001],{"type":418,"value":5002},"The complete solution to implement",{"type":413,"tag":414,"props":5004,"children":5005},{},[5006],{"type":413,"tag":487,"props":5007,"children":5011},{"alt":5008,"className":5009,"src":5010},"A diagram showing the interactions between Azure and GitHub.",[491,492],"/posts/images/azurereadygithub_overview_2.webp",[],{"type":413,"tag":420,"props":5013,"children":5015},{"id":5014},"why-use-pulumi-in-that-context",[5016],{"type":418,"value":5017},"Why use Pulumi in that context?",{"type":413,"tag":414,"props":5019,"children":5020},{},[5021],{"type":418,"value":5022},"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":413,"tag":496,"props":5024,"children":5025},{"icon":4578},[5026],{"type":413,"tag":414,"props":5027,"children":5028},{},[5029,5031,5037,5039,5045],{"type":418,"value":5030},"By the way, you should check ",{"type":413,"tag":432,"props":5032,"children":5035},{"href":5033,"rel":5034},"https://cli.github.com/",[436],[5036],{"type":418,"value":379},{"type":418,"value":5038}," if you have not done it yet, it's very handy. And if you have read my article about ",{"type":413,"tag":432,"props":5040,"children":5043},{"href":5041,"rel":5042},"https://www.techwatching.dev/posts/welcome-azure-cli",[436],[5044],{"type":418,"value":225},{"type":418,"value":5046},", you know it's a very convenient tool as well.",{"type":413,"tag":414,"props":5048,"children":5049},{},[5050],{"type":418,"value":5051},"I think Pulumi is a better choice here because:",{"type":413,"tag":443,"props":5053,"children":5054},{},[5055,5060,5065,5070],{"type":413,"tag":447,"props":5056,"children":5057},{},[5058],{"type":418,"value":5059},"a script is imperative by nature, but declarative infrastructure seems more suitable to avoid dealing with idempotency",{"type":413,"tag":447,"props":5061,"children":5062},{},[5063],{"type":418,"value":5064},"Pulumi can interact with both GitHub and Azure using its providers",{"type":413,"tag":447,"props":5066,"children":5067},{},[5068],{"type":418,"value":5069},"the code will be easier to write and maintain",{"type":413,"tag":447,"props":5071,"children":5072},{},[5073],{"type":418,"value":5074},"the code could be integrated into any application (including a future self-service infrastructure portal) using Pulumi Automation API",{"type":413,"tag":414,"props":5076,"children":5077},{},[5078],{"type":418,"value":5079},"In this article, the Pulumi code will be in TypeScript but it would work in any language supported by Pulumi.",{"type":413,"tag":420,"props":5081,"children":5083},{"id":5082},"automate-the-creation-of-the-azure-ready-github-repository",[5084],{"type":418,"value":5085},"Automate the creation of the Azure-Ready GitHub Repository",{"type":413,"tag":600,"props":5087,"children":5089},{"id":5088},"create-the-pulumi-project",[5090],{"type":418,"value":5091},"Create the Pulumi project",{"type":413,"tag":414,"props":5093,"children":5094},{},[5095],{"type":418,"value":5096},"Let's start by scaffolding a new Pulumi project using TypeScript:",{"type":413,"tag":612,"props":5098,"children":5101},{"className":5099,"code":5100,"language":248,"meta":401,"style":401},"language-powershell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pulumi new typescript -n AzureOIDC -s dev -d \"A program to set up an Azure-Ready GitHub repository\"\n",[5102],{"type":413,"tag":619,"props":5103,"children":5104},{"__ignoreMap":401},[5105],{"type":413,"tag":623,"props":5106,"children":5107},{"class":625,"line":626},[5108,5113,5117,5122,5126,5131,5135,5140,5144,5149],{"type":413,"tag":623,"props":5109,"children":5110},{"style":1058},[5111],{"type":418,"value":5112},"pulumi new typescript ",{"type":413,"tag":623,"props":5114,"children":5115},{"style":671},[5116],{"type":418,"value":3503},{"type":413,"tag":623,"props":5118,"children":5119},{"style":1058},[5120],{"type":418,"value":5121},"n AzureOIDC ",{"type":413,"tag":623,"props":5123,"children":5124},{"style":671},[5125],{"type":418,"value":3503},{"type":413,"tag":623,"props":5127,"children":5128},{"style":1058},[5129],{"type":418,"value":5130},"s dev ",{"type":413,"tag":623,"props":5132,"children":5133},{"style":671},[5134],{"type":418,"value":3503},{"type":413,"tag":623,"props":5136,"children":5137},{"style":1058},[5138],{"type":418,"value":5139},"d ",{"type":413,"tag":623,"props":5141,"children":5142},{"style":671},[5143],{"type":418,"value":1023},{"type":413,"tag":623,"props":5145,"children":5146},{"style":635},[5147],{"type":418,"value":5148},"A program to set up an Azure-Ready GitHub repository",{"type":413,"tag":623,"props":5150,"children":5151},{"style":671},[5152],{"type":418,"value":684},{"type":413,"tag":414,"props":5154,"children":5155},{},[5156],{"type":418,"value":5157},"This command creates a new pulumi project and stack from the TypeScript template:",{"type":413,"tag":443,"props":5159,"children":5160},{},[5161,5178,5193],{"type":413,"tag":447,"props":5162,"children":5163},{},[5164,5165,5170,5172,5177],{"type":418,"value":704},{"type":413,"tag":706,"props":5166,"children":5167},{},[5168],{"type":418,"value":5169},"AzureOIDC\"",{"type":418,"value":5171}," is specified using the ",{"type":413,"tag":619,"props":5173,"children":5175},{"className":5174},[],[5176],{"type":418,"value":718},{"type":418,"value":720},{"type":413,"tag":447,"props":5179,"children":5180},{},[5181,5182,5186,5187,5192],{"type":418,"value":725},{"type":413,"tag":706,"props":5183,"children":5184},{},[5185],{"type":418,"value":5148},{"type":418,"value":712},{"type":413,"tag":619,"props":5188,"children":5190},{"className":5189},[],[5191],{"type":418,"value":736},{"type":418,"value":720},{"type":413,"tag":447,"props":5194,"children":5195},{},[5196,5197,5201,5202,5207],{"type":418,"value":742},{"type":413,"tag":706,"props":5198,"children":5199},{},[5200],{"type":418,"value":747},{"type":418,"value":712},{"type":413,"tag":619,"props":5203,"children":5205},{"className":5204},[],[5206],{"type":418,"value":754},{"type":418,"value":720},{"type":413,"tag":496,"props":5209,"children":5210},{"icon":498},[5211],{"type":413,"tag":414,"props":5212,"children":5213},{},[5214,5216,5222,5224,5230,5232,5237,5239,5245],{"type":418,"value":5215},"By default, the ",{"type":413,"tag":619,"props":5217,"children":5219},{"className":5218},[],[5220],{"type":418,"value":5221},"pulumi new",{"type":418,"value":5223}," command installs the dependencies when creating the project. You can prevent this by specifying the ",{"type":413,"tag":619,"props":5225,"children":5227},{"className":5226},[],[5228],{"type":418,"value":5229},"-g",{"type":418,"value":5231}," option, which is useful when you want to use another package manager than the default one (",{"type":413,"tag":619,"props":5233,"children":5235},{"className":5234},[],[5236],{"type":418,"value":362},{"type":418,"value":5238}," instead of ",{"type":413,"tag":619,"props":5240,"children":5242},{"className":5241},[],[5243],{"type":418,"value":5244},"npm",{"type":418,"value":5246}," for instance).",{"type":413,"tag":414,"props":5248,"children":5249},{},[5250],{"type":418,"value":760},{"type":413,"tag":443,"props":5252,"children":5253},{},[5254,5263,5272],{"type":413,"tag":447,"props":5255,"children":5256},{},[5257,5258],{"type":418,"value":768},{"type":413,"tag":432,"props":5259,"children":5261},{"href":771,"rel":5260},[436],[5262],{"type":418,"value":775},{"type":413,"tag":447,"props":5264,"children":5265},{},[5266,5267],{"type":418,"value":768},{"type":413,"tag":432,"props":5268,"children":5270},{"href":782,"rel":5269},[436],[5271],{"type":418,"value":786},{"type":413,"tag":447,"props":5273,"children":5274},{},[5275,5276],{"type":418,"value":768},{"type":413,"tag":432,"props":5277,"children":5280},{"href":5278,"rel":5279},"https://www.pulumi.com/registry/packages/github/",[436],[5281],{"type":418,"value":5282},"GitHub provider",{"type":413,"tag":414,"props":5284,"children":5285},{},[5286,5288,5294],{"type":418,"value":5287},"So we can add the following packages to our ",{"type":413,"tag":619,"props":5289,"children":5291},{"className":5290},[],[5292],{"type":418,"value":5293},"package.json",{"type":418,"value":5295}," file:",{"type":413,"tag":443,"props":5297,"children":5298},{},[5299,5304,5309],{"type":413,"tag":447,"props":5300,"children":5301},{},[5302],{"type":418,"value":5303},"@pulumi/azure-native",{"type":413,"tag":447,"props":5305,"children":5306},{},[5307],{"type":418,"value":5308},"@pulumi/azuread",{"type":413,"tag":447,"props":5310,"children":5311},{},[5312],{"type":418,"value":5313},"@pulumi/github",{"type":413,"tag":600,"props":5315,"children":5317},{"id":5316},"create-the-repository-on-github",[5318],{"type":418,"value":5319},"Create the repository on GitHub",{"type":413,"tag":414,"props":5321,"children":5322},{},[5323,5325,5332,5334,5341],{"type":418,"value":5324},"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":413,"tag":432,"props":5326,"children":5329},{"href":5327,"rel":5328},"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#fine-grained-personal-access-tokens",[436],[5330],{"type":418,"value":5331},"fine-grained personal access token",{"type":418,"value":5333}," although a ",{"type":413,"tag":432,"props":5335,"children":5338},{"href":5336,"rel":5337},"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic",[436],[5339],{"type":418,"value":5340},"classic personal access token",{"type":418,"value":5342}," would also work). Next, we simply set the GitHub token in our Pulumi configuration, and the GitHub provider will automatically use it:",{"type":413,"tag":612,"props":5344,"children":5346},{"className":5099,"code":5345,"language":248,"meta":401,"style":401},"pulumi config set github:token XXXXXXXXXXXXXX --secret\n",[5347],{"type":413,"tag":619,"props":5348,"children":5349},{"__ignoreMap":401},[5350],{"type":413,"tag":623,"props":5351,"children":5352},{"class":625,"line":626},[5353,5358,5363],{"type":413,"tag":623,"props":5354,"children":5355},{"style":1058},[5356],{"type":418,"value":5357},"pulumi config set github:token XXXXXXXXXXXXXX ",{"type":413,"tag":623,"props":5359,"children":5360},{"style":671},[5361],{"type":418,"value":5362},"--",{"type":413,"tag":623,"props":5364,"children":5365},{"style":1058},[5366],{"type":418,"value":5367},"secret\n",{"type":413,"tag":496,"props":5369,"children":5370},{"icon":952},[5371],{"type":413,"tag":414,"props":5372,"children":5373},{},[5374,5376,5381],{"type":418,"value":5375},"Don't forget to include the ",{"type":413,"tag":619,"props":5377,"children":5379},{"className":5378},[],[5380],{"type":418,"value":964},{"type":418,"value":5382}," 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":413,"tag":414,"props":5384,"children":5385},{},[5386],{"type":418,"value":5387},"Now, it's time to create our GitHub repository!",{"type":413,"tag":612,"props":5389,"children":5392},{"className":5390,"code":5391,"language":357,"meta":401,"style":401},"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",[5393],{"type":413,"tag":619,"props":5394,"children":5395},{"__ignoreMap":401},[5396,5440,5447,5508,5537,5566,5584,5600,5607],{"type":413,"tag":623,"props":5397,"children":5398},{"class":625,"line":626},[5399,5404,5409,5414,5419,5424,5428,5432,5436],{"type":413,"tag":623,"props":5400,"children":5401},{"style":2485},[5402],{"type":418,"value":5403},"import",{"type":413,"tag":623,"props":5405,"children":5406},{"style":671},[5407],{"type":418,"value":5408}," *",{"type":413,"tag":623,"props":5410,"children":5411},{"style":2485},[5412],{"type":418,"value":5413}," as",{"type":413,"tag":623,"props":5415,"children":5416},{"style":1058},[5417],{"type":418,"value":5418}," github ",{"type":413,"tag":623,"props":5420,"children":5421},{"style":2485},[5422],{"type":418,"value":5423},"from",{"type":413,"tag":623,"props":5425,"children":5426},{"style":671},[5427],{"type":418,"value":674},{"type":413,"tag":623,"props":5429,"children":5430},{"style":635},[5431],{"type":418,"value":5313},{"type":413,"tag":623,"props":5433,"children":5434},{"style":671},[5435],{"type":418,"value":1023},{"type":413,"tag":623,"props":5437,"children":5438},{"style":671},[5439],{"type":418,"value":2524},{"type":413,"tag":623,"props":5441,"children":5442},{"class":625,"line":1045},[5443],{"type":413,"tag":623,"props":5444,"children":5445},{"emptyLinePlaceholder":2790},[5446],{"type":418,"value":2793},{"type":413,"tag":623,"props":5448,"children":5449},{"class":625,"line":1054},[5450,5455,5460,5464,5468,5473,5477,5482,5486,5490,5495,5499,5503],{"type":413,"tag":623,"props":5451,"children":5452},{"style":2854},[5453],{"type":418,"value":5454},"const",{"type":413,"tag":623,"props":5456,"children":5457},{"style":1058},[5458],{"type":418,"value":5459}," repository ",{"type":413,"tag":623,"props":5461,"children":5462},{"style":671},[5463],{"type":418,"value":1066},{"type":413,"tag":623,"props":5465,"children":5466},{"style":671},[5467],{"type":418,"value":638},{"type":413,"tag":623,"props":5469,"children":5470},{"style":1058},[5471],{"type":418,"value":5472}," github",{"type":413,"tag":623,"props":5474,"children":5475},{"style":671},[5476],{"type":418,"value":1404},{"type":413,"tag":623,"props":5478,"children":5479},{"style":1407},[5480],{"type":418,"value":5481},"Repository",{"type":413,"tag":623,"props":5483,"children":5484},{"style":1058},[5485],{"type":418,"value":1018},{"type":413,"tag":623,"props":5487,"children":5488},{"style":671},[5489],{"type":418,"value":1023},{"type":413,"tag":623,"props":5491,"children":5492},{"style":635},[5493],{"type":418,"value":5494},"azure-ready-repository",{"type":413,"tag":623,"props":5496,"children":5497},{"style":671},[5498],{"type":418,"value":1023},{"type":413,"tag":623,"props":5500,"children":5501},{"style":671},[5502],{"type":418,"value":1037},{"type":413,"tag":623,"props":5504,"children":5505},{"style":671},[5506],{"type":418,"value":5507}," {\n",{"type":413,"tag":623,"props":5509,"children":5510},{"class":625,"line":1087},[5511,5517,5521,5525,5529,5533],{"type":413,"tag":623,"props":5512,"children":5514},{"style":5513},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[5515],{"type":418,"value":5516},"  name",{"type":413,"tag":623,"props":5518,"children":5519},{"style":671},[5520],{"type":418,"value":3493},{"type":413,"tag":623,"props":5522,"children":5523},{"style":671},[5524],{"type":418,"value":674},{"type":413,"tag":623,"props":5526,"children":5527},{"style":635},[5528],{"type":418,"value":5494},{"type":413,"tag":623,"props":5530,"children":5531},{"style":671},[5532],{"type":418,"value":1023},{"type":413,"tag":623,"props":5534,"children":5535},{"style":671},[5536],{"type":418,"value":1084},{"type":413,"tag":623,"props":5538,"children":5539},{"class":625,"line":1104},[5540,5545,5549,5553,5558,5562],{"type":413,"tag":623,"props":5541,"children":5542},{"style":5513},[5543],{"type":418,"value":5544},"  visibility",{"type":413,"tag":623,"props":5546,"children":5547},{"style":671},[5548],{"type":418,"value":3493},{"type":413,"tag":623,"props":5550,"children":5551},{"style":671},[5552],{"type":418,"value":674},{"type":413,"tag":623,"props":5554,"children":5555},{"style":635},[5556],{"type":418,"value":5557},"public",{"type":413,"tag":623,"props":5559,"children":5560},{"style":671},[5561],{"type":418,"value":1023},{"type":413,"tag":623,"props":5563,"children":5564},{"style":671},[5565],{"type":418,"value":1084},{"type":413,"tag":623,"props":5567,"children":5568},{"class":625,"line":1113},[5569,5574,5578],{"type":413,"tag":623,"props":5570,"children":5571},{"style":5513},[5572],{"type":418,"value":5573},"  autoInit",{"type":413,"tag":623,"props":5575,"children":5576},{"style":671},[5577],{"type":418,"value":3493},{"type":413,"tag":623,"props":5579,"children":5581},{"style":5580},"--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC",[5582],{"type":418,"value":5583}," true\n",{"type":413,"tag":623,"props":5585,"children":5586},{"class":625,"line":1161},[5587,5591,5596],{"type":413,"tag":623,"props":5588,"children":5589},{"style":671},[5590],{"type":418,"value":3327},{"type":413,"tag":623,"props":5592,"children":5593},{"style":1058},[5594],{"type":418,"value":5595},")",{"type":413,"tag":623,"props":5597,"children":5598},{"style":671},[5599],{"type":418,"value":2524},{"type":413,"tag":623,"props":5601,"children":5602},{"class":625,"line":1207},[5603],{"type":413,"tag":623,"props":5604,"children":5605},{"emptyLinePlaceholder":2790},[5606],{"type":418,"value":2793},{"type":413,"tag":623,"props":5608,"children":5609},{"class":625,"line":1251},[5610,5615,5620,5625,5629,5633,5637,5642],{"type":413,"tag":623,"props":5611,"children":5612},{"style":2485},[5613],{"type":418,"value":5614},"export",{"type":413,"tag":623,"props":5616,"children":5617},{"style":2854},[5618],{"type":418,"value":5619}," const",{"type":413,"tag":623,"props":5621,"children":5622},{"style":1058},[5623],{"type":418,"value":5624}," repositoryCloneUrl ",{"type":413,"tag":623,"props":5626,"children":5627},{"style":671},[5628],{"type":418,"value":1066},{"type":413,"tag":623,"props":5630,"children":5631},{"style":1058},[5632],{"type":418,"value":1390},{"type":413,"tag":623,"props":5634,"children":5635},{"style":671},[5636],{"type":418,"value":1404},{"type":413,"tag":623,"props":5638,"children":5639},{"style":1058},[5640],{"type":418,"value":5641},"httpCloneUrl",{"type":413,"tag":623,"props":5643,"children":5644},{"style":671},[5645],{"type":418,"value":2524},{"type":413,"tag":414,"props":5647,"children":5648},{},[5649,5651,5658,5660,5666],{"type":418,"value":5650},"Pulumi has an ",{"type":413,"tag":432,"props":5652,"children":5655},{"href":5653,"rel":5654},"https://www.pulumi.com/docs/concepts/resources/names/#autonaming",[436],[5656],{"type":418,"value":5657},"auto-naming capability",{"type":418,"value":5659}," 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":413,"tag":619,"props":5661,"children":5663},{"className":5662},[],[5664],{"type":418,"value":5665},"name",{"type":418,"value":5667}," property to override the auto-naming behavior.",{"type":413,"tag":414,"props":5669,"children":5670},{},[5671,5673,5680,5682,5688],{"type":418,"value":5672},"The last line creates a stack ",{"type":413,"tag":432,"props":5674,"children":5677},{"href":5675,"rel":5676},"https://www.pulumi.com/docs/concepts/stack/#outputs",[436],[5678],{"type":418,"value":5679},"output",{"type":418,"value":5681}," named ",{"type":413,"tag":619,"props":5683,"children":5685},{"className":5684},[],[5686],{"type":418,"value":5687},"repositoryCloneUrl",{"type":418,"value":5689}," so that we can easily get the URL to clone our newly created repository.",{"type":413,"tag":496,"props":5691,"children":5692},{"icon":498},[5693],{"type":413,"tag":414,"props":5694,"children":5695},{},[5696,5698,5704,5706,5712,5714,5720],{"type":418,"value":5697},"I wanted the repository to be initialized, that's why I set the ",{"type":413,"tag":619,"props":5699,"children":5701},{"className":5700},[],[5702],{"type":418,"value":5703},"autoInit",{"type":418,"value":5705}," property to ",{"type":413,"tag":619,"props":5707,"children":5709},{"className":5708},[],[5710],{"type":418,"value":5711},"true",{"type":418,"value":5713}," but you should set it to ",{"type":413,"tag":619,"props":5715,"children":5717},{"className":5716},[],[5718],{"type":418,"value":5719},"false",{"type":418,"value":5721}," if you have an existing local git repository that you want to push on this GitHub repository.",{"type":413,"tag":600,"props":5723,"children":5725},{"id":5724},"create-the-identity-in-azure-active-directory-for-the-github-actions-workflow",[5726,5728,5732],{"type":418,"value":5727},"Create the ",{"type":413,"tag":706,"props":5729,"children":5730},{},[5731],{"type":418,"value":4847},{"type":418,"value":5733}," in Azure Active Directory for the GitHub Actions workflow",{"type":413,"tag":414,"props":5735,"children":5736},{},[5737],{"type":418,"value":5738},"Creating an Azure AD application and its service principal is not very complicated:",{"type":413,"tag":612,"props":5740,"children":5742},{"className":5390,"code":5741,"language":357,"meta":401,"style":401},"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",[5743],{"type":413,"tag":619,"props":5744,"children":5745},{"__ignoreMap":401},[5746,5786,5793,5888,5945,5974],{"type":413,"tag":623,"props":5747,"children":5748},{"class":625,"line":626},[5749,5753,5757,5761,5766,5770,5774,5778,5782],{"type":413,"tag":623,"props":5750,"children":5751},{"style":2485},[5752],{"type":418,"value":5403},{"type":413,"tag":623,"props":5754,"children":5755},{"style":671},[5756],{"type":418,"value":5408},{"type":413,"tag":623,"props":5758,"children":5759},{"style":2485},[5760],{"type":418,"value":5413},{"type":413,"tag":623,"props":5762,"children":5763},{"style":1058},[5764],{"type":418,"value":5765}," azuread ",{"type":413,"tag":623,"props":5767,"children":5768},{"style":2485},[5769],{"type":418,"value":5423},{"type":413,"tag":623,"props":5771,"children":5772},{"style":671},[5773],{"type":418,"value":674},{"type":413,"tag":623,"props":5775,"children":5776},{"style":635},[5777],{"type":418,"value":5308},{"type":413,"tag":623,"props":5779,"children":5780},{"style":671},[5781],{"type":418,"value":1023},{"type":413,"tag":623,"props":5783,"children":5784},{"style":671},[5785],{"type":418,"value":2524},{"type":413,"tag":623,"props":5787,"children":5788},{"class":625,"line":1045},[5789],{"type":413,"tag":623,"props":5790,"children":5791},{"emptyLinePlaceholder":2790},[5792],{"type":418,"value":2793},{"type":413,"tag":623,"props":5794,"children":5795},{"class":625,"line":1054},[5796,5800,5805,5809,5813,5818,5822,5827,5831,5835,5840,5844,5848,5853,5858,5862,5866,5871,5875,5880,5884],{"type":413,"tag":623,"props":5797,"children":5798},{"style":2854},[5799],{"type":418,"value":5454},{"type":413,"tag":623,"props":5801,"children":5802},{"style":1058},[5803],{"type":418,"value":5804}," aadApplication ",{"type":413,"tag":623,"props":5806,"children":5807},{"style":671},[5808],{"type":418,"value":1066},{"type":413,"tag":623,"props":5810,"children":5811},{"style":671},[5812],{"type":418,"value":638},{"type":413,"tag":623,"props":5814,"children":5815},{"style":1058},[5816],{"type":418,"value":5817}," azuread",{"type":413,"tag":623,"props":5819,"children":5820},{"style":671},[5821],{"type":418,"value":1404},{"type":413,"tag":623,"props":5823,"children":5824},{"style":1407},[5825],{"type":418,"value":5826},"Application",{"type":413,"tag":623,"props":5828,"children":5829},{"style":1058},[5830],{"type":418,"value":1018},{"type":413,"tag":623,"props":5832,"children":5833},{"style":671},[5834],{"type":418,"value":1023},{"type":413,"tag":623,"props":5836,"children":5837},{"style":635},[5838],{"type":418,"value":5839},"AzureReadyApp",{"type":413,"tag":623,"props":5841,"children":5842},{"style":671},[5843],{"type":418,"value":1023},{"type":413,"tag":623,"props":5845,"children":5846},{"style":671},[5847],{"type":418,"value":1037},{"type":413,"tag":623,"props":5849,"children":5850},{"style":671},[5851],{"type":418,"value":5852}," {",{"type":413,"tag":623,"props":5854,"children":5855},{"style":5513},[5856],{"type":418,"value":5857}," displayName",{"type":413,"tag":623,"props":5859,"children":5860},{"style":671},[5861],{"type":418,"value":3493},{"type":413,"tag":623,"props":5863,"children":5864},{"style":671},[5865],{"type":418,"value":674},{"type":413,"tag":623,"props":5867,"children":5868},{"style":635},[5869],{"type":418,"value":5870},"Azure Ready App",{"type":413,"tag":623,"props":5872,"children":5873},{"style":671},[5874],{"type":418,"value":1023},{"type":413,"tag":623,"props":5876,"children":5877},{"style":671},[5878],{"type":418,"value":5879}," }",{"type":413,"tag":623,"props":5881,"children":5882},{"style":1058},[5883],{"type":418,"value":5595},{"type":413,"tag":623,"props":5885,"children":5886},{"style":671},[5887],{"type":418,"value":2524},{"type":413,"tag":623,"props":5889,"children":5890},{"class":625,"line":1087},[5891,5895,5900,5904,5908,5912,5916,5920,5924,5928,5933,5937,5941],{"type":413,"tag":623,"props":5892,"children":5893},{"style":2854},[5894],{"type":418,"value":5454},{"type":413,"tag":623,"props":5896,"children":5897},{"style":1058},[5898],{"type":418,"value":5899}," servicePrincipal ",{"type":413,"tag":623,"props":5901,"children":5902},{"style":671},[5903],{"type":418,"value":1066},{"type":413,"tag":623,"props":5905,"children":5906},{"style":671},[5907],{"type":418,"value":638},{"type":413,"tag":623,"props":5909,"children":5910},{"style":1058},[5911],{"type":418,"value":5817},{"type":413,"tag":623,"props":5913,"children":5914},{"style":671},[5915],{"type":418,"value":1404},{"type":413,"tag":623,"props":5917,"children":5918},{"style":1407},[5919],{"type":418,"value":2950},{"type":413,"tag":623,"props":5921,"children":5922},{"style":1058},[5923],{"type":418,"value":1018},{"type":413,"tag":623,"props":5925,"children":5926},{"style":671},[5927],{"type":418,"value":1023},{"type":413,"tag":623,"props":5929,"children":5930},{"style":635},[5931],{"type":418,"value":5932},"AzureReadServicePrincipal",{"type":413,"tag":623,"props":5934,"children":5935},{"style":671},[5936],{"type":418,"value":1023},{"type":413,"tag":623,"props":5938,"children":5939},{"style":671},[5940],{"type":418,"value":1037},{"type":413,"tag":623,"props":5942,"children":5943},{"style":671},[5944],{"type":418,"value":5507},{"type":413,"tag":623,"props":5946,"children":5947},{"class":625,"line":1104},[5948,5953,5957,5961,5965,5970],{"type":413,"tag":623,"props":5949,"children":5950},{"style":5513},[5951],{"type":418,"value":5952},"  applicationId",{"type":413,"tag":623,"props":5954,"children":5955},{"style":671},[5956],{"type":418,"value":3493},{"type":413,"tag":623,"props":5958,"children":5959},{"style":1058},[5960],{"type":418,"value":2615},{"type":413,"tag":623,"props":5962,"children":5963},{"style":671},[5964],{"type":418,"value":1404},{"type":413,"tag":623,"props":5966,"children":5967},{"style":1058},[5968],{"type":418,"value":5969},"applicationId",{"type":413,"tag":623,"props":5971,"children":5972},{"style":671},[5973],{"type":418,"value":1084},{"type":413,"tag":623,"props":5975,"children":5976},{"class":625,"line":1113},[5977,5981,5985],{"type":413,"tag":623,"props":5978,"children":5979},{"style":671},[5980],{"type":418,"value":3327},{"type":413,"tag":623,"props":5982,"children":5983},{"style":1058},[5984],{"type":418,"value":5595},{"type":413,"tag":623,"props":5986,"children":5987},{"style":671},[5988],{"type":418,"value":2524},{"type":413,"tag":414,"props":5990,"children":5991},{},[5992,5994,6003],{"type":418,"value":5993},"The OIDC trust thing is a bit more complex. Fortunately, Microsoft's documentation has a detailed page ",{"type":413,"tag":432,"props":5995,"children":5997},{"href":4990,"rel":5996},[436],[5998],{"type":413,"tag":706,"props":5999,"children":6000},{},[6001],{"type":418,"value":6002},"Configuring an app to trust an external identity provider",{"type":418,"value":6004}," that explains everything and shows how to add a federated identity for GitHub Actions using the Azure Portal, Azure CLI, or Azure PowerShell.",{"type":413,"tag":414,"props":6006,"children":6007},{},[6008],{"type":418,"value":6009},"Let's do the same thing using TypeScript and Pulumi Azure AD provider:",{"type":413,"tag":612,"props":6011,"children":6013},{"className":5390,"code":6012,"language":357,"meta":401,"style":401},"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",[6014],{"type":413,"tag":619,"props":6015,"children":6016},{"__ignoreMap":401},[6017,6062,6091,6119,6147,6184,6213,6285],{"type":413,"tag":623,"props":6018,"children":6019},{"class":625,"line":626},[6020,6024,6028,6032,6037,6041,6045,6050,6054,6058],{"type":413,"tag":623,"props":6021,"children":6022},{"style":671},[6023],{"type":418,"value":2857},{"type":413,"tag":623,"props":6025,"children":6026},{"style":1058},[6027],{"type":418,"value":5817},{"type":413,"tag":623,"props":6029,"children":6030},{"style":671},[6031],{"type":418,"value":1404},{"type":413,"tag":623,"props":6033,"children":6034},{"style":1407},[6035],{"type":418,"value":6036},"ApplicationFederatedIdentityCredential",{"type":413,"tag":623,"props":6038,"children":6039},{"style":1058},[6040],{"type":418,"value":1018},{"type":413,"tag":623,"props":6042,"children":6043},{"style":671},[6044],{"type":418,"value":1023},{"type":413,"tag":623,"props":6046,"children":6047},{"style":635},[6048],{"type":418,"value":6049},"AzureReadyAppFederatedIdentityCredential",{"type":413,"tag":623,"props":6051,"children":6052},{"style":671},[6053],{"type":418,"value":1023},{"type":413,"tag":623,"props":6055,"children":6056},{"style":671},[6057],{"type":418,"value":1037},{"type":413,"tag":623,"props":6059,"children":6060},{"style":671},[6061],{"type":418,"value":5507},{"type":413,"tag":623,"props":6063,"children":6064},{"class":625,"line":1045},[6065,6070,6074,6078,6082,6087],{"type":413,"tag":623,"props":6066,"children":6067},{"style":5513},[6068],{"type":418,"value":6069},"  applicationObjectId",{"type":413,"tag":623,"props":6071,"children":6072},{"style":671},[6073],{"type":418,"value":3493},{"type":413,"tag":623,"props":6075,"children":6076},{"style":1058},[6077],{"type":418,"value":2615},{"type":413,"tag":623,"props":6079,"children":6080},{"style":671},[6081],{"type":418,"value":1404},{"type":413,"tag":623,"props":6083,"children":6084},{"style":1058},[6085],{"type":418,"value":6086},"objectId",{"type":413,"tag":623,"props":6088,"children":6089},{"style":671},[6090],{"type":418,"value":1084},{"type":413,"tag":623,"props":6092,"children":6093},{"class":625,"line":1054},[6094,6099,6103,6107,6111,6115],{"type":413,"tag":623,"props":6095,"children":6096},{"style":5513},[6097],{"type":418,"value":6098},"  displayName",{"type":413,"tag":623,"props":6100,"children":6101},{"style":671},[6102],{"type":418,"value":3493},{"type":413,"tag":623,"props":6104,"children":6105},{"style":671},[6106],{"type":418,"value":674},{"type":413,"tag":623,"props":6108,"children":6109},{"style":635},[6110],{"type":418,"value":3176},{"type":413,"tag":623,"props":6112,"children":6113},{"style":671},[6114],{"type":418,"value":1023},{"type":413,"tag":623,"props":6116,"children":6117},{"style":671},[6118],{"type":418,"value":1084},{"type":413,"tag":623,"props":6120,"children":6121},{"class":625,"line":1087},[6122,6127,6131,6135,6139,6143],{"type":413,"tag":623,"props":6123,"children":6124},{"style":5513},[6125],{"type":418,"value":6126},"  description",{"type":413,"tag":623,"props":6128,"children":6129},{"style":671},[6130],{"type":418,"value":3493},{"type":413,"tag":623,"props":6132,"children":6133},{"style":671},[6134],{"type":418,"value":674},{"type":413,"tag":623,"props":6136,"children":6137},{"style":635},[6138],{"type":418,"value":3204},{"type":413,"tag":623,"props":6140,"children":6141},{"style":671},[6142],{"type":418,"value":1023},{"type":413,"tag":623,"props":6144,"children":6145},{"style":671},[6146],{"type":418,"value":1084},{"type":413,"tag":623,"props":6148,"children":6149},{"class":625,"line":1104},[6150,6155,6159,6164,6168,6172,6176,6180],{"type":413,"tag":623,"props":6151,"children":6152},{"style":5513},[6153],{"type":418,"value":6154},"  audiences",{"type":413,"tag":623,"props":6156,"children":6157},{"style":671},[6158],{"type":418,"value":3493},{"type":413,"tag":623,"props":6160,"children":6161},{"style":1058},[6162],{"type":418,"value":6163}," [",{"type":413,"tag":623,"props":6165,"children":6166},{"style":671},[6167],{"type":418,"value":1023},{"type":413,"tag":623,"props":6169,"children":6170},{"style":635},[6171],{"type":418,"value":3238},{"type":413,"tag":623,"props":6173,"children":6174},{"style":671},[6175],{"type":418,"value":1023},{"type":413,"tag":623,"props":6177,"children":6178},{"style":1058},[6179],{"type":418,"value":1137},{"type":413,"tag":623,"props":6181,"children":6182},{"style":671},[6183],{"type":418,"value":1084},{"type":413,"tag":623,"props":6185,"children":6186},{"class":625,"line":1113},[6187,6192,6196,6200,6205,6209],{"type":413,"tag":623,"props":6188,"children":6189},{"style":5513},[6190],{"type":418,"value":6191},"  issuer",{"type":413,"tag":623,"props":6193,"children":6194},{"style":671},[6195],{"type":418,"value":3493},{"type":413,"tag":623,"props":6197,"children":6198},{"style":671},[6199],{"type":418,"value":674},{"type":413,"tag":623,"props":6201,"children":6202},{"style":635},[6203],{"type":418,"value":6204},"https://token.actions.githubusercontent.com",{"type":413,"tag":623,"props":6206,"children":6207},{"style":671},[6208],{"type":418,"value":1023},{"type":413,"tag":623,"props":6210,"children":6211},{"style":671},[6212],{"type":418,"value":1084},{"type":413,"tag":623,"props":6214,"children":6215},{"class":625,"line":1161},[6216,6221,6225,6230,6234,6239,6244,6249,6254,6259,6263,6268,6272,6277,6281],{"type":413,"tag":623,"props":6217,"children":6218},{"style":5513},[6219],{"type":418,"value":6220},"  subject",{"type":413,"tag":623,"props":6222,"children":6223},{"style":671},[6224],{"type":418,"value":3493},{"type":413,"tag":623,"props":6226,"children":6227},{"style":1058},[6228],{"type":418,"value":6229}," pulumi",{"type":413,"tag":623,"props":6231,"children":6232},{"style":671},[6233],{"type":418,"value":1404},{"type":413,"tag":623,"props":6235,"children":6236},{"style":1407},[6237],{"type":418,"value":6238},"interpolate",{"type":413,"tag":623,"props":6240,"children":6241},{"style":671},[6242],{"type":418,"value":6243},"`",{"type":413,"tag":623,"props":6245,"children":6246},{"style":635},[6247],{"type":418,"value":6248},"repo:",{"type":413,"tag":623,"props":6250,"children":6251},{"style":671},[6252],{"type":418,"value":6253},"${",{"type":413,"tag":623,"props":6255,"children":6256},{"style":1058},[6257],{"type":418,"value":6258},"repository",{"type":413,"tag":623,"props":6260,"children":6261},{"style":671},[6262],{"type":418,"value":1404},{"type":413,"tag":623,"props":6264,"children":6265},{"style":1058},[6266],{"type":418,"value":6267},"fullName",{"type":413,"tag":623,"props":6269,"children":6270},{"style":671},[6271],{"type":418,"value":3327},{"type":413,"tag":623,"props":6273,"children":6274},{"style":635},[6275],{"type":418,"value":6276},":ref:refs/heads/main",{"type":413,"tag":623,"props":6278,"children":6279},{"style":671},[6280],{"type":418,"value":6243},{"type":413,"tag":623,"props":6282,"children":6283},{"style":671},[6284],{"type":418,"value":1084},{"type":413,"tag":623,"props":6286,"children":6287},{"class":625,"line":1207},[6288,6292,6296],{"type":413,"tag":623,"props":6289,"children":6290},{"style":671},[6291],{"type":418,"value":3327},{"type":413,"tag":623,"props":6293,"children":6294},{"style":1058},[6295],{"type":418,"value":5595},{"type":413,"tag":623,"props":6297,"children":6298},{"style":671},[6299],{"type":418,"value":2524},{"type":413,"tag":414,"props":6301,"children":6302},{},[6303,6305,6311,6313,6318,6320,6325],{"type":418,"value":6304},"The ",{"type":413,"tag":619,"props":6306,"children":6308},{"className":6307},[],[6309],{"type":418,"value":6310},"subject",{"type":418,"value":6312}," 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":413,"tag":619,"props":6314,"children":6316},{"className":6315},[],[6317],{"type":418,"value":6310},{"type":418,"value":6319},". You can also specify that only workflows triggered by a pull request should be authorized. Here, I have used the ",{"type":413,"tag":619,"props":6321,"children":6323},{"className":6322},[],[6324],{"type":418,"value":4782},{"type":418,"value":6326}," branch but I could create multiple Federated Identity Credentials with different subjects if needed.",{"type":413,"tag":414,"props":6328,"children":6329},{},[6330],{"type":418,"value":6331},"With this configuration, the GitHub Actions workflow we create next will be able to obtain a valid Azure access token.",{"type":413,"tag":414,"props":6333,"children":6334},{},[6335,6337,6344],{"type":418,"value":6336},"If you are interested in gaining a better understanding of how all this works, you can refer to ",{"type":413,"tag":432,"props":6338,"children":6341},{"href":6339,"rel":6340},"https://learn.microsoft.com/en-us/azure/active-directory/workload-identities/workload-identity-federation#how-it-works",[436],[6342],{"type":418,"value":6343},"this diagram",{"type":418,"value":6345}," from Microsoft's documentation (with GitHub serving as the external identity provider in our case).",{"type":413,"tag":414,"props":6347,"children":6348},{},[6349],{"type":413,"tag":487,"props":6350,"children":6354},{"alt":6351,"className":6352,"src":6353},"Sequence diagram explaining Azure OIDC.",[491,492],"/posts/images/azurereadygithub_identityfederation.webp",[],{"type":413,"tag":600,"props":6356,"children":6358},{"id":6357},"authorize-the-service-principal-to-provision-resources-on-the-subscription",[6359],{"type":418,"value":6360},"Authorize the Service Principal to provision resources on the subscription",{"type":413,"tag":414,"props":6362,"children":6363},{},[6364],{"type":418,"value":6365},"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":413,"tag":414,"props":6367,"children":6368},{},[6369],{"type":418,"value":6370},"We can do that by giving the Contributor role to our service principal.",{"type":413,"tag":612,"props":6372,"children":6374},{"className":5390,"code":6373,"language":357,"meta":401,"style":401},"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",[6375],{"type":413,"tag":619,"props":6376,"children":6377},{"__ignoreMap":401},[6378,6419,6461,6468,6513,6542,6579,6607,6656],{"type":413,"tag":623,"props":6379,"children":6380},{"class":625,"line":626},[6381,6385,6389,6393,6398,6402,6406,6411,6415],{"type":413,"tag":623,"props":6382,"children":6383},{"style":2485},[6384],{"type":418,"value":5403},{"type":413,"tag":623,"props":6386,"children":6387},{"style":671},[6388],{"type":418,"value":5408},{"type":413,"tag":623,"props":6390,"children":6391},{"style":2485},[6392],{"type":418,"value":5413},{"type":413,"tag":623,"props":6394,"children":6395},{"style":1058},[6396],{"type":418,"value":6397}," authorization ",{"type":413,"tag":623,"props":6399,"children":6400},{"style":2485},[6401],{"type":418,"value":5423},{"type":413,"tag":623,"props":6403,"children":6404},{"style":671},[6405],{"type":418,"value":674},{"type":413,"tag":623,"props":6407,"children":6408},{"style":635},[6409],{"type":418,"value":6410},"@pulumi/azure-native/authorization",{"type":413,"tag":623,"props":6412,"children":6413},{"style":671},[6414],{"type":418,"value":1023},{"type":413,"tag":623,"props":6416,"children":6417},{"style":671},[6418],{"type":418,"value":2524},{"type":413,"tag":623,"props":6420,"children":6421},{"class":625,"line":1045},[6422,6426,6430,6435,6439,6444,6448,6453,6457],{"type":413,"tag":623,"props":6423,"children":6424},{"style":2485},[6425],{"type":418,"value":5403},{"type":413,"tag":623,"props":6427,"children":6428},{"style":671},[6429],{"type":418,"value":5852},{"type":413,"tag":623,"props":6431,"children":6432},{"style":1058},[6433],{"type":418,"value":6434}," azureBuiltInRoles",{"type":413,"tag":623,"props":6436,"children":6437},{"style":671},[6438],{"type":418,"value":5879},{"type":413,"tag":623,"props":6440,"children":6441},{"style":2485},[6442],{"type":418,"value":6443}," from",{"type":413,"tag":623,"props":6445,"children":6446},{"style":671},[6447],{"type":418,"value":674},{"type":413,"tag":623,"props":6449,"children":6450},{"style":635},[6451],{"type":418,"value":6452},"./builtInRoles",{"type":413,"tag":623,"props":6454,"children":6455},{"style":671},[6456],{"type":418,"value":1023},{"type":413,"tag":623,"props":6458,"children":6459},{"style":671},[6460],{"type":418,"value":2524},{"type":413,"tag":623,"props":6462,"children":6463},{"class":625,"line":1054},[6464],{"type":413,"tag":623,"props":6465,"children":6466},{"emptyLinePlaceholder":2790},[6467],{"type":418,"value":2793},{"type":413,"tag":623,"props":6469,"children":6470},{"class":625,"line":1087},[6471,6475,6480,6484,6489,6493,6497,6501,6505,6509],{"type":413,"tag":623,"props":6472,"children":6473},{"style":671},[6474],{"type":418,"value":2857},{"type":413,"tag":623,"props":6476,"children":6477},{"style":1058},[6478],{"type":418,"value":6479}," authorization",{"type":413,"tag":623,"props":6481,"children":6482},{"style":671},[6483],{"type":418,"value":1404},{"type":413,"tag":623,"props":6485,"children":6486},{"style":1407},[6487],{"type":418,"value":6488},"RoleAssignment",{"type":413,"tag":623,"props":6490,"children":6491},{"style":1058},[6492],{"type":418,"value":1018},{"type":413,"tag":623,"props":6494,"children":6495},{"style":671},[6496],{"type":418,"value":1023},{"type":413,"tag":623,"props":6498,"children":6499},{"style":635},[6500],{"type":418,"value":2875},{"type":413,"tag":623,"props":6502,"children":6503},{"style":671},[6504],{"type":418,"value":1023},{"type":413,"tag":623,"props":6506,"children":6507},{"style":671},[6508],{"type":418,"value":1037},{"type":413,"tag":623,"props":6510,"children":6511},{"style":671},[6512],{"type":418,"value":5507},{"type":413,"tag":623,"props":6514,"children":6515},{"class":625,"line":1104},[6516,6521,6525,6529,6533,6538],{"type":413,"tag":623,"props":6517,"children":6518},{"style":5513},[6519],{"type":418,"value":6520},"  principalId",{"type":413,"tag":623,"props":6522,"children":6523},{"style":671},[6524],{"type":418,"value":3493},{"type":413,"tag":623,"props":6526,"children":6527},{"style":1058},[6528],{"type":418,"value":2076},{"type":413,"tag":623,"props":6530,"children":6531},{"style":671},[6532],{"type":418,"value":1404},{"type":413,"tag":623,"props":6534,"children":6535},{"style":1058},[6536],{"type":418,"value":6537},"id",{"type":413,"tag":623,"props":6539,"children":6540},{"style":671},[6541],{"type":418,"value":1084},{"type":413,"tag":623,"props":6543,"children":6544},{"class":625,"line":1113},[6545,6550,6554,6558,6562,6567,6571,6575],{"type":413,"tag":623,"props":6546,"children":6547},{"style":5513},[6548],{"type":418,"value":6549},"  principalType",{"type":413,"tag":623,"props":6551,"children":6552},{"style":671},[6553],{"type":418,"value":3493},{"type":413,"tag":623,"props":6555,"children":6556},{"style":1058},[6557],{"type":418,"value":6479},{"type":413,"tag":623,"props":6559,"children":6560},{"style":671},[6561],{"type":418,"value":1404},{"type":413,"tag":623,"props":6563,"children":6564},{"style":1058},[6565],{"type":418,"value":6566},"PrincipalType",{"type":413,"tag":623,"props":6568,"children":6569},{"style":671},[6570],{"type":418,"value":1404},{"type":413,"tag":623,"props":6572,"children":6573},{"style":1058},[6574],{"type":418,"value":2950},{"type":413,"tag":623,"props":6576,"children":6577},{"style":671},[6578],{"type":418,"value":1084},{"type":413,"tag":623,"props":6580,"children":6581},{"class":625,"line":1161},[6582,6587,6591,6595,6599,6603],{"type":413,"tag":623,"props":6583,"children":6584},{"style":5513},[6585],{"type":418,"value":6586},"  roleDefinitionId",{"type":413,"tag":623,"props":6588,"children":6589},{"style":671},[6590],{"type":418,"value":3493},{"type":413,"tag":623,"props":6592,"children":6593},{"style":1058},[6594],{"type":418,"value":6434},{"type":413,"tag":623,"props":6596,"children":6597},{"style":671},[6598],{"type":418,"value":1404},{"type":413,"tag":623,"props":6600,"children":6601},{"style":1058},[6602],{"type":418,"value":2875},{"type":413,"tag":623,"props":6604,"children":6605},{"style":671},[6606],{"type":418,"value":1084},{"type":413,"tag":623,"props":6608,"children":6609},{"class":625,"line":1207},[6610,6615,6619,6623,6627,6631,6635,6639,6643,6647,6652],{"type":413,"tag":623,"props":6611,"children":6612},{"style":5513},[6613],{"type":418,"value":6614},"  scope",{"type":413,"tag":623,"props":6616,"children":6617},{"style":671},[6618],{"type":418,"value":3493},{"type":413,"tag":623,"props":6620,"children":6621},{"style":1058},[6622],{"type":418,"value":6229},{"type":413,"tag":623,"props":6624,"children":6625},{"style":671},[6626],{"type":418,"value":1404},{"type":413,"tag":623,"props":6628,"children":6629},{"style":1407},[6630],{"type":418,"value":6238},{"type":413,"tag":623,"props":6632,"children":6633},{"style":671},[6634],{"type":418,"value":6243},{"type":413,"tag":623,"props":6636,"children":6637},{"style":635},[6638],{"type":418,"value":2451},{"type":413,"tag":623,"props":6640,"children":6641},{"style":671},[6642],{"type":418,"value":6253},{"type":413,"tag":623,"props":6644,"children":6645},{"style":1058},[6646],{"type":418,"value":3033},{"type":413,"tag":623,"props":6648,"children":6649},{"style":671},[6650],{"type":418,"value":6651},"}`",{"type":413,"tag":623,"props":6653,"children":6654},{"style":671},[6655],{"type":418,"value":1084},{"type":413,"tag":623,"props":6657,"children":6658},{"class":625,"line":1251},[6659,6663,6667],{"type":413,"tag":623,"props":6660,"children":6661},{"style":671},[6662],{"type":418,"value":3327},{"type":413,"tag":623,"props":6664,"children":6665},{"style":1058},[6666],{"type":418,"value":5595},{"type":413,"tag":623,"props":6668,"children":6669},{"style":671},[6670],{"type":418,"value":2524},{"type":413,"tag":414,"props":6672,"children":6673},{},[6674,6676,6681],{"type":418,"value":6675},"I intentionally did not declare the variable ",{"type":413,"tag":619,"props":6677,"children":6679},{"className":6678},[],[6680],{"type":418,"value":3033},{"type":418,"value":6682}," 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":413,"tag":612,"props":6684,"children":6686},{"className":5390,"code":6685,"language":357,"meta":401,"style":401},"const config = new pulumi.Config();\nconst subscriptionId = config.get(\"subscriptionId\");\n",[6687],{"type":413,"tag":619,"props":6688,"children":6689},{"__ignoreMap":401},[6690,6732],{"type":413,"tag":623,"props":6691,"children":6692},{"class":625,"line":626},[6693,6697,6702,6706,6710,6714,6718,6723,6728],{"type":413,"tag":623,"props":6694,"children":6695},{"style":2854},[6696],{"type":418,"value":5454},{"type":413,"tag":623,"props":6698,"children":6699},{"style":1058},[6700],{"type":418,"value":6701}," config ",{"type":413,"tag":623,"props":6703,"children":6704},{"style":671},[6705],{"type":418,"value":1066},{"type":413,"tag":623,"props":6707,"children":6708},{"style":671},[6709],{"type":418,"value":638},{"type":413,"tag":623,"props":6711,"children":6712},{"style":1058},[6713],{"type":418,"value":6229},{"type":413,"tag":623,"props":6715,"children":6716},{"style":671},[6717],{"type":418,"value":1404},{"type":413,"tag":623,"props":6719,"children":6720},{"style":1407},[6721],{"type":418,"value":6722},"Config",{"type":413,"tag":623,"props":6724,"children":6725},{"style":1058},[6726],{"type":418,"value":6727},"()",{"type":413,"tag":623,"props":6729,"children":6730},{"style":671},[6731],{"type":418,"value":2524},{"type":413,"tag":623,"props":6733,"children":6734},{"class":625,"line":1045},[6735,6739,6744,6748,6752,6756,6761,6765,6769,6773,6777,6781],{"type":413,"tag":623,"props":6736,"children":6737},{"style":2854},[6738],{"type":418,"value":5454},{"type":413,"tag":623,"props":6740,"children":6741},{"style":1058},[6742],{"type":418,"value":6743}," subscriptionId ",{"type":413,"tag":623,"props":6745,"children":6746},{"style":671},[6747],{"type":418,"value":1066},{"type":413,"tag":623,"props":6749,"children":6750},{"style":1058},[6751],{"type":418,"value":879},{"type":413,"tag":623,"props":6753,"children":6754},{"style":671},[6755],{"type":418,"value":1404},{"type":413,"tag":623,"props":6757,"children":6758},{"style":1407},[6759],{"type":418,"value":6760},"get",{"type":413,"tag":623,"props":6762,"children":6763},{"style":1058},[6764],{"type":418,"value":1018},{"type":413,"tag":623,"props":6766,"children":6767},{"style":671},[6768],{"type":418,"value":1023},{"type":413,"tag":623,"props":6770,"children":6771},{"style":635},[6772],{"type":418,"value":3033},{"type":413,"tag":623,"props":6774,"children":6775},{"style":671},[6776],{"type":418,"value":1023},{"type":413,"tag":623,"props":6778,"children":6779},{"style":1058},[6780],{"type":418,"value":5595},{"type":413,"tag":623,"props":6782,"children":6783},{"style":671},[6784],{"type":418,"value":2524},{"type":413,"tag":414,"props":6786,"children":6787},{},[6788],{"type":418,"value":6789},"Or your might want to retrieve it from the current configuration of the Azure native provider :",{"type":413,"tag":612,"props":6791,"children":6793},{"className":5390,"code":6792,"language":357,"meta":401,"style":401},"const azureConfig = pulumi.output(authorization.getClientConfig());\nconst subscriptionId = azureConfig.subscriptionId;\n",[6794],{"type":413,"tag":619,"props":6795,"children":6796},{"__ignoreMap":401},[6797,6848],{"type":413,"tag":623,"props":6798,"children":6799},{"class":625,"line":626},[6800,6804,6809,6813,6817,6821,6825,6830,6834,6839,6844],{"type":413,"tag":623,"props":6801,"children":6802},{"style":2854},[6803],{"type":418,"value":5454},{"type":413,"tag":623,"props":6805,"children":6806},{"style":1058},[6807],{"type":418,"value":6808}," azureConfig ",{"type":413,"tag":623,"props":6810,"children":6811},{"style":671},[6812],{"type":418,"value":1066},{"type":413,"tag":623,"props":6814,"children":6815},{"style":1058},[6816],{"type":418,"value":6229},{"type":413,"tag":623,"props":6818,"children":6819},{"style":671},[6820],{"type":418,"value":1404},{"type":413,"tag":623,"props":6822,"children":6823},{"style":1407},[6824],{"type":418,"value":5679},{"type":413,"tag":623,"props":6826,"children":6827},{"style":1058},[6828],{"type":418,"value":6829},"(authorization",{"type":413,"tag":623,"props":6831,"children":6832},{"style":671},[6833],{"type":418,"value":1404},{"type":413,"tag":623,"props":6835,"children":6836},{"style":1407},[6837],{"type":418,"value":6838},"getClientConfig",{"type":413,"tag":623,"props":6840,"children":6841},{"style":1058},[6842],{"type":418,"value":6843},"())",{"type":413,"tag":623,"props":6845,"children":6846},{"style":671},[6847],{"type":418,"value":2524},{"type":413,"tag":623,"props":6849,"children":6850},{"class":625,"line":1045},[6851,6855,6859,6863,6867,6871,6875],{"type":413,"tag":623,"props":6852,"children":6853},{"style":2854},[6854],{"type":418,"value":5454},{"type":413,"tag":623,"props":6856,"children":6857},{"style":1058},[6858],{"type":418,"value":6743},{"type":413,"tag":623,"props":6860,"children":6861},{"style":671},[6862],{"type":418,"value":1066},{"type":413,"tag":623,"props":6864,"children":6865},{"style":1058},[6866],{"type":418,"value":2137},{"type":413,"tag":623,"props":6868,"children":6869},{"style":671},[6870],{"type":418,"value":1404},{"type":413,"tag":623,"props":6872,"children":6873},{"style":1058},[6874],{"type":418,"value":3033},{"type":413,"tag":623,"props":6876,"children":6877},{"style":671},[6878],{"type":418,"value":2524},{"type":413,"tag":414,"props":6880,"children":6881},{},[6882,6884,6890,6892,6898],{"type":418,"value":6883},"Concerning, the contributor role definition identifier, I could have dynamically retrieved it using Azure APIs (like ",{"type":413,"tag":432,"props":6885,"children":6888},{"href":6886,"rel":6887},"https://github.com/pulumi/examples/blob/master/azure-ts-call-azure-sdk/index.ts",[436],[6889],{"type":418,"value":542},{"type":418,"value":6891},"). But honestly, as these identifiers don't change it's much easier to hardcode it in a dedicated ",{"type":413,"tag":619,"props":6893,"children":6895},{"className":6894},[],[6896],{"type":418,"value":6897},"builtInRoles.ts",{"type":418,"value":6899}," file.",{"type":413,"tag":612,"props":6901,"children":6903},{"className":5390,"code":6902,"language":357,"meta":401,"style":401},"export const azureBuiltInRoles = {\n  contributor : \"/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c\"\n};\n",[6904],{"type":413,"tag":619,"props":6905,"children":6906},{"__ignoreMap":401},[6907,6931,6956],{"type":413,"tag":623,"props":6908,"children":6909},{"class":625,"line":626},[6910,6914,6918,6923,6927],{"type":413,"tag":623,"props":6911,"children":6912},{"style":2485},[6913],{"type":418,"value":5614},{"type":413,"tag":623,"props":6915,"children":6916},{"style":2854},[6917],{"type":418,"value":5619},{"type":413,"tag":623,"props":6919,"children":6920},{"style":1058},[6921],{"type":418,"value":6922}," azureBuiltInRoles ",{"type":413,"tag":623,"props":6924,"children":6925},{"style":671},[6926],{"type":418,"value":1066},{"type":413,"tag":623,"props":6928,"children":6929},{"style":671},[6930],{"type":418,"value":5507},{"type":413,"tag":623,"props":6932,"children":6933},{"class":625,"line":1045},[6934,6939,6943,6947,6952],{"type":413,"tag":623,"props":6935,"children":6936},{"style":5513},[6937],{"type":418,"value":6938},"  contributor ",{"type":413,"tag":623,"props":6940,"children":6941},{"style":671},[6942],{"type":418,"value":3493},{"type":413,"tag":623,"props":6944,"children":6945},{"style":671},[6946],{"type":418,"value":674},{"type":413,"tag":623,"props":6948,"children":6949},{"style":635},[6950],{"type":418,"value":6951},"/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c",{"type":413,"tag":623,"props":6953,"children":6954},{"style":671},[6955],{"type":418,"value":684},{"type":413,"tag":623,"props":6957,"children":6958},{"class":625,"line":1054},[6959],{"type":413,"tag":623,"props":6960,"children":6961},{"style":671},[6962],{"type":418,"value":4562},{"type":413,"tag":496,"props":6964,"children":6965},{"icon":4578},[6966],{"type":413,"tag":414,"props":6967,"children":6968},{},[6969],{"type":418,"value":6970},"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":413,"tag":600,"props":6972,"children":6974},{"id":6973},"add-the-configuration-for-the-github-actions-workflow",[6975],{"type":418,"value":6976},"Add the configuration for the GitHub Actions workflow",{"type":413,"tag":414,"props":6978,"children":6979},{},[6980],{"type":418,"value":6981},"The next step is to correctly set the configuration for the GitHub Actions of our Azure-Ready GitHub repository.",{"type":413,"tag":414,"props":6983,"children":6984},{},[6985],{"type":418,"value":6986},"The workflow requires three pieces of information for the OIDC authentication to function properly:",{"type":413,"tag":6988,"props":6989,"children":6990},"ol",{},[6991,6996,7001],{"type":413,"tag":447,"props":6992,"children":6993},{},[6994],{"type":418,"value":6995},"The identifier of the Azure tenant",{"type":413,"tag":447,"props":6997,"children":6998},{},[6999],{"type":418,"value":7000},"The identifier of the Azure subscription",{"type":413,"tag":447,"props":7002,"children":7003},{},[7004],{"type":418,"value":7005},"The application identifier (also known as client ID) of the previously created Azure AD application",{"type":413,"tag":414,"props":7007,"children":7008},{},[7009],{"type":418,"value":7010},"These identifiers are not secrets, they are just identifiers so we could directly set them as GitHub Actions variables like this:",{"type":413,"tag":612,"props":7012,"children":7014},{"className":5390,"code":7013,"language":357,"meta":401,"style":401},"new github.ActionsVariable(\"tenantId\", {\n  repository: repository.name,\n  variableName: \"ARM_TENANT_ID\",\n  value: azureConfig.tenantId,\n});\n",[7015],{"type":413,"tag":619,"props":7016,"children":7017},{"__ignoreMap":401},[7018,7062,7090,7119,7147],{"type":413,"tag":623,"props":7019,"children":7020},{"class":625,"line":626},[7021,7025,7029,7033,7038,7042,7046,7050,7054,7058],{"type":413,"tag":623,"props":7022,"children":7023},{"style":671},[7024],{"type":418,"value":2857},{"type":413,"tag":623,"props":7026,"children":7027},{"style":1058},[7028],{"type":418,"value":5472},{"type":413,"tag":623,"props":7030,"children":7031},{"style":671},[7032],{"type":418,"value":1404},{"type":413,"tag":623,"props":7034,"children":7035},{"style":1407},[7036],{"type":418,"value":7037},"ActionsVariable",{"type":413,"tag":623,"props":7039,"children":7040},{"style":1058},[7041],{"type":418,"value":1018},{"type":413,"tag":623,"props":7043,"children":7044},{"style":671},[7045],{"type":418,"value":1023},{"type":413,"tag":623,"props":7047,"children":7048},{"style":635},[7049],{"type":418,"value":2215},{"type":413,"tag":623,"props":7051,"children":7052},{"style":671},[7053],{"type":418,"value":1023},{"type":413,"tag":623,"props":7055,"children":7056},{"style":671},[7057],{"type":418,"value":1037},{"type":413,"tag":623,"props":7059,"children":7060},{"style":671},[7061],{"type":418,"value":5507},{"type":413,"tag":623,"props":7063,"children":7064},{"class":625,"line":1045},[7065,7070,7074,7078,7082,7086],{"type":413,"tag":623,"props":7066,"children":7067},{"style":5513},[7068],{"type":418,"value":7069},"  repository",{"type":413,"tag":623,"props":7071,"children":7072},{"style":671},[7073],{"type":418,"value":3493},{"type":413,"tag":623,"props":7075,"children":7076},{"style":1058},[7077],{"type":418,"value":1390},{"type":413,"tag":623,"props":7079,"children":7080},{"style":671},[7081],{"type":418,"value":1404},{"type":413,"tag":623,"props":7083,"children":7084},{"style":1058},[7085],{"type":418,"value":5665},{"type":413,"tag":623,"props":7087,"children":7088},{"style":671},[7089],{"type":418,"value":1084},{"type":413,"tag":623,"props":7091,"children":7092},{"class":625,"line":1054},[7093,7098,7102,7106,7111,7115],{"type":413,"tag":623,"props":7094,"children":7095},{"style":5513},[7096],{"type":418,"value":7097},"  variableName",{"type":413,"tag":623,"props":7099,"children":7100},{"style":671},[7101],{"type":418,"value":3493},{"type":413,"tag":623,"props":7103,"children":7104},{"style":671},[7105],{"type":418,"value":674},{"type":413,"tag":623,"props":7107,"children":7108},{"style":635},[7109],{"type":418,"value":7110},"ARM_TENANT_ID",{"type":413,"tag":623,"props":7112,"children":7113},{"style":671},[7114],{"type":418,"value":1023},{"type":413,"tag":623,"props":7116,"children":7117},{"style":671},[7118],{"type":418,"value":1084},{"type":413,"tag":623,"props":7120,"children":7121},{"class":625,"line":1087},[7122,7127,7131,7135,7139,7143],{"type":413,"tag":623,"props":7123,"children":7124},{"style":5513},[7125],{"type":418,"value":7126},"  value",{"type":413,"tag":623,"props":7128,"children":7129},{"style":671},[7130],{"type":418,"value":3493},{"type":413,"tag":623,"props":7132,"children":7133},{"style":1058},[7134],{"type":418,"value":2137},{"type":413,"tag":623,"props":7136,"children":7137},{"style":671},[7138],{"type":418,"value":1404},{"type":413,"tag":623,"props":7140,"children":7141},{"style":1058},[7142],{"type":418,"value":2215},{"type":413,"tag":623,"props":7144,"children":7145},{"style":671},[7146],{"type":418,"value":1084},{"type":413,"tag":623,"props":7148,"children":7149},{"class":625,"line":1104},[7150,7154,7158],{"type":413,"tag":623,"props":7151,"children":7152},{"style":671},[7153],{"type":418,"value":3327},{"type":413,"tag":623,"props":7155,"children":7156},{"style":1058},[7157],{"type":418,"value":5595},{"type":413,"tag":623,"props":7159,"children":7160},{"style":671},[7161],{"type":418,"value":2524},{"type":413,"tag":414,"props":7163,"children":7164},{},[7165],{"type":418,"value":7166},"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":413,"tag":612,"props":7168,"children":7170},{"className":5390,"code":7169,"language":357,"meta":401,"style":401},"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",[7171],{"type":413,"tag":619,"props":7172,"children":7173},{"__ignoreMap":401},[7174,7221,7228,7272,7299,7327,7355,7370,7377,7420,7447,7475,7502,7517,7524,7568,7595,7623,7650],{"type":413,"tag":623,"props":7175,"children":7176},{"class":625,"line":626},[7177,7181,7185,7189,7193,7197,7201,7205,7209,7213,7217],{"type":413,"tag":623,"props":7178,"children":7179},{"style":2854},[7180],{"type":418,"value":5454},{"type":413,"tag":623,"props":7182,"children":7183},{"style":1058},[7184],{"type":418,"value":6808},{"type":413,"tag":623,"props":7186,"children":7187},{"style":671},[7188],{"type":418,"value":1066},{"type":413,"tag":623,"props":7190,"children":7191},{"style":1058},[7192],{"type":418,"value":6229},{"type":413,"tag":623,"props":7194,"children":7195},{"style":671},[7196],{"type":418,"value":1404},{"type":413,"tag":623,"props":7198,"children":7199},{"style":1407},[7200],{"type":418,"value":5679},{"type":413,"tag":623,"props":7202,"children":7203},{"style":1058},[7204],{"type":418,"value":6829},{"type":413,"tag":623,"props":7206,"children":7207},{"style":671},[7208],{"type":418,"value":1404},{"type":413,"tag":623,"props":7210,"children":7211},{"style":1407},[7212],{"type":418,"value":6838},{"type":413,"tag":623,"props":7214,"children":7215},{"style":1058},[7216],{"type":418,"value":6843},{"type":413,"tag":623,"props":7218,"children":7219},{"style":671},[7220],{"type":418,"value":2524},{"type":413,"tag":623,"props":7222,"children":7223},{"class":625,"line":1045},[7224],{"type":413,"tag":623,"props":7225,"children":7226},{"emptyLinePlaceholder":2790},[7227],{"type":418,"value":2793},{"type":413,"tag":623,"props":7229,"children":7230},{"class":625,"line":1054},[7231,7235,7239,7243,7248,7252,7256,7260,7264,7268],{"type":413,"tag":623,"props":7232,"children":7233},{"style":671},[7234],{"type":418,"value":2857},{"type":413,"tag":623,"props":7236,"children":7237},{"style":1058},[7238],{"type":418,"value":5472},{"type":413,"tag":623,"props":7240,"children":7241},{"style":671},[7242],{"type":418,"value":1404},{"type":413,"tag":623,"props":7244,"children":7245},{"style":1407},[7246],{"type":418,"value":7247},"ActionsSecret",{"type":413,"tag":623,"props":7249,"children":7250},{"style":1058},[7251],{"type":418,"value":1018},{"type":413,"tag":623,"props":7253,"children":7254},{"style":671},[7255],{"type":418,"value":1023},{"type":413,"tag":623,"props":7257,"children":7258},{"style":635},[7259],{"type":418,"value":2215},{"type":413,"tag":623,"props":7261,"children":7262},{"style":671},[7263],{"type":418,"value":1023},{"type":413,"tag":623,"props":7265,"children":7266},{"style":671},[7267],{"type":418,"value":1037},{"type":413,"tag":623,"props":7269,"children":7270},{"style":671},[7271],{"type":418,"value":5507},{"type":413,"tag":623,"props":7273,"children":7274},{"class":625,"line":1087},[7275,7279,7283,7287,7291,7295],{"type":413,"tag":623,"props":7276,"children":7277},{"style":5513},[7278],{"type":418,"value":7069},{"type":413,"tag":623,"props":7280,"children":7281},{"style":671},[7282],{"type":418,"value":3493},{"type":413,"tag":623,"props":7284,"children":7285},{"style":1058},[7286],{"type":418,"value":1390},{"type":413,"tag":623,"props":7288,"children":7289},{"style":671},[7290],{"type":418,"value":1404},{"type":413,"tag":623,"props":7292,"children":7293},{"style":1058},[7294],{"type":418,"value":5665},{"type":413,"tag":623,"props":7296,"children":7297},{"style":671},[7298],{"type":418,"value":1084},{"type":413,"tag":623,"props":7300,"children":7301},{"class":625,"line":1104},[7302,7307,7311,7315,7319,7323],{"type":413,"tag":623,"props":7303,"children":7304},{"style":5513},[7305],{"type":418,"value":7306},"  secretName",{"type":413,"tag":623,"props":7308,"children":7309},{"style":671},[7310],{"type":418,"value":3493},{"type":413,"tag":623,"props":7312,"children":7313},{"style":671},[7314],{"type":418,"value":674},{"type":413,"tag":623,"props":7316,"children":7317},{"style":635},[7318],{"type":418,"value":7110},{"type":413,"tag":623,"props":7320,"children":7321},{"style":671},[7322],{"type":418,"value":1023},{"type":413,"tag":623,"props":7324,"children":7325},{"style":671},[7326],{"type":418,"value":1084},{"type":413,"tag":623,"props":7328,"children":7329},{"class":625,"line":1113},[7330,7335,7339,7343,7347,7351],{"type":413,"tag":623,"props":7331,"children":7332},{"style":5513},[7333],{"type":418,"value":7334},"  plaintextValue",{"type":413,"tag":623,"props":7336,"children":7337},{"style":671},[7338],{"type":418,"value":3493},{"type":413,"tag":623,"props":7340,"children":7341},{"style":1058},[7342],{"type":418,"value":2137},{"type":413,"tag":623,"props":7344,"children":7345},{"style":671},[7346],{"type":418,"value":1404},{"type":413,"tag":623,"props":7348,"children":7349},{"style":1058},[7350],{"type":418,"value":2215},{"type":413,"tag":623,"props":7352,"children":7353},{"style":671},[7354],{"type":418,"value":1084},{"type":413,"tag":623,"props":7356,"children":7357},{"class":625,"line":1161},[7358,7362,7366],{"type":413,"tag":623,"props":7359,"children":7360},{"style":671},[7361],{"type":418,"value":3327},{"type":413,"tag":623,"props":7363,"children":7364},{"style":1058},[7365],{"type":418,"value":5595},{"type":413,"tag":623,"props":7367,"children":7368},{"style":671},[7369],{"type":418,"value":2524},{"type":413,"tag":623,"props":7371,"children":7372},{"class":625,"line":1207},[7373],{"type":413,"tag":623,"props":7374,"children":7375},{"emptyLinePlaceholder":2790},[7376],{"type":418,"value":2793},{"type":413,"tag":623,"props":7378,"children":7379},{"class":625,"line":1251},[7380,7384,7388,7392,7396,7400,7404,7408,7412,7416],{"type":413,"tag":623,"props":7381,"children":7382},{"style":671},[7383],{"type":418,"value":2857},{"type":413,"tag":623,"props":7385,"children":7386},{"style":1058},[7387],{"type":418,"value":5472},{"type":413,"tag":623,"props":7389,"children":7390},{"style":671},[7391],{"type":418,"value":1404},{"type":413,"tag":623,"props":7393,"children":7394},{"style":1407},[7395],{"type":418,"value":7247},{"type":413,"tag":623,"props":7397,"children":7398},{"style":1058},[7399],{"type":418,"value":1018},{"type":413,"tag":623,"props":7401,"children":7402},{"style":671},[7403],{"type":418,"value":1023},{"type":413,"tag":623,"props":7405,"children":7406},{"style":635},[7407],{"type":418,"value":3033},{"type":413,"tag":623,"props":7409,"children":7410},{"style":671},[7411],{"type":418,"value":1023},{"type":413,"tag":623,"props":7413,"children":7414},{"style":671},[7415],{"type":418,"value":1037},{"type":413,"tag":623,"props":7417,"children":7418},{"style":671},[7419],{"type":418,"value":5507},{"type":413,"tag":623,"props":7421,"children":7422},{"class":625,"line":1296},[7423,7427,7431,7435,7439,7443],{"type":413,"tag":623,"props":7424,"children":7425},{"style":5513},[7426],{"type":418,"value":7069},{"type":413,"tag":623,"props":7428,"children":7429},{"style":671},[7430],{"type":418,"value":3493},{"type":413,"tag":623,"props":7432,"children":7433},{"style":1058},[7434],{"type":418,"value":1390},{"type":413,"tag":623,"props":7436,"children":7437},{"style":671},[7438],{"type":418,"value":1404},{"type":413,"tag":623,"props":7440,"children":7441},{"style":1058},[7442],{"type":418,"value":5665},{"type":413,"tag":623,"props":7444,"children":7445},{"style":671},[7446],{"type":418,"value":1084},{"type":413,"tag":623,"props":7448,"children":7449},{"class":625,"line":1337},[7450,7454,7458,7462,7467,7471],{"type":413,"tag":623,"props":7451,"children":7452},{"style":5513},[7453],{"type":418,"value":7306},{"type":413,"tag":623,"props":7455,"children":7456},{"style":671},[7457],{"type":418,"value":3493},{"type":413,"tag":623,"props":7459,"children":7460},{"style":671},[7461],{"type":418,"value":674},{"type":413,"tag":623,"props":7463,"children":7464},{"style":635},[7465],{"type":418,"value":7466},"ARM_SUBSCRIPTION_ID",{"type":413,"tag":623,"props":7468,"children":7469},{"style":671},[7470],{"type":418,"value":1023},{"type":413,"tag":623,"props":7472,"children":7473},{"style":671},[7474],{"type":418,"value":1084},{"type":413,"tag":623,"props":7476,"children":7477},{"class":625,"line":1346},[7478,7482,7486,7490,7494,7498],{"type":413,"tag":623,"props":7479,"children":7480},{"style":5513},[7481],{"type":418,"value":7334},{"type":413,"tag":623,"props":7483,"children":7484},{"style":671},[7485],{"type":418,"value":3493},{"type":413,"tag":623,"props":7487,"children":7488},{"style":1058},[7489],{"type":418,"value":2137},{"type":413,"tag":623,"props":7491,"children":7492},{"style":671},[7493],{"type":418,"value":1404},{"type":413,"tag":623,"props":7495,"children":7496},{"style":1058},[7497],{"type":418,"value":3033},{"type":413,"tag":623,"props":7499,"children":7500},{"style":671},[7501],{"type":418,"value":1084},{"type":413,"tag":623,"props":7503,"children":7504},{"class":625,"line":2100},[7505,7509,7513],{"type":413,"tag":623,"props":7506,"children":7507},{"style":671},[7508],{"type":418,"value":3327},{"type":413,"tag":623,"props":7510,"children":7511},{"style":1058},[7512],{"type":418,"value":5595},{"type":413,"tag":623,"props":7514,"children":7515},{"style":671},[7516],{"type":418,"value":2524},{"type":413,"tag":623,"props":7518,"children":7519},{"class":625,"line":2897},[7520],{"type":413,"tag":623,"props":7521,"children":7522},{"emptyLinePlaceholder":2790},[7523],{"type":418,"value":2793},{"type":413,"tag":623,"props":7525,"children":7526},{"class":625,"line":2926},[7527,7531,7535,7539,7543,7547,7551,7556,7560,7564],{"type":413,"tag":623,"props":7528,"children":7529},{"style":671},[7530],{"type":418,"value":2857},{"type":413,"tag":623,"props":7532,"children":7533},{"style":1058},[7534],{"type":418,"value":5472},{"type":413,"tag":623,"props":7536,"children":7537},{"style":671},[7538],{"type":418,"value":1404},{"type":413,"tag":623,"props":7540,"children":7541},{"style":1407},[7542],{"type":418,"value":7247},{"type":413,"tag":623,"props":7544,"children":7545},{"style":1058},[7546],{"type":418,"value":1018},{"type":413,"tag":623,"props":7548,"children":7549},{"style":671},[7550],{"type":418,"value":1023},{"type":413,"tag":623,"props":7552,"children":7553},{"style":635},[7554],{"type":418,"value":7555},"clientId",{"type":413,"tag":623,"props":7557,"children":7558},{"style":671},[7559],{"type":418,"value":1023},{"type":413,"tag":623,"props":7561,"children":7562},{"style":671},[7563],{"type":418,"value":1037},{"type":413,"tag":623,"props":7565,"children":7566},{"style":671},[7567],{"type":418,"value":5507},{"type":413,"tag":623,"props":7569,"children":7570},{"class":625,"line":2957},[7571,7575,7579,7583,7587,7591],{"type":413,"tag":623,"props":7572,"children":7573},{"style":5513},[7574],{"type":418,"value":7069},{"type":413,"tag":623,"props":7576,"children":7577},{"style":671},[7578],{"type":418,"value":3493},{"type":413,"tag":623,"props":7580,"children":7581},{"style":1058},[7582],{"type":418,"value":1390},{"type":413,"tag":623,"props":7584,"children":7585},{"style":671},[7586],{"type":418,"value":1404},{"type":413,"tag":623,"props":7588,"children":7589},{"style":1058},[7590],{"type":418,"value":5665},{"type":413,"tag":623,"props":7592,"children":7593},{"style":671},[7594],{"type":418,"value":1084},{"type":413,"tag":623,"props":7596,"children":7597},{"class":625,"line":2988},[7598,7602,7606,7610,7615,7619],{"type":413,"tag":623,"props":7599,"children":7600},{"style":5513},[7601],{"type":418,"value":7306},{"type":413,"tag":623,"props":7603,"children":7604},{"style":671},[7605],{"type":418,"value":3493},{"type":413,"tag":623,"props":7607,"children":7608},{"style":671},[7609],{"type":418,"value":674},{"type":413,"tag":623,"props":7611,"children":7612},{"style":635},[7613],{"type":418,"value":7614},"ARM_CLIENT_ID",{"type":413,"tag":623,"props":7616,"children":7617},{"style":671},[7618],{"type":418,"value":1023},{"type":413,"tag":623,"props":7620,"children":7621},{"style":671},[7622],{"type":418,"value":1084},{"type":413,"tag":623,"props":7624,"children":7625},{"class":625,"line":3045},[7626,7630,7634,7638,7642,7646],{"type":413,"tag":623,"props":7627,"children":7628},{"style":5513},[7629],{"type":418,"value":7334},{"type":413,"tag":623,"props":7631,"children":7632},{"style":671},[7633],{"type":418,"value":3493},{"type":413,"tag":623,"props":7635,"children":7636},{"style":1058},[7637],{"type":418,"value":2615},{"type":413,"tag":623,"props":7639,"children":7640},{"style":671},[7641],{"type":418,"value":1404},{"type":413,"tag":623,"props":7643,"children":7644},{"style":1058},[7645],{"type":418,"value":5969},{"type":413,"tag":623,"props":7647,"children":7648},{"style":671},[7649],{"type":418,"value":1084},{"type":413,"tag":623,"props":7651,"children":7653},{"class":625,"line":7652},19,[7654,7658,7662],{"type":413,"tag":623,"props":7655,"children":7656},{"style":671},[7657],{"type":418,"value":3327},{"type":413,"tag":623,"props":7659,"children":7660},{"style":1058},[7661],{"type":418,"value":5595},{"type":413,"tag":623,"props":7663,"children":7664},{"style":671},[7665],{"type":418,"value":2524},{"type":413,"tag":496,"props":7667,"children":7668},{"icon":498},[7669],{"type":413,"tag":414,"props":7670,"children":7671},{},[7672,7674,7681],{"type":418,"value":7673},"Please note that could also use ",{"type":413,"tag":432,"props":7675,"children":7678},{"href":7676,"rel":7677},"https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment",[436],[7679],{"type":418,"value":7680},"environments for deployment",{"type":418,"value":7682}," and their associated secrets and variables.",{"type":413,"tag":600,"props":7684,"children":7686},{"id":7685},"create-the-github-actions-workflow",[7687],{"type":418,"value":7688},"Create the GitHub Actions workflow",{"type":413,"tag":414,"props":7690,"children":7691},{},[7692],{"type":418,"value":7693},"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":413,"tag":414,"props":7695,"children":7696},{},[7697],{"type":418,"value":7698},"Here is such a pipeline:",{"type":413,"tag":612,"props":7700,"children":7704},{"className":7701,"code":7702,"language":7703,"meta":401,"style":401},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","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","yaml",[7705],{"type":413,"tag":619,"props":7706,"children":7707},{"__ignoreMap":401},[7708,7724,7731,7743,7755,7762,7774,7791,7808,7820,7832,7849,7861,7891,7908,7920,7937,7954,7971,7978,8007,8025,8034],{"type":413,"tag":623,"props":7709,"children":7710},{"class":625,"line":626},[7711,7715,7719],{"type":413,"tag":623,"props":7712,"children":7713},{"style":5513},[7714],{"type":418,"value":5665},{"type":413,"tag":623,"props":7716,"children":7717},{"style":671},[7718],{"type":418,"value":3493},{"type":413,"tag":623,"props":7720,"children":7721},{"style":635},[7722],{"type":418,"value":7723}," infra\n",{"type":413,"tag":623,"props":7725,"children":7726},{"class":625,"line":1045},[7727],{"type":413,"tag":623,"props":7728,"children":7729},{"emptyLinePlaceholder":2790},[7730],{"type":418,"value":2793},{"type":413,"tag":623,"props":7732,"children":7733},{"class":625,"line":1054},[7734,7739],{"type":413,"tag":623,"props":7735,"children":7736},{"style":5580},[7737],{"type":418,"value":7738},"on",{"type":413,"tag":623,"props":7740,"children":7741},{"style":671},[7742],{"type":418,"value":3448},{"type":413,"tag":623,"props":7744,"children":7745},{"class":625,"line":1087},[7746,7751],{"type":413,"tag":623,"props":7747,"children":7748},{"style":5513},[7749],{"type":418,"value":7750},"  workflow_dispatch",{"type":413,"tag":623,"props":7752,"children":7753},{"style":671},[7754],{"type":418,"value":3448},{"type":413,"tag":623,"props":7756,"children":7757},{"class":625,"line":1104},[7758],{"type":413,"tag":623,"props":7759,"children":7760},{"emptyLinePlaceholder":2790},[7761],{"type":418,"value":2793},{"type":413,"tag":623,"props":7763,"children":7764},{"class":625,"line":1113},[7765,7770],{"type":413,"tag":623,"props":7766,"children":7767},{"style":5513},[7768],{"type":418,"value":7769},"permissions",{"type":413,"tag":623,"props":7771,"children":7772},{"style":671},[7773],{"type":418,"value":3448},{"type":413,"tag":623,"props":7775,"children":7776},{"class":625,"line":1161},[7777,7782,7786],{"type":413,"tag":623,"props":7778,"children":7779},{"style":5513},[7780],{"type":418,"value":7781},"      id-token",{"type":413,"tag":623,"props":7783,"children":7784},{"style":671},[7785],{"type":418,"value":3493},{"type":413,"tag":623,"props":7787,"children":7788},{"style":635},[7789],{"type":418,"value":7790}," write\n",{"type":413,"tag":623,"props":7792,"children":7793},{"class":625,"line":1207},[7794,7799,7803],{"type":413,"tag":623,"props":7795,"children":7796},{"style":5513},[7797],{"type":418,"value":7798},"      contents",{"type":413,"tag":623,"props":7800,"children":7801},{"style":671},[7802],{"type":418,"value":3493},{"type":413,"tag":623,"props":7804,"children":7805},{"style":635},[7806],{"type":418,"value":7807}," read\n",{"type":413,"tag":623,"props":7809,"children":7810},{"class":625,"line":1251},[7811,7816],{"type":413,"tag":623,"props":7812,"children":7813},{"style":5513},[7814],{"type":418,"value":7815},"jobs",{"type":413,"tag":623,"props":7817,"children":7818},{"style":671},[7819],{"type":418,"value":3448},{"type":413,"tag":623,"props":7821,"children":7822},{"class":625,"line":1296},[7823,7828],{"type":413,"tag":623,"props":7824,"children":7825},{"style":5513},[7826],{"type":418,"value":7827},"  provision-infra",{"type":413,"tag":623,"props":7829,"children":7830},{"style":671},[7831],{"type":418,"value":3448},{"type":413,"tag":623,"props":7833,"children":7834},{"class":625,"line":1337},[7835,7840,7844],{"type":413,"tag":623,"props":7836,"children":7837},{"style":5513},[7838],{"type":418,"value":7839},"    runs-on",{"type":413,"tag":623,"props":7841,"children":7842},{"style":671},[7843],{"type":418,"value":3493},{"type":413,"tag":623,"props":7845,"children":7846},{"style":635},[7847],{"type":418,"value":7848}," ubuntu-latest\n",{"type":413,"tag":623,"props":7850,"children":7851},{"class":625,"line":1346},[7852,7857],{"type":413,"tag":623,"props":7853,"children":7854},{"style":5513},[7855],{"type":418,"value":7856},"    steps",{"type":413,"tag":623,"props":7858,"children":7859},{"style":671},[7860],{"type":418,"value":3448},{"type":413,"tag":623,"props":7862,"children":7863},{"class":625,"line":2100},[7864,7869,7874,7878,7882,7887],{"type":413,"tag":623,"props":7865,"children":7866},{"style":671},[7867],{"type":418,"value":7868},"      -",{"type":413,"tag":623,"props":7870,"children":7871},{"style":5513},[7872],{"type":418,"value":7873}," name",{"type":413,"tag":623,"props":7875,"children":7876},{"style":671},[7877],{"type":418,"value":3493},{"type":413,"tag":623,"props":7879,"children":7880},{"style":671},[7881],{"type":418,"value":3583},{"type":413,"tag":623,"props":7883,"children":7884},{"style":635},[7885],{"type":418,"value":7886},"Az CLI login",{"type":413,"tag":623,"props":7888,"children":7889},{"style":671},[7890],{"type":418,"value":3592},{"type":413,"tag":623,"props":7892,"children":7893},{"class":625,"line":2897},[7894,7899,7903],{"type":413,"tag":623,"props":7895,"children":7896},{"style":5513},[7897],{"type":418,"value":7898},"        uses",{"type":413,"tag":623,"props":7900,"children":7901},{"style":671},[7902],{"type":418,"value":3493},{"type":413,"tag":623,"props":7904,"children":7905},{"style":635},[7906],{"type":418,"value":7907}," azure/login@v1\n",{"type":413,"tag":623,"props":7909,"children":7910},{"class":625,"line":2926},[7911,7916],{"type":413,"tag":623,"props":7912,"children":7913},{"style":5513},[7914],{"type":418,"value":7915},"        with",{"type":413,"tag":623,"props":7917,"children":7918},{"style":671},[7919],{"type":418,"value":3448},{"type":413,"tag":623,"props":7921,"children":7922},{"class":625,"line":2957},[7923,7928,7932],{"type":413,"tag":623,"props":7924,"children":7925},{"style":5513},[7926],{"type":418,"value":7927},"          client-id",{"type":413,"tag":623,"props":7929,"children":7930},{"style":671},[7931],{"type":418,"value":3493},{"type":413,"tag":623,"props":7933,"children":7934},{"style":635},[7935],{"type":418,"value":7936}," ${{ secrets.AZURE_CLIENT_ID }}\n",{"type":413,"tag":623,"props":7938,"children":7939},{"class":625,"line":2988},[7940,7945,7949],{"type":413,"tag":623,"props":7941,"children":7942},{"style":5513},[7943],{"type":418,"value":7944},"          tenant-id",{"type":413,"tag":623,"props":7946,"children":7947},{"style":671},[7948],{"type":418,"value":3493},{"type":413,"tag":623,"props":7950,"children":7951},{"style":635},[7952],{"type":418,"value":7953}," ${{ secrets.AZURE_TENANT_ID }}\n",{"type":413,"tag":623,"props":7955,"children":7956},{"class":625,"line":3045},[7957,7962,7966],{"type":413,"tag":623,"props":7958,"children":7959},{"style":5513},[7960],{"type":418,"value":7961},"          subscription-id",{"type":413,"tag":623,"props":7963,"children":7964},{"style":671},[7965],{"type":418,"value":3493},{"type":413,"tag":623,"props":7967,"children":7968},{"style":635},[7969],{"type":418,"value":7970}," ${{ secrets.AZURE_SUBSCRIPTION_ID }}\n",{"type":413,"tag":623,"props":7972,"children":7973},{"class":625,"line":7652},[7974],{"type":413,"tag":623,"props":7975,"children":7976},{"emptyLinePlaceholder":2790},[7977],{"type":418,"value":2793},{"type":413,"tag":623,"props":7979,"children":7981},{"class":625,"line":7980},20,[7982,7986,7990,7994,7998,8003],{"type":413,"tag":623,"props":7983,"children":7984},{"style":671},[7985],{"type":418,"value":7868},{"type":413,"tag":623,"props":7987,"children":7988},{"style":5513},[7989],{"type":418,"value":7873},{"type":413,"tag":623,"props":7991,"children":7992},{"style":671},[7993],{"type":418,"value":3493},{"type":413,"tag":623,"props":7995,"children":7996},{"style":671},[7997],{"type":418,"value":3583},{"type":413,"tag":623,"props":7999,"children":8000},{"style":635},[8001],{"type":418,"value":8002},"Run az commands",{"type":413,"tag":623,"props":8004,"children":8005},{"style":671},[8006],{"type":418,"value":3592},{"type":413,"tag":623,"props":8008,"children":8010},{"class":625,"line":8009},21,[8011,8016,8020],{"type":413,"tag":623,"props":8012,"children":8013},{"style":5513},[8014],{"type":418,"value":8015},"        run",{"type":413,"tag":623,"props":8017,"children":8018},{"style":671},[8019],{"type":418,"value":3493},{"type":413,"tag":623,"props":8021,"children":8022},{"style":2485},[8023],{"type":418,"value":8024}," |\n",{"type":413,"tag":623,"props":8026,"children":8028},{"class":625,"line":8027},22,[8029],{"type":413,"tag":623,"props":8030,"children":8031},{"style":635},[8032],{"type":418,"value":8033},"          az account show\n",{"type":413,"tag":623,"props":8035,"children":8037},{"class":625,"line":8036},23,[8038],{"type":413,"tag":623,"props":8039,"children":8040},{"style":635},[8041],{"type":418,"value":8042},"          az group list\n",{"type":413,"tag":414,"props":8044,"children":8045},{},[8046,8048,8054],{"type":418,"value":8047},"This workflow first authenticates to Azure using OIDC with the ",{"type":413,"tag":619,"props":8049,"children":8051},{"className":8050},[],[8052],{"type":418,"value":8053},"azure/login",{"type":418,"value":8055}," 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":413,"tag":612,"props":8057,"children":8059},{"className":7701,"code":8058,"language":7703,"meta":401,"style":401},"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",[8060],{"type":413,"tag":619,"props":8061,"children":8062},{"__ignoreMap":401},[8063,8078,8085,8096,8107,8114,8125,8148,8170,8177,8188,8199,8214,8225,8246,8253,8273,8289,8300,8317,8324,8344,8360,8371,8389,8414,8423,8444,8461,8469,8490,8507,8525,8537,8555,8573,8586,8603,8621,8639,8657],{"type":413,"tag":623,"props":8064,"children":8065},{"class":625,"line":626},[8066,8070,8074],{"type":413,"tag":623,"props":8067,"children":8068},{"style":5513},[8069],{"type":418,"value":5665},{"type":413,"tag":623,"props":8071,"children":8072},{"style":671},[8073],{"type":418,"value":3493},{"type":413,"tag":623,"props":8075,"children":8076},{"style":635},[8077],{"type":418,"value":7723},{"type":413,"tag":623,"props":8079,"children":8080},{"class":625,"line":1045},[8081],{"type":413,"tag":623,"props":8082,"children":8083},{"emptyLinePlaceholder":2790},[8084],{"type":418,"value":2793},{"type":413,"tag":623,"props":8086,"children":8087},{"class":625,"line":1054},[8088,8092],{"type":413,"tag":623,"props":8089,"children":8090},{"style":5580},[8091],{"type":418,"value":7738},{"type":413,"tag":623,"props":8093,"children":8094},{"style":671},[8095],{"type":418,"value":3448},{"type":413,"tag":623,"props":8097,"children":8098},{"class":625,"line":1087},[8099,8103],{"type":413,"tag":623,"props":8100,"children":8101},{"style":5513},[8102],{"type":418,"value":7750},{"type":413,"tag":623,"props":8104,"children":8105},{"style":671},[8106],{"type":418,"value":3448},{"type":413,"tag":623,"props":8108,"children":8109},{"class":625,"line":1104},[8110],{"type":413,"tag":623,"props":8111,"children":8112},{"emptyLinePlaceholder":2790},[8113],{"type":418,"value":2793},{"type":413,"tag":623,"props":8115,"children":8116},{"class":625,"line":1113},[8117,8121],{"type":413,"tag":623,"props":8118,"children":8119},{"style":5513},[8120],{"type":418,"value":7769},{"type":413,"tag":623,"props":8122,"children":8123},{"style":671},[8124],{"type":418,"value":3448},{"type":413,"tag":623,"props":8126,"children":8127},{"class":625,"line":1161},[8128,8133,8137,8142],{"type":413,"tag":623,"props":8129,"children":8130},{"style":5513},[8131],{"type":418,"value":8132},"  id-token",{"type":413,"tag":623,"props":8134,"children":8135},{"style":671},[8136],{"type":418,"value":3493},{"type":413,"tag":623,"props":8138,"children":8139},{"style":635},[8140],{"type":418,"value":8141}," write",{"type":413,"tag":623,"props":8143,"children":8145},{"style":8144},"--shiki-light:#90A4AE;--shiki-default:#546E7A;--shiki-dark:#676E95;--shiki-light-font-style:italic;--shiki-default-font-style:italic;--shiki-dark-font-style:italic",[8146],{"type":418,"value":8147},"   # required for OIDC auth\n",{"type":413,"tag":623,"props":8149,"children":8150},{"class":625,"line":1207},[8151,8156,8160,8165],{"type":413,"tag":623,"props":8152,"children":8153},{"style":5513},[8154],{"type":418,"value":8155},"  contents",{"type":413,"tag":623,"props":8157,"children":8158},{"style":671},[8159],{"type":418,"value":3493},{"type":413,"tag":623,"props":8161,"children":8162},{"style":635},[8163],{"type":418,"value":8164}," read",{"type":413,"tag":623,"props":8166,"children":8167},{"style":8144},[8168],{"type":418,"value":8169},"    # required to perform a checkout\n",{"type":413,"tag":623,"props":8171,"children":8172},{"class":625,"line":1251},[8173],{"type":413,"tag":623,"props":8174,"children":8175},{"emptyLinePlaceholder":2790},[8176],{"type":418,"value":2793},{"type":413,"tag":623,"props":8178,"children":8179},{"class":625,"line":1296},[8180,8184],{"type":413,"tag":623,"props":8181,"children":8182},{"style":5513},[8183],{"type":418,"value":7815},{"type":413,"tag":623,"props":8185,"children":8186},{"style":671},[8187],{"type":418,"value":3448},{"type":413,"tag":623,"props":8189,"children":8190},{"class":625,"line":1337},[8191,8195],{"type":413,"tag":623,"props":8192,"children":8193},{"style":5513},[8194],{"type":418,"value":7827},{"type":413,"tag":623,"props":8196,"children":8197},{"style":671},[8198],{"type":418,"value":3448},{"type":413,"tag":623,"props":8200,"children":8201},{"class":625,"line":1346},[8202,8206,8210],{"type":413,"tag":623,"props":8203,"children":8204},{"style":5513},[8205],{"type":418,"value":7839},{"type":413,"tag":623,"props":8207,"children":8208},{"style":671},[8209],{"type":418,"value":3493},{"type":413,"tag":623,"props":8211,"children":8212},{"style":635},[8213],{"type":418,"value":7848},{"type":413,"tag":623,"props":8215,"children":8216},{"class":625,"line":2100},[8217,8221],{"type":413,"tag":623,"props":8218,"children":8219},{"style":5513},[8220],{"type":418,"value":7856},{"type":413,"tag":623,"props":8222,"children":8223},{"style":671},[8224],{"type":418,"value":3448},{"type":413,"tag":623,"props":8226,"children":8227},{"class":625,"line":2897},[8228,8232,8237,8241],{"type":413,"tag":623,"props":8229,"children":8230},{"style":671},[8231],{"type":418,"value":7868},{"type":413,"tag":623,"props":8233,"children":8234},{"style":5513},[8235],{"type":418,"value":8236}," uses",{"type":413,"tag":623,"props":8238,"children":8239},{"style":671},[8240],{"type":418,"value":3493},{"type":413,"tag":623,"props":8242,"children":8243},{"style":635},[8244],{"type":418,"value":8245}," actions/checkout@v3\n",{"type":413,"tag":623,"props":8247,"children":8248},{"class":625,"line":2926},[8249],{"type":413,"tag":623,"props":8250,"children":8251},{"emptyLinePlaceholder":2790},[8252],{"type":418,"value":2793},{"type":413,"tag":623,"props":8254,"children":8255},{"class":625,"line":2957},[8256,8260,8264,8268],{"type":413,"tag":623,"props":8257,"children":8258},{"style":671},[8259],{"type":418,"value":7868},{"type":413,"tag":623,"props":8261,"children":8262},{"style":5513},[8263],{"type":418,"value":7873},{"type":413,"tag":623,"props":8265,"children":8266},{"style":671},[8267],{"type":418,"value":3493},{"type":413,"tag":623,"props":8269,"children":8270},{"style":635},[8271],{"type":418,"value":8272}," Install pnpm\n",{"type":413,"tag":623,"props":8274,"children":8275},{"class":625,"line":2988},[8276,8280,8284],{"type":413,"tag":623,"props":8277,"children":8278},{"style":5513},[8279],{"type":418,"value":7898},{"type":413,"tag":623,"props":8281,"children":8282},{"style":671},[8283],{"type":418,"value":3493},{"type":413,"tag":623,"props":8285,"children":8286},{"style":635},[8287],{"type":418,"value":8288}," pnpm/action-setup@v2\n",{"type":413,"tag":623,"props":8290,"children":8291},{"class":625,"line":3045},[8292,8296],{"type":413,"tag":623,"props":8293,"children":8294},{"style":5513},[8295],{"type":418,"value":7915},{"type":413,"tag":623,"props":8297,"children":8298},{"style":671},[8299],{"type":418,"value":3448},{"type":413,"tag":623,"props":8301,"children":8302},{"class":625,"line":7652},[8303,8308,8312],{"type":413,"tag":623,"props":8304,"children":8305},{"style":5513},[8306],{"type":418,"value":8307},"          version",{"type":413,"tag":623,"props":8309,"children":8310},{"style":671},[8311],{"type":418,"value":3493},{"type":413,"tag":623,"props":8313,"children":8314},{"style":635},[8315],{"type":418,"value":8316}," latest\n",{"type":413,"tag":623,"props":8318,"children":8319},{"class":625,"line":7980},[8320],{"type":413,"tag":623,"props":8321,"children":8322},{"emptyLinePlaceholder":2790},[8323],{"type":418,"value":2793},{"type":413,"tag":623,"props":8325,"children":8326},{"class":625,"line":8009},[8327,8331,8335,8339],{"type":413,"tag":623,"props":8328,"children":8329},{"style":671},[8330],{"type":418,"value":7868},{"type":413,"tag":623,"props":8332,"children":8333},{"style":5513},[8334],{"type":418,"value":7873},{"type":413,"tag":623,"props":8336,"children":8337},{"style":671},[8338],{"type":418,"value":3493},{"type":413,"tag":623,"props":8340,"children":8341},{"style":635},[8342],{"type":418,"value":8343}," Set node version to 18\n",{"type":413,"tag":623,"props":8345,"children":8346},{"class":625,"line":8027},[8347,8351,8355],{"type":413,"tag":623,"props":8348,"children":8349},{"style":5513},[8350],{"type":418,"value":7898},{"type":413,"tag":623,"props":8352,"children":8353},{"style":671},[8354],{"type":418,"value":3493},{"type":413,"tag":623,"props":8356,"children":8357},{"style":635},[8358],{"type":418,"value":8359}," actions/setup-node@v3\n",{"type":413,"tag":623,"props":8361,"children":8362},{"class":625,"line":8036},[8363,8367],{"type":413,"tag":623,"props":8364,"children":8365},{"style":5513},[8366],{"type":418,"value":7915},{"type":413,"tag":623,"props":8368,"children":8369},{"style":671},[8370],{"type":418,"value":3448},{"type":413,"tag":623,"props":8372,"children":8374},{"class":625,"line":8373},24,[8375,8380,8384],{"type":413,"tag":623,"props":8376,"children":8377},{"style":5513},[8378],{"type":418,"value":8379},"          node-version",{"type":413,"tag":623,"props":8381,"children":8382},{"style":671},[8383],{"type":418,"value":3493},{"type":413,"tag":623,"props":8385,"children":8386},{"style":3551},[8387],{"type":418,"value":8388}," 18\n",{"type":413,"tag":623,"props":8390,"children":8392},{"class":625,"line":8391},25,[8393,8398,8402,8406,8410],{"type":413,"tag":623,"props":8394,"children":8395},{"style":5513},[8396],{"type":418,"value":8397},"          cache",{"type":413,"tag":623,"props":8399,"children":8400},{"style":671},[8401],{"type":418,"value":3493},{"type":413,"tag":623,"props":8403,"children":8404},{"style":671},[8405],{"type":418,"value":3583},{"type":413,"tag":623,"props":8407,"children":8408},{"style":635},[8409],{"type":418,"value":362},{"type":413,"tag":623,"props":8411,"children":8412},{"style":671},[8413],{"type":418,"value":3592},{"type":413,"tag":623,"props":8415,"children":8417},{"class":625,"line":8416},26,[8418],{"type":413,"tag":623,"props":8419,"children":8420},{"style":1058},[8421],{"type":418,"value":8422},"      \n",{"type":413,"tag":623,"props":8424,"children":8426},{"class":625,"line":8425},27,[8427,8431,8435,8439],{"type":413,"tag":623,"props":8428,"children":8429},{"style":671},[8430],{"type":418,"value":7868},{"type":413,"tag":623,"props":8432,"children":8433},{"style":5513},[8434],{"type":418,"value":7873},{"type":413,"tag":623,"props":8436,"children":8437},{"style":671},[8438],{"type":418,"value":3493},{"type":413,"tag":623,"props":8440,"children":8441},{"style":635},[8442],{"type":418,"value":8443}," Install dependencies\n",{"type":413,"tag":623,"props":8445,"children":8447},{"class":625,"line":8446},28,[8448,8452,8456],{"type":413,"tag":623,"props":8449,"children":8450},{"style":5513},[8451],{"type":418,"value":8015},{"type":413,"tag":623,"props":8453,"children":8454},{"style":671},[8455],{"type":418,"value":3493},{"type":413,"tag":623,"props":8457,"children":8458},{"style":635},[8459],{"type":418,"value":8460}," pnpm install\n",{"type":413,"tag":623,"props":8462,"children":8464},{"class":625,"line":8463},29,[8465],{"type":413,"tag":623,"props":8466,"children":8467},{"style":1058},[8468],{"type":418,"value":8422},{"type":413,"tag":623,"props":8470,"children":8472},{"class":625,"line":8471},30,[8473,8477,8481,8485],{"type":413,"tag":623,"props":8474,"children":8475},{"style":671},[8476],{"type":418,"value":7868},{"type":413,"tag":623,"props":8478,"children":8479},{"style":5513},[8480],{"type":418,"value":7873},{"type":413,"tag":623,"props":8482,"children":8483},{"style":671},[8484],{"type":418,"value":3493},{"type":413,"tag":623,"props":8486,"children":8487},{"style":635},[8488],{"type":418,"value":8489}," Provision infrastructure\n",{"type":413,"tag":623,"props":8491,"children":8493},{"class":625,"line":8492},31,[8494,8498,8502],{"type":413,"tag":623,"props":8495,"children":8496},{"style":5513},[8497],{"type":418,"value":7898},{"type":413,"tag":623,"props":8499,"children":8500},{"style":671},[8501],{"type":418,"value":3493},{"type":413,"tag":623,"props":8503,"children":8504},{"style":635},[8505],{"type":418,"value":8506}," pulumi/actions@v4.4.0\n",{"type":413,"tag":623,"props":8508,"children":8510},{"class":625,"line":8509},32,[8511,8516,8520],{"type":413,"tag":623,"props":8512,"children":8513},{"style":5513},[8514],{"type":418,"value":8515},"        id",{"type":413,"tag":623,"props":8517,"children":8518},{"style":671},[8519],{"type":418,"value":3493},{"type":413,"tag":623,"props":8521,"children":8522},{"style":635},[8523],{"type":418,"value":8524}," pulumi\n",{"type":413,"tag":623,"props":8526,"children":8528},{"class":625,"line":8527},33,[8529,8533],{"type":413,"tag":623,"props":8530,"children":8531},{"style":5513},[8532],{"type":418,"value":7915},{"type":413,"tag":623,"props":8534,"children":8535},{"style":671},[8536],{"type":418,"value":3448},{"type":413,"tag":623,"props":8538,"children":8540},{"class":625,"line":8539},34,[8541,8546,8550],{"type":413,"tag":623,"props":8542,"children":8543},{"style":5513},[8544],{"type":418,"value":8545},"          command",{"type":413,"tag":623,"props":8547,"children":8548},{"style":671},[8549],{"type":418,"value":3493},{"type":413,"tag":623,"props":8551,"children":8552},{"style":635},[8553],{"type":418,"value":8554}," up\n",{"type":413,"tag":623,"props":8556,"children":8558},{"class":625,"line":8557},35,[8559,8564,8568],{"type":413,"tag":623,"props":8560,"children":8561},{"style":5513},[8562],{"type":418,"value":8563},"          stack-name",{"type":413,"tag":623,"props":8565,"children":8566},{"style":671},[8567],{"type":418,"value":3493},{"type":413,"tag":623,"props":8569,"children":8570},{"style":635},[8571],{"type":418,"value":8572}," dev\n",{"type":413,"tag":623,"props":8574,"children":8576},{"class":625,"line":8575},36,[8577,8582],{"type":413,"tag":623,"props":8578,"children":8579},{"style":5513},[8580],{"type":418,"value":8581},"        env",{"type":413,"tag":623,"props":8583,"children":8584},{"style":671},[8585],{"type":418,"value":3448},{"type":413,"tag":623,"props":8587,"children":8589},{"class":625,"line":8588},37,[8590,8595,8599],{"type":413,"tag":623,"props":8591,"children":8592},{"style":5513},[8593],{"type":418,"value":8594},"          ARM_USE_OIDC",{"type":413,"tag":623,"props":8596,"children":8597},{"style":671},[8598],{"type":418,"value":3493},{"type":413,"tag":623,"props":8600,"children":8601},{"style":5580},[8602],{"type":418,"value":5583},{"type":413,"tag":623,"props":8604,"children":8606},{"class":625,"line":8605},38,[8607,8612,8616],{"type":413,"tag":623,"props":8608,"children":8609},{"style":5513},[8610],{"type":418,"value":8611},"          PULUMI_ACCESS_TOKEN",{"type":413,"tag":623,"props":8613,"children":8614},{"style":671},[8615],{"type":418,"value":3493},{"type":413,"tag":623,"props":8617,"children":8618},{"style":635},[8619],{"type":418,"value":8620}," ${{ secrets.PULUMI_ACCESS_TOKEN }}\n",{"type":413,"tag":623,"props":8622,"children":8624},{"class":625,"line":8623},39,[8625,8630,8634],{"type":413,"tag":623,"props":8626,"children":8627},{"style":5513},[8628],{"type":418,"value":8629},"          ARM_CLIENT_ID",{"type":413,"tag":623,"props":8631,"children":8632},{"style":671},[8633],{"type":418,"value":3493},{"type":413,"tag":623,"props":8635,"children":8636},{"style":635},[8637],{"type":418,"value":8638}," ${{ secrets.ARM_CLIENT_ID }}\n",{"type":413,"tag":623,"props":8640,"children":8642},{"class":625,"line":8641},40,[8643,8648,8652],{"type":413,"tag":623,"props":8644,"children":8645},{"style":5513},[8646],{"type":418,"value":8647},"          ARM_TENANT_ID",{"type":413,"tag":623,"props":8649,"children":8650},{"style":671},[8651],{"type":418,"value":3493},{"type":413,"tag":623,"props":8653,"children":8654},{"style":635},[8655],{"type":418,"value":8656}," ${{ secrets.ARM_TENANT_ID }}\n",{"type":413,"tag":623,"props":8658,"children":8660},{"class":625,"line":8659},41,[8661,8666,8670],{"type":413,"tag":623,"props":8662,"children":8663},{"style":5513},[8664],{"type":418,"value":8665},"          ARM_SUBSCRIPTION_ID",{"type":413,"tag":623,"props":8667,"children":8668},{"style":671},[8669],{"type":418,"value":3493},{"type":413,"tag":623,"props":8671,"children":8672},{"style":635},[8673],{"type":418,"value":8674}," ${{ secrets.ARM_SUBSCRIPTION_ID }}\n",{"type":413,"tag":414,"props":8676,"children":8677},{},[8678,8680,8686],{"type":418,"value":8679},"A permission section is required with 2 settings (more details ",{"type":413,"tag":432,"props":8681,"children":8684},{"href":8682,"rel":8683},"https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings",[436],[8685],{"type":418,"value":542},{"type":418,"value":8687},"):",{"type":413,"tag":443,"props":8689,"children":8690},{},[8691,8702],{"type":413,"tag":447,"props":8692,"children":8693},{},[8694,8700],{"type":413,"tag":619,"props":8695,"children":8697},{"className":8696},[],[8698],{"type":418,"value":8699},"id-token: write",{"type":418,"value":8701}," ➡️ needed to request the OIDC token",{"type":413,"tag":447,"props":8703,"children":8704},{},[8705,8711],{"type":413,"tag":619,"props":8706,"children":8708},{"className":8707},[],[8709],{"type":418,"value":8710},"contents: read",{"type":418,"value":8712}," ➡️ needed to perform checkout action",{"type":413,"tag":496,"props":8714,"children":8715},{"icon":498},[8716],{"type":413,"tag":414,"props":8717,"children":8718},{},[8719],{"type":418,"value":8720},"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":413,"tag":414,"props":8722,"children":8723},{},[8724,8726,8732,8734,8740,8742,8748],{"type":418,"value":8725},"The 3 steps following the checkout step are actions to specify the Node.js version to use, install and correctly configure ",{"type":413,"tag":432,"props":8727,"children":8730},{"href":8728,"rel":8729},"https://bordeauxcoders.com/series/pnpm-101",[436],[8731],{"type":418,"value":362},{"type":418,"value":8733},". 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":413,"tag":619,"props":8735,"children":8737},{"className":8736},[],[8738],{"type":418,"value":8739},"setup-dotnet",{"type":418,"value":8741}," and a ",{"type":413,"tag":619,"props":8743,"children":8745},{"className":8744},[],[8746],{"type":418,"value":8747},"dotnet retore",{"type":418,"value":8749}," action for .NET for instance).",{"type":413,"tag":414,"props":8751,"children":8752},{},[8753,8755,8760,8762,8767,8769,8775,8777,8782],{"type":418,"value":8754},"The last action is the Pulumi action to provision the infrastructure by running the ",{"type":413,"tag":619,"props":8756,"children":8758},{"className":8757},[],[8759],{"type":418,"value":4573},{"type":418,"value":8761}," on the ",{"type":413,"tag":619,"props":8763,"children":8765},{"className":8764},[],[8766],{"type":418,"value":747},{"type":418,"value":8768}," 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":413,"tag":619,"props":8770,"children":8772},{"className":8771},[],[8773],{"type":418,"value":8774},"ARM_USE_OIDC",{"type":418,"value":8776}," environment variable to ",{"type":413,"tag":619,"props":8778,"children":8780},{"className":8779},[],[8781],{"type":418,"value":5711},{"type":418,"value":1404},{"type":413,"tag":612,"props":8784,"children":8786},{"className":7701,"code":8785,"language":7703,"meta":401,"style":401},"        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",[8787],{"type":413,"tag":619,"props":8788,"children":8789},{"__ignoreMap":401},[8790,8801,8816,8831,8846,8861],{"type":413,"tag":623,"props":8791,"children":8792},{"class":625,"line":626},[8793,8797],{"type":413,"tag":623,"props":8794,"children":8795},{"style":5513},[8796],{"type":418,"value":8581},{"type":413,"tag":623,"props":8798,"children":8799},{"style":671},[8800],{"type":418,"value":3448},{"type":413,"tag":623,"props":8802,"children":8803},{"class":625,"line":1045},[8804,8808,8812],{"type":413,"tag":623,"props":8805,"children":8806},{"style":5513},[8807],{"type":418,"value":8594},{"type":413,"tag":623,"props":8809,"children":8810},{"style":671},[8811],{"type":418,"value":3493},{"type":413,"tag":623,"props":8813,"children":8814},{"style":5580},[8815],{"type":418,"value":5583},{"type":413,"tag":623,"props":8817,"children":8818},{"class":625,"line":1054},[8819,8823,8827],{"type":413,"tag":623,"props":8820,"children":8821},{"style":5513},[8822],{"type":418,"value":8611},{"type":413,"tag":623,"props":8824,"children":8825},{"style":671},[8826],{"type":418,"value":3493},{"type":413,"tag":623,"props":8828,"children":8829},{"style":635},[8830],{"type":418,"value":8620},{"type":413,"tag":623,"props":8832,"children":8833},{"class":625,"line":1087},[8834,8838,8842],{"type":413,"tag":623,"props":8835,"children":8836},{"style":5513},[8837],{"type":418,"value":8629},{"type":413,"tag":623,"props":8839,"children":8840},{"style":671},[8841],{"type":418,"value":3493},{"type":413,"tag":623,"props":8843,"children":8844},{"style":635},[8845],{"type":418,"value":8638},{"type":413,"tag":623,"props":8847,"children":8848},{"class":625,"line":1104},[8849,8853,8857],{"type":413,"tag":623,"props":8850,"children":8851},{"style":5513},[8852],{"type":418,"value":8647},{"type":413,"tag":623,"props":8854,"children":8855},{"style":671},[8856],{"type":418,"value":3493},{"type":413,"tag":623,"props":8858,"children":8859},{"style":635},[8860],{"type":418,"value":8656},{"type":413,"tag":623,"props":8862,"children":8863},{"class":625,"line":1113},[8864,8868,8872],{"type":413,"tag":623,"props":8865,"children":8866},{"style":5513},[8867],{"type":418,"value":8665},{"type":413,"tag":623,"props":8869,"children":8870},{"style":671},[8871],{"type":418,"value":3493},{"type":413,"tag":623,"props":8873,"children":8874},{"style":635},[8875],{"type":418,"value":8674},{"type":413,"tag":414,"props":8877,"children":8878},{},[8879,8881,8887,8889,8896],{"type":418,"value":8880},"A GitHub Actions secret we did not talk about is ",{"type":413,"tag":619,"props":8882,"children":8884},{"className":8883},[],[8885],{"type":418,"value":8886},"PULUMI_ACCESS_TOKEN",{"type":418,"value":8888}," that is a ",{"type":413,"tag":432,"props":8890,"children":8893},{"href":8891,"rel":8892},"https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/",[436],[8894],{"type":418,"value":8895},"Pulumi access token",{"type":418,"value":8897}," to use Pulumi Cloud as our backend to store the infrastructure state and encrypt secrets. This token should be:",{"type":413,"tag":6988,"props":8899,"children":8900},{},[8901,8913,8924],{"type":413,"tag":447,"props":8902,"children":8903},{},[8904,8906,8912],{"type":418,"value":8905},"Created from Pulumi Cloud (following the documentation ",{"type":413,"tag":432,"props":8907,"children":8910},{"href":8908,"rel":8909},"https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/#personal-access-tokens",[436],[8911],{"type":418,"value":542},{"type":418,"value":5595},{"type":413,"tag":447,"props":8914,"children":8915},{},[8916,8918],{"type":418,"value":8917},"Stored in the stack configuration using the following command ",{"type":413,"tag":619,"props":8919,"children":8921},{"className":8920},[],[8922],{"type":418,"value":8923},"pulumi config set pulumiTokenForRepository ******* --secret",{"type":413,"tag":447,"props":8925,"children":8926},{},[8927,8929],{"type":418,"value":8928},"Stored in a GitHub Actions secret using this code",{"type":413,"tag":612,"props":8930,"children":8932},{"className":5390,"code":8931,"language":357,"meta":401,"style":401},"new github.ActionsSecret(\"pulumiAccessToken\", {\n  repository: repository.name,\n  secretName: \"PULUMI_ACCESS_TOKEN\",\n  plaintextValue: config.requireSecret(\"pulumiTokenForRepository\"),\n});\n",[8933],{"type":413,"tag":619,"props":8934,"children":8935},{"__ignoreMap":401},[8936,8980,9007,9034,9083],{"type":413,"tag":623,"props":8937,"children":8938},{"class":625,"line":626},[8939,8943,8947,8951,8955,8959,8963,8968,8972,8976],{"type":413,"tag":623,"props":8940,"children":8941},{"style":671},[8942],{"type":418,"value":2857},{"type":413,"tag":623,"props":8944,"children":8945},{"style":1058},[8946],{"type":418,"value":5472},{"type":413,"tag":623,"props":8948,"children":8949},{"style":671},[8950],{"type":418,"value":1404},{"type":413,"tag":623,"props":8952,"children":8953},{"style":1407},[8954],{"type":418,"value":7247},{"type":413,"tag":623,"props":8956,"children":8957},{"style":1058},[8958],{"type":418,"value":1018},{"type":413,"tag":623,"props":8960,"children":8961},{"style":671},[8962],{"type":418,"value":1023},{"type":413,"tag":623,"props":8964,"children":8965},{"style":635},[8966],{"type":418,"value":8967},"pulumiAccessToken",{"type":413,"tag":623,"props":8969,"children":8970},{"style":671},[8971],{"type":418,"value":1023},{"type":413,"tag":623,"props":8973,"children":8974},{"style":671},[8975],{"type":418,"value":1037},{"type":413,"tag":623,"props":8977,"children":8978},{"style":671},[8979],{"type":418,"value":5507},{"type":413,"tag":623,"props":8981,"children":8982},{"class":625,"line":1045},[8983,8987,8991,8995,8999,9003],{"type":413,"tag":623,"props":8984,"children":8985},{"style":5513},[8986],{"type":418,"value":7069},{"type":413,"tag":623,"props":8988,"children":8989},{"style":671},[8990],{"type":418,"value":3493},{"type":413,"tag":623,"props":8992,"children":8993},{"style":1058},[8994],{"type":418,"value":1390},{"type":413,"tag":623,"props":8996,"children":8997},{"style":671},[8998],{"type":418,"value":1404},{"type":413,"tag":623,"props":9000,"children":9001},{"style":1058},[9002],{"type":418,"value":5665},{"type":413,"tag":623,"props":9004,"children":9005},{"style":671},[9006],{"type":418,"value":1084},{"type":413,"tag":623,"props":9008,"children":9009},{"class":625,"line":1054},[9010,9014,9018,9022,9026,9030],{"type":413,"tag":623,"props":9011,"children":9012},{"style":5513},[9013],{"type":418,"value":7306},{"type":413,"tag":623,"props":9015,"children":9016},{"style":671},[9017],{"type":418,"value":3493},{"type":413,"tag":623,"props":9019,"children":9020},{"style":671},[9021],{"type":418,"value":674},{"type":413,"tag":623,"props":9023,"children":9024},{"style":635},[9025],{"type":418,"value":8886},{"type":413,"tag":623,"props":9027,"children":9028},{"style":671},[9029],{"type":418,"value":1023},{"type":413,"tag":623,"props":9031,"children":9032},{"style":671},[9033],{"type":418,"value":1084},{"type":413,"tag":623,"props":9035,"children":9036},{"class":625,"line":1087},[9037,9041,9045,9049,9053,9058,9062,9066,9071,9075,9079],{"type":413,"tag":623,"props":9038,"children":9039},{"style":5513},[9040],{"type":418,"value":7334},{"type":413,"tag":623,"props":9042,"children":9043},{"style":671},[9044],{"type":418,"value":3493},{"type":413,"tag":623,"props":9046,"children":9047},{"style":1058},[9048],{"type":418,"value":879},{"type":413,"tag":623,"props":9050,"children":9051},{"style":671},[9052],{"type":418,"value":1404},{"type":413,"tag":623,"props":9054,"children":9055},{"style":1407},[9056],{"type":418,"value":9057},"requireSecret",{"type":413,"tag":623,"props":9059,"children":9060},{"style":1058},[9061],{"type":418,"value":1018},{"type":413,"tag":623,"props":9063,"children":9064},{"style":671},[9065],{"type":418,"value":1023},{"type":413,"tag":623,"props":9067,"children":9068},{"style":635},[9069],{"type":418,"value":9070},"pulumiTokenForRepository",{"type":413,"tag":623,"props":9072,"children":9073},{"style":671},[9074],{"type":418,"value":1023},{"type":413,"tag":623,"props":9076,"children":9077},{"style":1058},[9078],{"type":418,"value":5595},{"type":413,"tag":623,"props":9080,"children":9081},{"style":671},[9082],{"type":418,"value":1084},{"type":413,"tag":623,"props":9084,"children":9085},{"class":625,"line":1104},[9086,9090,9094],{"type":413,"tag":623,"props":9087,"children":9088},{"style":671},[9089],{"type":418,"value":3327},{"type":413,"tag":623,"props":9091,"children":9092},{"style":1058},[9093],{"type":418,"value":5595},{"type":413,"tag":623,"props":9095,"children":9096},{"style":671},[9097],{"type":418,"value":2524},{"type":413,"tag":414,"props":9099,"children":9100},{},[9101],{"type":418,"value":9102},"The last thing to do is to add this workflow file to the GitHub repository:",{"type":413,"tag":612,"props":9104,"children":9106},{"className":5390,"code":9105,"language":357,"meta":401,"style":401},"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",[9107],{"type":413,"tag":619,"props":9108,"children":9109},{"__ignoreMap":401},[9110,9151,9158,9220,9265,9292,9320,9349,9370,9398,9427,9456,9477],{"type":413,"tag":623,"props":9111,"children":9112},{"class":625,"line":626},[9113,9117,9121,9126,9130,9134,9138,9143,9147],{"type":413,"tag":623,"props":9114,"children":9115},{"style":2485},[9116],{"type":418,"value":5403},{"type":413,"tag":623,"props":9118,"children":9119},{"style":671},[9120],{"type":418,"value":5852},{"type":413,"tag":623,"props":9122,"children":9123},{"style":1058},[9124],{"type":418,"value":9125}," readFileSync",{"type":413,"tag":623,"props":9127,"children":9128},{"style":671},[9129],{"type":418,"value":5879},{"type":413,"tag":623,"props":9131,"children":9132},{"style":2485},[9133],{"type":418,"value":6443},{"type":413,"tag":623,"props":9135,"children":9136},{"style":671},[9137],{"type":418,"value":674},{"type":413,"tag":623,"props":9139,"children":9140},{"style":635},[9141],{"type":418,"value":9142},"fs",{"type":413,"tag":623,"props":9144,"children":9145},{"style":671},[9146],{"type":418,"value":1023},{"type":413,"tag":623,"props":9148,"children":9149},{"style":671},[9150],{"type":418,"value":2524},{"type":413,"tag":623,"props":9152,"children":9153},{"class":625,"line":1045},[9154],{"type":413,"tag":623,"props":9155,"children":9156},{"emptyLinePlaceholder":2790},[9157],{"type":418,"value":2793},{"type":413,"tag":623,"props":9159,"children":9160},{"class":625,"line":1054},[9161,9165,9170,9174,9178,9182,9186,9191,9195,9199,9203,9208,9212,9216],{"type":413,"tag":623,"props":9162,"children":9163},{"style":2854},[9164],{"type":418,"value":5454},{"type":413,"tag":623,"props":9166,"children":9167},{"style":1058},[9168],{"type":418,"value":9169}," pipelineContent ",{"type":413,"tag":623,"props":9171,"children":9172},{"style":671},[9173],{"type":418,"value":1066},{"type":413,"tag":623,"props":9175,"children":9176},{"style":1407},[9177],{"type":418,"value":9125},{"type":413,"tag":623,"props":9179,"children":9180},{"style":1058},[9181],{"type":418,"value":1018},{"type":413,"tag":623,"props":9183,"children":9184},{"style":671},[9185],{"type":418,"value":1023},{"type":413,"tag":623,"props":9187,"children":9188},{"style":635},[9189],{"type":418,"value":9190},"main.yml",{"type":413,"tag":623,"props":9192,"children":9193},{"style":671},[9194],{"type":418,"value":1023},{"type":413,"tag":623,"props":9196,"children":9197},{"style":671},[9198],{"type":418,"value":1037},{"type":413,"tag":623,"props":9200,"children":9201},{"style":671},[9202],{"type":418,"value":674},{"type":413,"tag":623,"props":9204,"children":9205},{"style":635},[9206],{"type":418,"value":9207},"utf-8",{"type":413,"tag":623,"props":9209,"children":9210},{"style":671},[9211],{"type":418,"value":1023},{"type":413,"tag":623,"props":9213,"children":9214},{"style":1058},[9215],{"type":418,"value":5595},{"type":413,"tag":623,"props":9217,"children":9218},{"style":671},[9219],{"type":418,"value":2524},{"type":413,"tag":623,"props":9221,"children":9222},{"class":625,"line":1087},[9223,9227,9231,9235,9240,9244,9248,9253,9257,9261],{"type":413,"tag":623,"props":9224,"children":9225},{"style":671},[9226],{"type":418,"value":2857},{"type":413,"tag":623,"props":9228,"children":9229},{"style":1058},[9230],{"type":418,"value":5472},{"type":413,"tag":623,"props":9232,"children":9233},{"style":671},[9234],{"type":418,"value":1404},{"type":413,"tag":623,"props":9236,"children":9237},{"style":1407},[9238],{"type":418,"value":9239},"RepositoryFile",{"type":413,"tag":623,"props":9241,"children":9242},{"style":1058},[9243],{"type":418,"value":1018},{"type":413,"tag":623,"props":9245,"children":9246},{"style":671},[9247],{"type":418,"value":1023},{"type":413,"tag":623,"props":9249,"children":9250},{"style":635},[9251],{"type":418,"value":9252},"pipelineRepositoryFile",{"type":413,"tag":623,"props":9254,"children":9255},{"style":671},[9256],{"type":418,"value":1023},{"type":413,"tag":623,"props":9258,"children":9259},{"style":671},[9260],{"type":418,"value":1037},{"type":413,"tag":623,"props":9262,"children":9263},{"style":671},[9264],{"type":418,"value":5507},{"type":413,"tag":623,"props":9266,"children":9267},{"class":625,"line":1104},[9268,9272,9276,9280,9284,9288],{"type":413,"tag":623,"props":9269,"children":9270},{"style":5513},[9271],{"type":418,"value":7069},{"type":413,"tag":623,"props":9273,"children":9274},{"style":671},[9275],{"type":418,"value":3493},{"type":413,"tag":623,"props":9277,"children":9278},{"style":1058},[9279],{"type":418,"value":1390},{"type":413,"tag":623,"props":9281,"children":9282},{"style":671},[9283],{"type":418,"value":1404},{"type":413,"tag":623,"props":9285,"children":9286},{"style":1058},[9287],{"type":418,"value":5665},{"type":413,"tag":623,"props":9289,"children":9290},{"style":671},[9291],{"type":418,"value":1084},{"type":413,"tag":623,"props":9293,"children":9294},{"class":625,"line":1113},[9295,9300,9304,9308,9312,9316],{"type":413,"tag":623,"props":9296,"children":9297},{"style":5513},[9298],{"type":418,"value":9299},"  branch",{"type":413,"tag":623,"props":9301,"children":9302},{"style":671},[9303],{"type":418,"value":3493},{"type":413,"tag":623,"props":9305,"children":9306},{"style":671},[9307],{"type":418,"value":674},{"type":413,"tag":623,"props":9309,"children":9310},{"style":635},[9311],{"type":418,"value":4782},{"type":413,"tag":623,"props":9313,"children":9314},{"style":671},[9315],{"type":418,"value":1023},{"type":413,"tag":623,"props":9317,"children":9318},{"style":671},[9319],{"type":418,"value":1084},{"type":413,"tag":623,"props":9321,"children":9322},{"class":625,"line":1161},[9323,9328,9332,9336,9341,9345],{"type":413,"tag":623,"props":9324,"children":9325},{"style":5513},[9326],{"type":418,"value":9327},"  file",{"type":413,"tag":623,"props":9329,"children":9330},{"style":671},[9331],{"type":418,"value":3493},{"type":413,"tag":623,"props":9333,"children":9334},{"style":671},[9335],{"type":418,"value":674},{"type":413,"tag":623,"props":9337,"children":9338},{"style":635},[9339],{"type":418,"value":9340},".github/workflows/main.yml",{"type":413,"tag":623,"props":9342,"children":9343},{"style":671},[9344],{"type":418,"value":1023},{"type":413,"tag":623,"props":9346,"children":9347},{"style":671},[9348],{"type":418,"value":1084},{"type":413,"tag":623,"props":9350,"children":9351},{"class":625,"line":1207},[9352,9357,9361,9366],{"type":413,"tag":623,"props":9353,"children":9354},{"style":5513},[9355],{"type":418,"value":9356},"  content",{"type":413,"tag":623,"props":9358,"children":9359},{"style":671},[9360],{"type":418,"value":3493},{"type":413,"tag":623,"props":9362,"children":9363},{"style":1058},[9364],{"type":418,"value":9365}," pipelineContent",{"type":413,"tag":623,"props":9367,"children":9368},{"style":671},[9369],{"type":418,"value":1084},{"type":413,"tag":623,"props":9371,"children":9372},{"class":625,"line":1251},[9373,9378,9382,9386,9390,9394],{"type":413,"tag":623,"props":9374,"children":9375},{"style":5513},[9376],{"type":418,"value":9377},"  commitMessage",{"type":413,"tag":623,"props":9379,"children":9380},{"style":671},[9381],{"type":418,"value":3493},{"type":413,"tag":623,"props":9383,"children":9384},{"style":671},[9385],{"type":418,"value":674},{"type":413,"tag":623,"props":9387,"children":9388},{"style":635},[9389],{"type":418,"value":3841},{"type":413,"tag":623,"props":9391,"children":9392},{"style":671},[9393],{"type":418,"value":1023},{"type":413,"tag":623,"props":9395,"children":9396},{"style":671},[9397],{"type":418,"value":1084},{"type":413,"tag":623,"props":9399,"children":9400},{"class":625,"line":1296},[9401,9406,9410,9414,9419,9423],{"type":413,"tag":623,"props":9402,"children":9403},{"style":5513},[9404],{"type":418,"value":9405},"  commitAuthor",{"type":413,"tag":623,"props":9407,"children":9408},{"style":671},[9409],{"type":418,"value":3493},{"type":413,"tag":623,"props":9411,"children":9412},{"style":671},[9413],{"type":418,"value":674},{"type":413,"tag":623,"props":9415,"children":9416},{"style":635},[9417],{"type":418,"value":9418},"Alexandre Nédélec",{"type":413,"tag":623,"props":9420,"children":9421},{"style":671},[9422],{"type":418,"value":1023},{"type":413,"tag":623,"props":9424,"children":9425},{"style":671},[9426],{"type":418,"value":1084},{"type":413,"tag":623,"props":9428,"children":9429},{"class":625,"line":1337},[9430,9435,9439,9443,9448,9452],{"type":413,"tag":623,"props":9431,"children":9432},{"style":5513},[9433],{"type":418,"value":9434},"  commitEmail",{"type":413,"tag":623,"props":9436,"children":9437},{"style":671},[9438],{"type":418,"value":3493},{"type":413,"tag":623,"props":9440,"children":9441},{"style":671},[9442],{"type":418,"value":674},{"type":413,"tag":623,"props":9444,"children":9445},{"style":635},[9446],{"type":418,"value":9447},"15186176+TechWatching@users.noreply.github.com",{"type":413,"tag":623,"props":9449,"children":9450},{"style":671},[9451],{"type":418,"value":1023},{"type":413,"tag":623,"props":9453,"children":9454},{"style":671},[9455],{"type":418,"value":1084},{"type":413,"tag":623,"props":9457,"children":9458},{"class":625,"line":1346},[9459,9464,9468,9473],{"type":413,"tag":623,"props":9460,"children":9461},{"style":5513},[9462],{"type":418,"value":9463},"  overwriteOnCreate",{"type":413,"tag":623,"props":9465,"children":9466},{"style":671},[9467],{"type":418,"value":3493},{"type":413,"tag":623,"props":9469,"children":9470},{"style":5580},[9471],{"type":418,"value":9472}," true",{"type":413,"tag":623,"props":9474,"children":9475},{"style":671},[9476],{"type":418,"value":1084},{"type":413,"tag":623,"props":9478,"children":9479},{"class":625,"line":2100},[9480,9484,9488],{"type":413,"tag":623,"props":9481,"children":9482},{"style":671},[9483],{"type":418,"value":3327},{"type":413,"tag":623,"props":9485,"children":9486},{"style":1058},[9487],{"type":418,"value":5595},{"type":413,"tag":623,"props":9489,"children":9490},{"style":671},[9491],{"type":418,"value":2524},{"type":413,"tag":414,"props":9493,"children":9494},{},[9495],{"type":418,"value":9496},"This code:",{"type":413,"tag":6988,"props":9498,"children":9499},{},[9500,9512,9524],{"type":413,"tag":447,"props":9501,"children":9502},{},[9503,9505,9510],{"type":418,"value":9504},"reads the ",{"type":413,"tag":619,"props":9506,"children":9508},{"className":9507},[],[9509],{"type":418,"value":9190},{"type":418,"value":9511}," file that contains the workflow we saw previously",{"type":413,"tag":447,"props":9513,"children":9514},{},[9515,9517,9522],{"type":418,"value":9516},"creates a file with this content in the repository in the ",{"type":413,"tag":619,"props":9518,"children":9520},{"className":9519},[],[9521],{"type":418,"value":4800},{"type":418,"value":9523}," folder for the GitHub Actions workflows",{"type":413,"tag":447,"props":9525,"children":9526},{},[9527],{"type":418,"value":9528},"makes a commit when creating the file (or modifying it)",{"type":413,"tag":496,"props":9530,"children":9531},{"icon":557},[9532],{"type":413,"tag":414,"props":9533,"children":9534},{},[9535,9537,9543,9545,9550],{"type":418,"value":9536},"To read the YAML file, I use the ",{"type":413,"tag":619,"props":9538,"children":9540},{"className":9539},[],[9541],{"type":418,"value":9542},"readFileSync",{"type":418,"value":9544}," method from the File System API ",{"type":413,"tag":619,"props":9546,"children":9548},{"className":9547},[],[9549],{"type":418,"value":9142},{"type":418,"value":9551},". 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":413,"tag":420,"props":9553,"children":9555},{"id":9554},"test-the-azure-ready-github-repository",[9556],{"type":418,"value":9557},"Test the Azure-Ready GitHub Repository",{"type":413,"tag":414,"props":9559,"children":9560},{},[9561,9563,9568],{"type":418,"value":9562},"Now that the infrastructure code to provision the Azure-Ready GitHub repository is written, let's run it with the ",{"type":413,"tag":619,"props":9564,"children":9566},{"className":9565},[],[9567],{"type":418,"value":4573},{"type":418,"value":9569}," command and see if it works!",{"type":413,"tag":414,"props":9571,"children":9572},{},[9573],{"type":413,"tag":487,"props":9574,"children":9578},{"alt":9575,"className":9576,"src":9577,"width":591},"Ouput of the pulumi up command with all the resources created.",[491,492],"/posts/images/azurereadygithub_pulumi_1.webp",[],{"type":413,"tag":414,"props":9580,"children":9581},{},[9582],{"type":418,"value":9583},"All the resources are correctly created and our new GitHub repository is ready to be used.",{"type":413,"tag":414,"props":9585,"children":9586},{},[9587],{"type":413,"tag":487,"props":9588,"children":9592},{"alt":9589,"className":9590,"src":9591},"Picture of the Azure Ready GitHub repository",[491,492],"/posts/images/azurereadygithub_github_2.webp",[],{"type":413,"tag":414,"props":9594,"children":9595},{},[9596],{"type":418,"value":9597},"Let's clone it.",{"type":413,"tag":612,"props":9599,"children":9601},{"className":614,"code":9600,"language":616,"meta":401,"style":401},"git clone https://github.com/TechWatching/azure-ready-repository; cd azure-ready-repository\n",[9602],{"type":413,"tag":619,"props":9603,"children":9604},{"__ignoreMap":401},[9605],{"type":413,"tag":623,"props":9606,"children":9607},{"class":625,"line":626},[9608,9612,9617,9622,9627,9632],{"type":413,"tag":623,"props":9609,"children":9610},{"style":630},[9611],{"type":418,"value":241},{"type":413,"tag":623,"props":9613,"children":9614},{"style":635},[9615],{"type":418,"value":9616}," clone",{"type":413,"tag":623,"props":9618,"children":9619},{"style":635},[9620],{"type":418,"value":9621}," https://github.com/TechWatching/azure-ready-repository",{"type":413,"tag":623,"props":9623,"children":9624},{"style":671},[9625],{"type":418,"value":9626},";",{"type":413,"tag":623,"props":9628,"children":9629},{"style":1407},[9630],{"type":418,"value":9631}," cd",{"type":413,"tag":623,"props":9633,"children":9634},{"style":635},[9635],{"type":418,"value":9636}," azure-ready-repository\n",{"type":413,"tag":414,"props":9638,"children":9639},{},[9640],{"type":418,"value":9641},"We want to verify that the GitHub project is properly configured and can provision Azure resources from its GitHub Actions workflow.",{"type":413,"tag":414,"props":9643,"children":9644},{},[9645],{"type":418,"value":9646},"Let's add some infrastructure code that provisions a few Azure resources to check that:",{"type":413,"tag":612,"props":9648,"children":9650},{"className":614,"code":9649,"language":616,"meta":401,"style":401},"pulumi new azure-typescript -n \"AzureReadyGitHuRepository\" -y --force\n",[9651],{"type":413,"tag":619,"props":9652,"children":9653},{"__ignoreMap":401},[9654],{"type":413,"tag":623,"props":9655,"children":9656},{"class":625,"line":626},[9657,9661,9665,9670,9674,9678,9683,9687,9692],{"type":413,"tag":623,"props":9658,"children":9659},{"style":630},[9660],{"type":418,"value":311},{"type":413,"tag":623,"props":9662,"children":9663},{"style":635},[9664],{"type":418,"value":638},{"type":413,"tag":623,"props":9666,"children":9667},{"style":635},[9668],{"type":418,"value":9669}," azure-typescript",{"type":413,"tag":623,"props":9671,"children":9672},{"style":635},[9673],{"type":418,"value":648},{"type":413,"tag":623,"props":9675,"children":9676},{"style":671},[9677],{"type":418,"value":674},{"type":413,"tag":623,"props":9679,"children":9680},{"style":635},[9681],{"type":418,"value":9682},"AzureReadyGitHuRepository",{"type":413,"tag":623,"props":9684,"children":9685},{"style":671},[9686],{"type":418,"value":1023},{"type":413,"tag":623,"props":9688,"children":9689},{"style":635},[9690],{"type":418,"value":9691}," -y",{"type":413,"tag":623,"props":9693,"children":9694},{"style":635},[9695],{"type":418,"value":9696}," --force\n",{"type":413,"tag":414,"props":9698,"children":9699},{},[9700,9701,9707],{"type":418,"value":6304},{"type":413,"tag":619,"props":9702,"children":9704},{"className":9703},[],[9705],{"type":418,"value":9706},"--force",{"type":418,"value":9708}," option allows us to create the code within a non-empty directory.",{"type":413,"tag":414,"props":9710,"children":9711},{},[9712,9714,9720],{"type":418,"value":9713},"I used the ",{"type":413,"tag":619,"props":9715,"children":9717},{"className":9716},[],[9718],{"type":418,"value":9719},"azure-typescript",{"type":418,"value":9721}," template that creates a storage account and outputs retrieve its primary access key.",{"type":413,"tag":496,"props":9723,"children":9724},{"icon":952},[9725],{"type":413,"tag":414,"props":9726,"children":9727},{},[9728,9730,9737],{"type":418,"value":9729},"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":413,"tag":432,"props":9731,"children":9734},{"href":9732,"rel":9733},"https://github.com/pulumi/pulumi-azure-native/issues/2408",[436],[9735],{"type":418,"value":9736},"open issue",{"type":418,"value":9738}," to change that but in the meantime, I have just modified the code to label the stack output as secret ensuring its encryption.",{"type":413,"tag":414,"props":9740,"children":9741},{},[9742,9744,9750,9752,9758],{"type":418,"value":9743},"Let's run a ",{"type":413,"tag":619,"props":9745,"children":9747},{"className":9746},[],[9748],{"type":418,"value":9749},"pnpm install",{"type":418,"value":9751}," to install the dependencies and generate the ",{"type":413,"tag":619,"props":9753,"children":9755},{"className":9754},[],[9756],{"type":418,"value":9757},"pnpm-lock.yaml",{"type":418,"value":9759}," file. Then, we can push the code to GitHub and run the pipeline to see how it goes.",{"type":413,"tag":414,"props":9761,"children":9762},{},[9763],{"type":413,"tag":487,"props":9764,"children":9768},{"alt":9765,"className":9766,"src":9767},"Logs of the pipeline run showing that the workflow successfully created a storage account.",[491,492],"/posts/images/azurereadygithub_github_3.webp",[],{"type":413,"tag":414,"props":9770,"children":9771},{},[9772],{"type":418,"value":9773},"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":413,"tag":420,"props":9775,"children":9776},{"id":4624},[9777],{"type":418,"value":4627},{"type":413,"tag":600,"props":9779,"children":9781},{"id":9780},"additional-information",[9782],{"type":418,"value":9783},"Additional information",{"type":413,"tag":414,"props":9785,"children":9786},{},[9787],{"type":418,"value":9788},"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":413,"tag":414,"props":9790,"children":9791},{},[9792],{"type":418,"value":9793},"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":413,"tag":600,"props":9795,"children":9797},{"id":9796},"potential-enhancements",[9798],{"type":418,"value":9799},"Potential Enhancements",{"type":413,"tag":414,"props":9801,"children":9802},{},[9803],{"type":418,"value":9804},"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":413,"tag":443,"props":9806,"children":9807},{},[9808,9813,9818],{"type":413,"tag":447,"props":9809,"children":9810},{},[9811],{"type":418,"value":9812},"make additional items, such as the commit author, configurable",{"type":413,"tag":447,"props":9814,"children":9815},{},[9816],{"type":418,"value":9817},"authorize an environment and not only a branch to retrieve an Azure token",{"type":413,"tag":447,"props":9819,"children":9820},{},[9821],{"type":418,"value":9822},"use environment variables/secrets instead of variable/secrets at the repository scope",{"type":413,"tag":414,"props":9824,"children":9825},{},[9826],{"type":418,"value":9827},"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":413,"tag":600,"props":9829,"children":9831},{"id":9830},"related-articles",[9832],{"type":418,"value":9833},"Related articles",{"type":413,"tag":414,"props":9835,"children":9836},{},[9837],{"type":418,"value":9838},"Here are some articles on the same topic I wanted to mention:",{"type":413,"tag":443,"props":9840,"children":9841},{},[9842,9873,9901],{"type":413,"tag":447,"props":9843,"children":9844},{},[9845,9855,9857,9862,9866,9871],{"type":413,"tag":432,"props":9846,"children":9849},{"href":9847,"rel":9848},"https://leebriggs.co.uk/blog/2022/01/23/gha-cloud-credentials",[436],[9850],{"type":413,"tag":521,"props":9851,"children":9852},{},[9853],{"type":418,"value":9854},"Stop using static cloud credentials in GitHub Actions",{"type":418,"value":9856}," ",{"type":413,"tag":521,"props":9858,"children":9859},{},[9860],{"type":418,"value":9861},"by Lee Briggs",{"type":413,"tag":9863,"props":9864,"children":9865},"br",{},[],{"type":413,"tag":521,"props":9867,"children":9868},{},[9869],{"type":418,"value":9870},"➡️",{"type":418,"value":9872}," 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":413,"tag":447,"props":9874,"children":9875},{},[9876,9886,9887,9892,9895,9899],{"type":413,"tag":432,"props":9877,"children":9880},{"href":9878,"rel":9879},"https://xaviergeerinck.com/2023/05/16/configuring-github-actions-to-azure-authentication-with-oidc/",[436],[9881],{"type":413,"tag":521,"props":9882,"children":9883},{},[9884],{"type":418,"value":9885},"Configuring GitHub Actions to Azure authentication with OIDC",{"type":418,"value":9856},{"type":413,"tag":521,"props":9888,"children":9889},{},[9890],{"type":418,"value":9891},"by Xavier Geerinck",{"type":413,"tag":9863,"props":9893,"children":9894},{},[],{"type":413,"tag":521,"props":9896,"children":9897},{},[9898],{"type":418,"value":9870},{"type":418,"value":9900},"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":413,"tag":447,"props":9902,"children":9903},{},[9904,9914,9915,9920,9923],{"type":413,"tag":432,"props":9905,"children":9908},{"href":9906,"rel":9907},"https://samcogan.com/getting-rid-of-passwords-for-deployment-with-pulumi-oidc-support/",[436],[9909],{"type":413,"tag":521,"props":9910,"children":9911},{},[9912],{"type":418,"value":9913},"Getting Rid of Passwords for Deployment with Pulumi OIDC Support",{"type":418,"value":9856},{"type":413,"tag":521,"props":9916,"children":9917},{},[9918],{"type":418,"value":9919},"by Sam Cogan",{"type":413,"tag":9863,"props":9921,"children":9922},{},[],{"type":418,"value":9924},"\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":413,"tag":600,"props":9926,"children":9928},{"id":9927},"complete-code-solution",[9929],{"type":418,"value":9930},"Complete code solution",{"type":413,"tag":414,"props":9932,"children":9933},{},[9934],{"type":418,"value":9935},"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":413,"tag":414,"props":9937,"children":9938},{},[9939],{"type":418,"value":9940},"Anyway, now that we've covered everything, here is the complete code, which is just 75 lines long:",{"type":413,"tag":612,"props":9942,"children":9944},{"className":5390,"code":9943,"language":357,"meta":401,"style":401},"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",[9945],{"type":413,"tag":619,"props":9946,"children":9947},{"__ignoreMap":401},[9948,9989,10028,10067,10106,10145,10184,10191,10230,10237,10292,10319,10346,10361,10376,10383,10418,10425,10512,10567,10594,10609,10652,10679,10706,10733,10768,10795,10858,10873,10880,10927,10958,10965,11008,11035,11070,11097,11144,11159,11166,11209,11237,11265,11293,11309,11317,11361,11389,11417,11445,11461,11469,11513,11541,11569,11597,11613,11621,11665,11693,11721,11769,11785,11793,11853,11897,11925,11953,11981,12001,12029,12057,12085,12105],{"type":413,"tag":623,"props":9949,"children":9950},{"class":625,"line":626},[9951,9955,9959,9963,9968,9972,9976,9981,9985],{"type":413,"tag":623,"props":9952,"children":9953},{"style":2485},[9954],{"type":418,"value":5403},{"type":413,"tag":623,"props":9956,"children":9957},{"style":671},[9958],{"type":418,"value":5408},{"type":413,"tag":623,"props":9960,"children":9961},{"style":2485},[9962],{"type":418,"value":5413},{"type":413,"tag":623,"props":9964,"children":9965},{"style":1058},[9966],{"type":418,"value":9967}," pulumi ",{"type":413,"tag":623,"props":9969,"children":9970},{"style":2485},[9971],{"type":418,"value":5423},{"type":413,"tag":623,"props":9973,"children":9974},{"style":671},[9975],{"type":418,"value":674},{"type":413,"tag":623,"props":9977,"children":9978},{"style":635},[9979],{"type":418,"value":9980},"@pulumi/pulumi",{"type":413,"tag":623,"props":9982,"children":9983},{"style":671},[9984],{"type":418,"value":1023},{"type":413,"tag":623,"props":9986,"children":9987},{"style":671},[9988],{"type":418,"value":2524},{"type":413,"tag":623,"props":9990,"children":9991},{"class":625,"line":1045},[9992,9996,10000,10004,10008,10012,10016,10020,10024],{"type":413,"tag":623,"props":9993,"children":9994},{"style":2485},[9995],{"type":418,"value":5403},{"type":413,"tag":623,"props":9997,"children":9998},{"style":671},[9999],{"type":418,"value":5408},{"type":413,"tag":623,"props":10001,"children":10002},{"style":2485},[10003],{"type":418,"value":5413},{"type":413,"tag":623,"props":10005,"children":10006},{"style":1058},[10007],{"type":418,"value":5418},{"type":413,"tag":623,"props":10009,"children":10010},{"style":2485},[10011],{"type":418,"value":5423},{"type":413,"tag":623,"props":10013,"children":10014},{"style":671},[10015],{"type":418,"value":674},{"type":413,"tag":623,"props":10017,"children":10018},{"style":635},[10019],{"type":418,"value":5313},{"type":413,"tag":623,"props":10021,"children":10022},{"style":671},[10023],{"type":418,"value":1023},{"type":413,"tag":623,"props":10025,"children":10026},{"style":671},[10027],{"type":418,"value":2524},{"type":413,"tag":623,"props":10029,"children":10030},{"class":625,"line":1054},[10031,10035,10039,10043,10047,10051,10055,10059,10063],{"type":413,"tag":623,"props":10032,"children":10033},{"style":2485},[10034],{"type":418,"value":5403},{"type":413,"tag":623,"props":10036,"children":10037},{"style":671},[10038],{"type":418,"value":5408},{"type":413,"tag":623,"props":10040,"children":10041},{"style":2485},[10042],{"type":418,"value":5413},{"type":413,"tag":623,"props":10044,"children":10045},{"style":1058},[10046],{"type":418,"value":5765},{"type":413,"tag":623,"props":10048,"children":10049},{"style":2485},[10050],{"type":418,"value":5423},{"type":413,"tag":623,"props":10052,"children":10053},{"style":671},[10054],{"type":418,"value":674},{"type":413,"tag":623,"props":10056,"children":10057},{"style":635},[10058],{"type":418,"value":5308},{"type":413,"tag":623,"props":10060,"children":10061},{"style":671},[10062],{"type":418,"value":1023},{"type":413,"tag":623,"props":10064,"children":10065},{"style":671},[10066],{"type":418,"value":2524},{"type":413,"tag":623,"props":10068,"children":10069},{"class":625,"line":1087},[10070,10074,10078,10082,10086,10090,10094,10098,10102],{"type":413,"tag":623,"props":10071,"children":10072},{"style":2485},[10073],{"type":418,"value":5403},{"type":413,"tag":623,"props":10075,"children":10076},{"style":671},[10077],{"type":418,"value":5408},{"type":413,"tag":623,"props":10079,"children":10080},{"style":2485},[10081],{"type":418,"value":5413},{"type":413,"tag":623,"props":10083,"children":10084},{"style":1058},[10085],{"type":418,"value":6397},{"type":413,"tag":623,"props":10087,"children":10088},{"style":2485},[10089],{"type":418,"value":5423},{"type":413,"tag":623,"props":10091,"children":10092},{"style":671},[10093],{"type":418,"value":674},{"type":413,"tag":623,"props":10095,"children":10096},{"style":635},[10097],{"type":418,"value":6410},{"type":413,"tag":623,"props":10099,"children":10100},{"style":671},[10101],{"type":418,"value":1023},{"type":413,"tag":623,"props":10103,"children":10104},{"style":671},[10105],{"type":418,"value":2524},{"type":413,"tag":623,"props":10107,"children":10108},{"class":625,"line":1104},[10109,10113,10117,10121,10125,10129,10133,10137,10141],{"type":413,"tag":623,"props":10110,"children":10111},{"style":2485},[10112],{"type":418,"value":5403},{"type":413,"tag":623,"props":10114,"children":10115},{"style":671},[10116],{"type":418,"value":5852},{"type":413,"tag":623,"props":10118,"children":10119},{"style":1058},[10120],{"type":418,"value":6434},{"type":413,"tag":623,"props":10122,"children":10123},{"style":671},[10124],{"type":418,"value":5879},{"type":413,"tag":623,"props":10126,"children":10127},{"style":2485},[10128],{"type":418,"value":6443},{"type":413,"tag":623,"props":10130,"children":10131},{"style":671},[10132],{"type":418,"value":674},{"type":413,"tag":623,"props":10134,"children":10135},{"style":635},[10136],{"type":418,"value":6452},{"type":413,"tag":623,"props":10138,"children":10139},{"style":671},[10140],{"type":418,"value":1023},{"type":413,"tag":623,"props":10142,"children":10143},{"style":671},[10144],{"type":418,"value":2524},{"type":413,"tag":623,"props":10146,"children":10147},{"class":625,"line":1113},[10148,10152,10156,10160,10164,10168,10172,10176,10180],{"type":413,"tag":623,"props":10149,"children":10150},{"style":2485},[10151],{"type":418,"value":5403},{"type":413,"tag":623,"props":10153,"children":10154},{"style":671},[10155],{"type":418,"value":5852},{"type":413,"tag":623,"props":10157,"children":10158},{"style":1058},[10159],{"type":418,"value":9125},{"type":413,"tag":623,"props":10161,"children":10162},{"style":671},[10163],{"type":418,"value":5879},{"type":413,"tag":623,"props":10165,"children":10166},{"style":2485},[10167],{"type":418,"value":6443},{"type":413,"tag":623,"props":10169,"children":10170},{"style":671},[10171],{"type":418,"value":674},{"type":413,"tag":623,"props":10173,"children":10174},{"style":635},[10175],{"type":418,"value":9142},{"type":413,"tag":623,"props":10177,"children":10178},{"style":671},[10179],{"type":418,"value":1023},{"type":413,"tag":623,"props":10181,"children":10182},{"style":671},[10183],{"type":418,"value":2524},{"type":413,"tag":623,"props":10185,"children":10186},{"class":625,"line":1161},[10187],{"type":413,"tag":623,"props":10188,"children":10189},{"emptyLinePlaceholder":2790},[10190],{"type":418,"value":2793},{"type":413,"tag":623,"props":10192,"children":10193},{"class":625,"line":1207},[10194,10198,10202,10206,10210,10214,10218,10222,10226],{"type":413,"tag":623,"props":10195,"children":10196},{"style":2854},[10197],{"type":418,"value":5454},{"type":413,"tag":623,"props":10199,"children":10200},{"style":1058},[10201],{"type":418,"value":6701},{"type":413,"tag":623,"props":10203,"children":10204},{"style":671},[10205],{"type":418,"value":1066},{"type":413,"tag":623,"props":10207,"children":10208},{"style":671},[10209],{"type":418,"value":638},{"type":413,"tag":623,"props":10211,"children":10212},{"style":1058},[10213],{"type":418,"value":6229},{"type":413,"tag":623,"props":10215,"children":10216},{"style":671},[10217],{"type":418,"value":1404},{"type":413,"tag":623,"props":10219,"children":10220},{"style":1407},[10221],{"type":418,"value":6722},{"type":413,"tag":623,"props":10223,"children":10224},{"style":1058},[10225],{"type":418,"value":6727},{"type":413,"tag":623,"props":10227,"children":10228},{"style":671},[10229],{"type":418,"value":2524},{"type":413,"tag":623,"props":10231,"children":10232},{"class":625,"line":1251},[10233],{"type":413,"tag":623,"props":10234,"children":10235},{"emptyLinePlaceholder":2790},[10236],{"type":418,"value":2793},{"type":413,"tag":623,"props":10238,"children":10239},{"class":625,"line":1296},[10240,10244,10248,10252,10256,10260,10264,10268,10272,10276,10280,10284,10288],{"type":413,"tag":623,"props":10241,"children":10242},{"style":2854},[10243],{"type":418,"value":5454},{"type":413,"tag":623,"props":10245,"children":10246},{"style":1058},[10247],{"type":418,"value":5459},{"type":413,"tag":623,"props":10249,"children":10250},{"style":671},[10251],{"type":418,"value":1066},{"type":413,"tag":623,"props":10253,"children":10254},{"style":671},[10255],{"type":418,"value":638},{"type":413,"tag":623,"props":10257,"children":10258},{"style":1058},[10259],{"type":418,"value":5472},{"type":413,"tag":623,"props":10261,"children":10262},{"style":671},[10263],{"type":418,"value":1404},{"type":413,"tag":623,"props":10265,"children":10266},{"style":1407},[10267],{"type":418,"value":5481},{"type":413,"tag":623,"props":10269,"children":10270},{"style":1058},[10271],{"type":418,"value":1018},{"type":413,"tag":623,"props":10273,"children":10274},{"style":671},[10275],{"type":418,"value":1023},{"type":413,"tag":623,"props":10277,"children":10278},{"style":635},[10279],{"type":418,"value":5494},{"type":413,"tag":623,"props":10281,"children":10282},{"style":671},[10283],{"type":418,"value":1023},{"type":413,"tag":623,"props":10285,"children":10286},{"style":671},[10287],{"type":418,"value":1037},{"type":413,"tag":623,"props":10289,"children":10290},{"style":671},[10291],{"type":418,"value":5507},{"type":413,"tag":623,"props":10293,"children":10294},{"class":625,"line":1337},[10295,10299,10303,10307,10311,10315],{"type":413,"tag":623,"props":10296,"children":10297},{"style":5513},[10298],{"type":418,"value":5516},{"type":413,"tag":623,"props":10300,"children":10301},{"style":671},[10302],{"type":418,"value":3493},{"type":413,"tag":623,"props":10304,"children":10305},{"style":671},[10306],{"type":418,"value":674},{"type":413,"tag":623,"props":10308,"children":10309},{"style":635},[10310],{"type":418,"value":5494},{"type":413,"tag":623,"props":10312,"children":10313},{"style":671},[10314],{"type":418,"value":1023},{"type":413,"tag":623,"props":10316,"children":10317},{"style":671},[10318],{"type":418,"value":1084},{"type":413,"tag":623,"props":10320,"children":10321},{"class":625,"line":1346},[10322,10326,10330,10334,10338,10342],{"type":413,"tag":623,"props":10323,"children":10324},{"style":5513},[10325],{"type":418,"value":5544},{"type":413,"tag":623,"props":10327,"children":10328},{"style":671},[10329],{"type":418,"value":3493},{"type":413,"tag":623,"props":10331,"children":10332},{"style":671},[10333],{"type":418,"value":674},{"type":413,"tag":623,"props":10335,"children":10336},{"style":635},[10337],{"type":418,"value":5557},{"type":413,"tag":623,"props":10339,"children":10340},{"style":671},[10341],{"type":418,"value":1023},{"type":413,"tag":623,"props":10343,"children":10344},{"style":671},[10345],{"type":418,"value":1084},{"type":413,"tag":623,"props":10347,"children":10348},{"class":625,"line":2100},[10349,10353,10357],{"type":413,"tag":623,"props":10350,"children":10351},{"style":5513},[10352],{"type":418,"value":5573},{"type":413,"tag":623,"props":10354,"children":10355},{"style":671},[10356],{"type":418,"value":3493},{"type":413,"tag":623,"props":10358,"children":10359},{"style":5580},[10360],{"type":418,"value":5583},{"type":413,"tag":623,"props":10362,"children":10363},{"class":625,"line":2897},[10364,10368,10372],{"type":413,"tag":623,"props":10365,"children":10366},{"style":671},[10367],{"type":418,"value":3327},{"type":413,"tag":623,"props":10369,"children":10370},{"style":1058},[10371],{"type":418,"value":5595},{"type":413,"tag":623,"props":10373,"children":10374},{"style":671},[10375],{"type":418,"value":2524},{"type":413,"tag":623,"props":10377,"children":10378},{"class":625,"line":2926},[10379],{"type":413,"tag":623,"props":10380,"children":10381},{"emptyLinePlaceholder":2790},[10382],{"type":418,"value":2793},{"type":413,"tag":623,"props":10384,"children":10385},{"class":625,"line":2957},[10386,10390,10394,10398,10402,10406,10410,10414],{"type":413,"tag":623,"props":10387,"children":10388},{"style":2485},[10389],{"type":418,"value":5614},{"type":413,"tag":623,"props":10391,"children":10392},{"style":2854},[10393],{"type":418,"value":5619},{"type":413,"tag":623,"props":10395,"children":10396},{"style":1058},[10397],{"type":418,"value":5624},{"type":413,"tag":623,"props":10399,"children":10400},{"style":671},[10401],{"type":418,"value":1066},{"type":413,"tag":623,"props":10403,"children":10404},{"style":1058},[10405],{"type":418,"value":1390},{"type":413,"tag":623,"props":10407,"children":10408},{"style":671},[10409],{"type":418,"value":1404},{"type":413,"tag":623,"props":10411,"children":10412},{"style":1058},[10413],{"type":418,"value":5641},{"type":413,"tag":623,"props":10415,"children":10416},{"style":671},[10417],{"type":418,"value":2524},{"type":413,"tag":623,"props":10419,"children":10420},{"class":625,"line":2988},[10421],{"type":413,"tag":623,"props":10422,"children":10423},{"emptyLinePlaceholder":2790},[10424],{"type":418,"value":2793},{"type":413,"tag":623,"props":10426,"children":10427},{"class":625,"line":3045},[10428,10432,10436,10440,10444,10448,10452,10456,10460,10464,10468,10472,10476,10480,10484,10488,10492,10496,10500,10504,10508],{"type":413,"tag":623,"props":10429,"children":10430},{"style":2854},[10431],{"type":418,"value":5454},{"type":413,"tag":623,"props":10433,"children":10434},{"style":1058},[10435],{"type":418,"value":5804},{"type":413,"tag":623,"props":10437,"children":10438},{"style":671},[10439],{"type":418,"value":1066},{"type":413,"tag":623,"props":10441,"children":10442},{"style":671},[10443],{"type":418,"value":638},{"type":413,"tag":623,"props":10445,"children":10446},{"style":1058},[10447],{"type":418,"value":5817},{"type":413,"tag":623,"props":10449,"children":10450},{"style":671},[10451],{"type":418,"value":1404},{"type":413,"tag":623,"props":10453,"children":10454},{"style":1407},[10455],{"type":418,"value":5826},{"type":413,"tag":623,"props":10457,"children":10458},{"style":1058},[10459],{"type":418,"value":1018},{"type":413,"tag":623,"props":10461,"children":10462},{"style":671},[10463],{"type":418,"value":1023},{"type":413,"tag":623,"props":10465,"children":10466},{"style":635},[10467],{"type":418,"value":5839},{"type":413,"tag":623,"props":10469,"children":10470},{"style":671},[10471],{"type":418,"value":1023},{"type":413,"tag":623,"props":10473,"children":10474},{"style":671},[10475],{"type":418,"value":1037},{"type":413,"tag":623,"props":10477,"children":10478},{"style":671},[10479],{"type":418,"value":5852},{"type":413,"tag":623,"props":10481,"children":10482},{"style":5513},[10483],{"type":418,"value":5857},{"type":413,"tag":623,"props":10485,"children":10486},{"style":671},[10487],{"type":418,"value":3493},{"type":413,"tag":623,"props":10489,"children":10490},{"style":671},[10491],{"type":418,"value":674},{"type":413,"tag":623,"props":10493,"children":10494},{"style":635},[10495],{"type":418,"value":5870},{"type":413,"tag":623,"props":10497,"children":10498},{"style":671},[10499],{"type":418,"value":1023},{"type":413,"tag":623,"props":10501,"children":10502},{"style":671},[10503],{"type":418,"value":5879},{"type":413,"tag":623,"props":10505,"children":10506},{"style":1058},[10507],{"type":418,"value":5595},{"type":413,"tag":623,"props":10509,"children":10510},{"style":671},[10511],{"type":418,"value":2524},{"type":413,"tag":623,"props":10513,"children":10514},{"class":625,"line":7652},[10515,10519,10523,10527,10531,10535,10539,10543,10547,10551,10555,10559,10563],{"type":413,"tag":623,"props":10516,"children":10517},{"style":2854},[10518],{"type":418,"value":5454},{"type":413,"tag":623,"props":10520,"children":10521},{"style":1058},[10522],{"type":418,"value":5899},{"type":413,"tag":623,"props":10524,"children":10525},{"style":671},[10526],{"type":418,"value":1066},{"type":413,"tag":623,"props":10528,"children":10529},{"style":671},[10530],{"type":418,"value":638},{"type":413,"tag":623,"props":10532,"children":10533},{"style":1058},[10534],{"type":418,"value":5817},{"type":413,"tag":623,"props":10536,"children":10537},{"style":671},[10538],{"type":418,"value":1404},{"type":413,"tag":623,"props":10540,"children":10541},{"style":1407},[10542],{"type":418,"value":2950},{"type":413,"tag":623,"props":10544,"children":10545},{"style":1058},[10546],{"type":418,"value":1018},{"type":413,"tag":623,"props":10548,"children":10549},{"style":671},[10550],{"type":418,"value":1023},{"type":413,"tag":623,"props":10552,"children":10553},{"style":635},[10554],{"type":418,"value":2730},{"type":413,"tag":623,"props":10556,"children":10557},{"style":671},[10558],{"type":418,"value":1023},{"type":413,"tag":623,"props":10560,"children":10561},{"style":671},[10562],{"type":418,"value":1037},{"type":413,"tag":623,"props":10564,"children":10565},{"style":671},[10566],{"type":418,"value":5507},{"type":413,"tag":623,"props":10568,"children":10569},{"class":625,"line":7980},[10570,10574,10578,10582,10586,10590],{"type":413,"tag":623,"props":10571,"children":10572},{"style":5513},[10573],{"type":418,"value":5952},{"type":413,"tag":623,"props":10575,"children":10576},{"style":671},[10577],{"type":418,"value":3493},{"type":413,"tag":623,"props":10579,"children":10580},{"style":1058},[10581],{"type":418,"value":2615},{"type":413,"tag":623,"props":10583,"children":10584},{"style":671},[10585],{"type":418,"value":1404},{"type":413,"tag":623,"props":10587,"children":10588},{"style":1058},[10589],{"type":418,"value":5969},{"type":413,"tag":623,"props":10591,"children":10592},{"style":671},[10593],{"type":418,"value":1084},{"type":413,"tag":623,"props":10595,"children":10596},{"class":625,"line":8009},[10597,10601,10605],{"type":413,"tag":623,"props":10598,"children":10599},{"style":671},[10600],{"type":418,"value":3327},{"type":413,"tag":623,"props":10602,"children":10603},{"style":1058},[10604],{"type":418,"value":5595},{"type":413,"tag":623,"props":10606,"children":10607},{"style":671},[10608],{"type":418,"value":2524},{"type":413,"tag":623,"props":10610,"children":10611},{"class":625,"line":8027},[10612,10616,10620,10624,10628,10632,10636,10640,10644,10648],{"type":413,"tag":623,"props":10613,"children":10614},{"style":671},[10615],{"type":418,"value":2857},{"type":413,"tag":623,"props":10617,"children":10618},{"style":1058},[10619],{"type":418,"value":5817},{"type":413,"tag":623,"props":10621,"children":10622},{"style":671},[10623],{"type":418,"value":1404},{"type":413,"tag":623,"props":10625,"children":10626},{"style":1407},[10627],{"type":418,"value":6036},{"type":413,"tag":623,"props":10629,"children":10630},{"style":1058},[10631],{"type":418,"value":1018},{"type":413,"tag":623,"props":10633,"children":10634},{"style":671},[10635],{"type":418,"value":1023},{"type":413,"tag":623,"props":10637,"children":10638},{"style":635},[10639],{"type":418,"value":6049},{"type":413,"tag":623,"props":10641,"children":10642},{"style":671},[10643],{"type":418,"value":1023},{"type":413,"tag":623,"props":10645,"children":10646},{"style":671},[10647],{"type":418,"value":1037},{"type":413,"tag":623,"props":10649,"children":10650},{"style":671},[10651],{"type":418,"value":5507},{"type":413,"tag":623,"props":10653,"children":10654},{"class":625,"line":8036},[10655,10659,10663,10667,10671,10675],{"type":413,"tag":623,"props":10656,"children":10657},{"style":5513},[10658],{"type":418,"value":6069},{"type":413,"tag":623,"props":10660,"children":10661},{"style":671},[10662],{"type":418,"value":3493},{"type":413,"tag":623,"props":10664,"children":10665},{"style":1058},[10666],{"type":418,"value":2615},{"type":413,"tag":623,"props":10668,"children":10669},{"style":671},[10670],{"type":418,"value":1404},{"type":413,"tag":623,"props":10672,"children":10673},{"style":1058},[10674],{"type":418,"value":6086},{"type":413,"tag":623,"props":10676,"children":10677},{"style":671},[10678],{"type":418,"value":1084},{"type":413,"tag":623,"props":10680,"children":10681},{"class":625,"line":8373},[10682,10686,10690,10694,10698,10702],{"type":413,"tag":623,"props":10683,"children":10684},{"style":5513},[10685],{"type":418,"value":6098},{"type":413,"tag":623,"props":10687,"children":10688},{"style":671},[10689],{"type":418,"value":3493},{"type":413,"tag":623,"props":10691,"children":10692},{"style":671},[10693],{"type":418,"value":674},{"type":413,"tag":623,"props":10695,"children":10696},{"style":635},[10697],{"type":418,"value":3176},{"type":413,"tag":623,"props":10699,"children":10700},{"style":671},[10701],{"type":418,"value":1023},{"type":413,"tag":623,"props":10703,"children":10704},{"style":671},[10705],{"type":418,"value":1084},{"type":413,"tag":623,"props":10707,"children":10708},{"class":625,"line":8391},[10709,10713,10717,10721,10725,10729],{"type":413,"tag":623,"props":10710,"children":10711},{"style":5513},[10712],{"type":418,"value":6126},{"type":413,"tag":623,"props":10714,"children":10715},{"style":671},[10716],{"type":418,"value":3493},{"type":413,"tag":623,"props":10718,"children":10719},{"style":671},[10720],{"type":418,"value":674},{"type":413,"tag":623,"props":10722,"children":10723},{"style":635},[10724],{"type":418,"value":3204},{"type":413,"tag":623,"props":10726,"children":10727},{"style":671},[10728],{"type":418,"value":1023},{"type":413,"tag":623,"props":10730,"children":10731},{"style":671},[10732],{"type":418,"value":1084},{"type":413,"tag":623,"props":10734,"children":10735},{"class":625,"line":8416},[10736,10740,10744,10748,10752,10756,10760,10764],{"type":413,"tag":623,"props":10737,"children":10738},{"style":5513},[10739],{"type":418,"value":6154},{"type":413,"tag":623,"props":10741,"children":10742},{"style":671},[10743],{"type":418,"value":3493},{"type":413,"tag":623,"props":10745,"children":10746},{"style":1058},[10747],{"type":418,"value":6163},{"type":413,"tag":623,"props":10749,"children":10750},{"style":671},[10751],{"type":418,"value":1023},{"type":413,"tag":623,"props":10753,"children":10754},{"style":635},[10755],{"type":418,"value":3238},{"type":413,"tag":623,"props":10757,"children":10758},{"style":671},[10759],{"type":418,"value":1023},{"type":413,"tag":623,"props":10761,"children":10762},{"style":1058},[10763],{"type":418,"value":1137},{"type":413,"tag":623,"props":10765,"children":10766},{"style":671},[10767],{"type":418,"value":1084},{"type":413,"tag":623,"props":10769,"children":10770},{"class":625,"line":8425},[10771,10775,10779,10783,10787,10791],{"type":413,"tag":623,"props":10772,"children":10773},{"style":5513},[10774],{"type":418,"value":6191},{"type":413,"tag":623,"props":10776,"children":10777},{"style":671},[10778],{"type":418,"value":3493},{"type":413,"tag":623,"props":10780,"children":10781},{"style":671},[10782],{"type":418,"value":674},{"type":413,"tag":623,"props":10784,"children":10785},{"style":635},[10786],{"type":418,"value":6204},{"type":413,"tag":623,"props":10788,"children":10789},{"style":671},[10790],{"type":418,"value":1023},{"type":413,"tag":623,"props":10792,"children":10793},{"style":671},[10794],{"type":418,"value":1084},{"type":413,"tag":623,"props":10796,"children":10797},{"class":625,"line":8446},[10798,10802,10806,10810,10814,10818,10822,10826,10830,10834,10838,10842,10846,10850,10854],{"type":413,"tag":623,"props":10799,"children":10800},{"style":5513},[10801],{"type":418,"value":6220},{"type":413,"tag":623,"props":10803,"children":10804},{"style":671},[10805],{"type":418,"value":3493},{"type":413,"tag":623,"props":10807,"children":10808},{"style":1058},[10809],{"type":418,"value":6229},{"type":413,"tag":623,"props":10811,"children":10812},{"style":671},[10813],{"type":418,"value":1404},{"type":413,"tag":623,"props":10815,"children":10816},{"style":1407},[10817],{"type":418,"value":6238},{"type":413,"tag":623,"props":10819,"children":10820},{"style":671},[10821],{"type":418,"value":6243},{"type":413,"tag":623,"props":10823,"children":10824},{"style":635},[10825],{"type":418,"value":6248},{"type":413,"tag":623,"props":10827,"children":10828},{"style":671},[10829],{"type":418,"value":6253},{"type":413,"tag":623,"props":10831,"children":10832},{"style":1058},[10833],{"type":418,"value":6258},{"type":413,"tag":623,"props":10835,"children":10836},{"style":671},[10837],{"type":418,"value":1404},{"type":413,"tag":623,"props":10839,"children":10840},{"style":1058},[10841],{"type":418,"value":6267},{"type":413,"tag":623,"props":10843,"children":10844},{"style":671},[10845],{"type":418,"value":3327},{"type":413,"tag":623,"props":10847,"children":10848},{"style":635},[10849],{"type":418,"value":6276},{"type":413,"tag":623,"props":10851,"children":10852},{"style":671},[10853],{"type":418,"value":6243},{"type":413,"tag":623,"props":10855,"children":10856},{"style":671},[10857],{"type":418,"value":1084},{"type":413,"tag":623,"props":10859,"children":10860},{"class":625,"line":8463},[10861,10865,10869],{"type":413,"tag":623,"props":10862,"children":10863},{"style":671},[10864],{"type":418,"value":3327},{"type":413,"tag":623,"props":10866,"children":10867},{"style":1058},[10868],{"type":418,"value":5595},{"type":413,"tag":623,"props":10870,"children":10871},{"style":671},[10872],{"type":418,"value":2524},{"type":413,"tag":623,"props":10874,"children":10875},{"class":625,"line":8471},[10876],{"type":413,"tag":623,"props":10877,"children":10878},{"emptyLinePlaceholder":2790},[10879],{"type":418,"value":2793},{"type":413,"tag":623,"props":10881,"children":10882},{"class":625,"line":8492},[10883,10887,10891,10895,10899,10903,10907,10911,10915,10919,10923],{"type":413,"tag":623,"props":10884,"children":10885},{"style":2854},[10886],{"type":418,"value":5454},{"type":413,"tag":623,"props":10888,"children":10889},{"style":1058},[10890],{"type":418,"value":6808},{"type":413,"tag":623,"props":10892,"children":10893},{"style":671},[10894],{"type":418,"value":1066},{"type":413,"tag":623,"props":10896,"children":10897},{"style":1058},[10898],{"type":418,"value":6229},{"type":413,"tag":623,"props":10900,"children":10901},{"style":671},[10902],{"type":418,"value":1404},{"type":413,"tag":623,"props":10904,"children":10905},{"style":1407},[10906],{"type":418,"value":5679},{"type":413,"tag":623,"props":10908,"children":10909},{"style":1058},[10910],{"type":418,"value":6829},{"type":413,"tag":623,"props":10912,"children":10913},{"style":671},[10914],{"type":418,"value":1404},{"type":413,"tag":623,"props":10916,"children":10917},{"style":1407},[10918],{"type":418,"value":6838},{"type":413,"tag":623,"props":10920,"children":10921},{"style":1058},[10922],{"type":418,"value":6843},{"type":413,"tag":623,"props":10924,"children":10925},{"style":671},[10926],{"type":418,"value":2524},{"type":413,"tag":623,"props":10928,"children":10929},{"class":625,"line":8509},[10930,10934,10938,10942,10946,10950,10954],{"type":413,"tag":623,"props":10931,"children":10932},{"style":2854},[10933],{"type":418,"value":5454},{"type":413,"tag":623,"props":10935,"children":10936},{"style":1058},[10937],{"type":418,"value":6743},{"type":413,"tag":623,"props":10939,"children":10940},{"style":671},[10941],{"type":418,"value":1066},{"type":413,"tag":623,"props":10943,"children":10944},{"style":1058},[10945],{"type":418,"value":2137},{"type":413,"tag":623,"props":10947,"children":10948},{"style":671},[10949],{"type":418,"value":1404},{"type":413,"tag":623,"props":10951,"children":10952},{"style":1058},[10953],{"type":418,"value":3033},{"type":413,"tag":623,"props":10955,"children":10956},{"style":671},[10957],{"type":418,"value":2524},{"type":413,"tag":623,"props":10959,"children":10960},{"class":625,"line":8527},[10961],{"type":413,"tag":623,"props":10962,"children":10963},{"emptyLinePlaceholder":2790},[10964],{"type":418,"value":2793},{"type":413,"tag":623,"props":10966,"children":10967},{"class":625,"line":8539},[10968,10972,10976,10980,10984,10988,10992,10996,11000,11004],{"type":413,"tag":623,"props":10969,"children":10970},{"style":671},[10971],{"type":418,"value":2857},{"type":413,"tag":623,"props":10973,"children":10974},{"style":1058},[10975],{"type":418,"value":6479},{"type":413,"tag":623,"props":10977,"children":10978},{"style":671},[10979],{"type":418,"value":1404},{"type":413,"tag":623,"props":10981,"children":10982},{"style":1407},[10983],{"type":418,"value":6488},{"type":413,"tag":623,"props":10985,"children":10986},{"style":1058},[10987],{"type":418,"value":1018},{"type":413,"tag":623,"props":10989,"children":10990},{"style":671},[10991],{"type":418,"value":1023},{"type":413,"tag":623,"props":10993,"children":10994},{"style":635},[10995],{"type":418,"value":2875},{"type":413,"tag":623,"props":10997,"children":10998},{"style":671},[10999],{"type":418,"value":1023},{"type":413,"tag":623,"props":11001,"children":11002},{"style":671},[11003],{"type":418,"value":1037},{"type":413,"tag":623,"props":11005,"children":11006},{"style":671},[11007],{"type":418,"value":5507},{"type":413,"tag":623,"props":11009,"children":11010},{"class":625,"line":8557},[11011,11015,11019,11023,11027,11031],{"type":413,"tag":623,"props":11012,"children":11013},{"style":5513},[11014],{"type":418,"value":6520},{"type":413,"tag":623,"props":11016,"children":11017},{"style":671},[11018],{"type":418,"value":3493},{"type":413,"tag":623,"props":11020,"children":11021},{"style":1058},[11022],{"type":418,"value":2076},{"type":413,"tag":623,"props":11024,"children":11025},{"style":671},[11026],{"type":418,"value":1404},{"type":413,"tag":623,"props":11028,"children":11029},{"style":1058},[11030],{"type":418,"value":6537},{"type":413,"tag":623,"props":11032,"children":11033},{"style":671},[11034],{"type":418,"value":1084},{"type":413,"tag":623,"props":11036,"children":11037},{"class":625,"line":8575},[11038,11042,11046,11050,11054,11058,11062,11066],{"type":413,"tag":623,"props":11039,"children":11040},{"style":5513},[11041],{"type":418,"value":6549},{"type":413,"tag":623,"props":11043,"children":11044},{"style":671},[11045],{"type":418,"value":3493},{"type":413,"tag":623,"props":11047,"children":11048},{"style":1058},[11049],{"type":418,"value":6479},{"type":413,"tag":623,"props":11051,"children":11052},{"style":671},[11053],{"type":418,"value":1404},{"type":413,"tag":623,"props":11055,"children":11056},{"style":1058},[11057],{"type":418,"value":6566},{"type":413,"tag":623,"props":11059,"children":11060},{"style":671},[11061],{"type":418,"value":1404},{"type":413,"tag":623,"props":11063,"children":11064},{"style":1058},[11065],{"type":418,"value":2950},{"type":413,"tag":623,"props":11067,"children":11068},{"style":671},[11069],{"type":418,"value":1084},{"type":413,"tag":623,"props":11071,"children":11072},{"class":625,"line":8588},[11073,11077,11081,11085,11089,11093],{"type":413,"tag":623,"props":11074,"children":11075},{"style":5513},[11076],{"type":418,"value":6586},{"type":413,"tag":623,"props":11078,"children":11079},{"style":671},[11080],{"type":418,"value":3493},{"type":413,"tag":623,"props":11082,"children":11083},{"style":1058},[11084],{"type":418,"value":6434},{"type":413,"tag":623,"props":11086,"children":11087},{"style":671},[11088],{"type":418,"value":1404},{"type":413,"tag":623,"props":11090,"children":11091},{"style":1058},[11092],{"type":418,"value":2875},{"type":413,"tag":623,"props":11094,"children":11095},{"style":671},[11096],{"type":418,"value":1084},{"type":413,"tag":623,"props":11098,"children":11099},{"class":625,"line":8605},[11100,11104,11108,11112,11116,11120,11124,11128,11132,11136,11140],{"type":413,"tag":623,"props":11101,"children":11102},{"style":5513},[11103],{"type":418,"value":6614},{"type":413,"tag":623,"props":11105,"children":11106},{"style":671},[11107],{"type":418,"value":3493},{"type":413,"tag":623,"props":11109,"children":11110},{"style":1058},[11111],{"type":418,"value":6229},{"type":413,"tag":623,"props":11113,"children":11114},{"style":671},[11115],{"type":418,"value":1404},{"type":413,"tag":623,"props":11117,"children":11118},{"style":1407},[11119],{"type":418,"value":6238},{"type":413,"tag":623,"props":11121,"children":11122},{"style":671},[11123],{"type":418,"value":6243},{"type":413,"tag":623,"props":11125,"children":11126},{"style":635},[11127],{"type":418,"value":2451},{"type":413,"tag":623,"props":11129,"children":11130},{"style":671},[11131],{"type":418,"value":6253},{"type":413,"tag":623,"props":11133,"children":11134},{"style":1058},[11135],{"type":418,"value":3033},{"type":413,"tag":623,"props":11137,"children":11138},{"style":671},[11139],{"type":418,"value":6651},{"type":413,"tag":623,"props":11141,"children":11142},{"style":671},[11143],{"type":418,"value":1084},{"type":413,"tag":623,"props":11145,"children":11146},{"class":625,"line":8623},[11147,11151,11155],{"type":413,"tag":623,"props":11148,"children":11149},{"style":671},[11150],{"type":418,"value":3327},{"type":413,"tag":623,"props":11152,"children":11153},{"style":1058},[11154],{"type":418,"value":5595},{"type":413,"tag":623,"props":11156,"children":11157},{"style":671},[11158],{"type":418,"value":2524},{"type":413,"tag":623,"props":11160,"children":11161},{"class":625,"line":8641},[11162],{"type":413,"tag":623,"props":11163,"children":11164},{"emptyLinePlaceholder":2790},[11165],{"type":418,"value":2793},{"type":413,"tag":623,"props":11167,"children":11168},{"class":625,"line":8659},[11169,11173,11177,11181,11185,11189,11193,11197,11201,11205],{"type":413,"tag":623,"props":11170,"children":11171},{"style":671},[11172],{"type":418,"value":2857},{"type":413,"tag":623,"props":11174,"children":11175},{"style":1058},[11176],{"type":418,"value":5472},{"type":413,"tag":623,"props":11178,"children":11179},{"style":671},[11180],{"type":418,"value":1404},{"type":413,"tag":623,"props":11182,"children":11183},{"style":1407},[11184],{"type":418,"value":7247},{"type":413,"tag":623,"props":11186,"children":11187},{"style":1058},[11188],{"type":418,"value":1018},{"type":413,"tag":623,"props":11190,"children":11191},{"style":671},[11192],{"type":418,"value":1023},{"type":413,"tag":623,"props":11194,"children":11195},{"style":635},[11196],{"type":418,"value":2215},{"type":413,"tag":623,"props":11198,"children":11199},{"style":671},[11200],{"type":418,"value":1023},{"type":413,"tag":623,"props":11202,"children":11203},{"style":671},[11204],{"type":418,"value":1037},{"type":413,"tag":623,"props":11206,"children":11207},{"style":671},[11208],{"type":418,"value":5507},{"type":413,"tag":623,"props":11210,"children":11212},{"class":625,"line":11211},42,[11213,11217,11221,11225,11229,11233],{"type":413,"tag":623,"props":11214,"children":11215},{"style":5513},[11216],{"type":418,"value":7069},{"type":413,"tag":623,"props":11218,"children":11219},{"style":671},[11220],{"type":418,"value":3493},{"type":413,"tag":623,"props":11222,"children":11223},{"style":1058},[11224],{"type":418,"value":1390},{"type":413,"tag":623,"props":11226,"children":11227},{"style":671},[11228],{"type":418,"value":1404},{"type":413,"tag":623,"props":11230,"children":11231},{"style":1058},[11232],{"type":418,"value":5665},{"type":413,"tag":623,"props":11234,"children":11235},{"style":671},[11236],{"type":418,"value":1084},{"type":413,"tag":623,"props":11238,"children":11240},{"class":625,"line":11239},43,[11241,11245,11249,11253,11257,11261],{"type":413,"tag":623,"props":11242,"children":11243},{"style":5513},[11244],{"type":418,"value":7306},{"type":413,"tag":623,"props":11246,"children":11247},{"style":671},[11248],{"type":418,"value":3493},{"type":413,"tag":623,"props":11250,"children":11251},{"style":671},[11252],{"type":418,"value":674},{"type":413,"tag":623,"props":11254,"children":11255},{"style":635},[11256],{"type":418,"value":7110},{"type":413,"tag":623,"props":11258,"children":11259},{"style":671},[11260],{"type":418,"value":1023},{"type":413,"tag":623,"props":11262,"children":11263},{"style":671},[11264],{"type":418,"value":1084},{"type":413,"tag":623,"props":11266,"children":11268},{"class":625,"line":11267},44,[11269,11273,11277,11281,11285,11289],{"type":413,"tag":623,"props":11270,"children":11271},{"style":5513},[11272],{"type":418,"value":7334},{"type":413,"tag":623,"props":11274,"children":11275},{"style":671},[11276],{"type":418,"value":3493},{"type":413,"tag":623,"props":11278,"children":11279},{"style":1058},[11280],{"type":418,"value":2137},{"type":413,"tag":623,"props":11282,"children":11283},{"style":671},[11284],{"type":418,"value":1404},{"type":413,"tag":623,"props":11286,"children":11287},{"style":1058},[11288],{"type":418,"value":2215},{"type":413,"tag":623,"props":11290,"children":11291},{"style":671},[11292],{"type":418,"value":1084},{"type":413,"tag":623,"props":11294,"children":11296},{"class":625,"line":11295},45,[11297,11301,11305],{"type":413,"tag":623,"props":11298,"children":11299},{"style":671},[11300],{"type":418,"value":3327},{"type":413,"tag":623,"props":11302,"children":11303},{"style":1058},[11304],{"type":418,"value":5595},{"type":413,"tag":623,"props":11306,"children":11307},{"style":671},[11308],{"type":418,"value":2524},{"type":413,"tag":623,"props":11310,"children":11312},{"class":625,"line":11311},46,[11313],{"type":413,"tag":623,"props":11314,"children":11315},{"emptyLinePlaceholder":2790},[11316],{"type":418,"value":2793},{"type":413,"tag":623,"props":11318,"children":11320},{"class":625,"line":11319},47,[11321,11325,11329,11333,11337,11341,11345,11349,11353,11357],{"type":413,"tag":623,"props":11322,"children":11323},{"style":671},[11324],{"type":418,"value":2857},{"type":413,"tag":623,"props":11326,"children":11327},{"style":1058},[11328],{"type":418,"value":5472},{"type":413,"tag":623,"props":11330,"children":11331},{"style":671},[11332],{"type":418,"value":1404},{"type":413,"tag":623,"props":11334,"children":11335},{"style":1407},[11336],{"type":418,"value":7247},{"type":413,"tag":623,"props":11338,"children":11339},{"style":1058},[11340],{"type":418,"value":1018},{"type":413,"tag":623,"props":11342,"children":11343},{"style":671},[11344],{"type":418,"value":1023},{"type":413,"tag":623,"props":11346,"children":11347},{"style":635},[11348],{"type":418,"value":3033},{"type":413,"tag":623,"props":11350,"children":11351},{"style":671},[11352],{"type":418,"value":1023},{"type":413,"tag":623,"props":11354,"children":11355},{"style":671},[11356],{"type":418,"value":1037},{"type":413,"tag":623,"props":11358,"children":11359},{"style":671},[11360],{"type":418,"value":5507},{"type":413,"tag":623,"props":11362,"children":11364},{"class":625,"line":11363},48,[11365,11369,11373,11377,11381,11385],{"type":413,"tag":623,"props":11366,"children":11367},{"style":5513},[11368],{"type":418,"value":7069},{"type":413,"tag":623,"props":11370,"children":11371},{"style":671},[11372],{"type":418,"value":3493},{"type":413,"tag":623,"props":11374,"children":11375},{"style":1058},[11376],{"type":418,"value":1390},{"type":413,"tag":623,"props":11378,"children":11379},{"style":671},[11380],{"type":418,"value":1404},{"type":413,"tag":623,"props":11382,"children":11383},{"style":1058},[11384],{"type":418,"value":5665},{"type":413,"tag":623,"props":11386,"children":11387},{"style":671},[11388],{"type":418,"value":1084},{"type":413,"tag":623,"props":11390,"children":11392},{"class":625,"line":11391},49,[11393,11397,11401,11405,11409,11413],{"type":413,"tag":623,"props":11394,"children":11395},{"style":5513},[11396],{"type":418,"value":7306},{"type":413,"tag":623,"props":11398,"children":11399},{"style":671},[11400],{"type":418,"value":3493},{"type":413,"tag":623,"props":11402,"children":11403},{"style":671},[11404],{"type":418,"value":674},{"type":413,"tag":623,"props":11406,"children":11407},{"style":635},[11408],{"type":418,"value":7466},{"type":413,"tag":623,"props":11410,"children":11411},{"style":671},[11412],{"type":418,"value":1023},{"type":413,"tag":623,"props":11414,"children":11415},{"style":671},[11416],{"type":418,"value":1084},{"type":413,"tag":623,"props":11418,"children":11420},{"class":625,"line":11419},50,[11421,11425,11429,11433,11437,11441],{"type":413,"tag":623,"props":11422,"children":11423},{"style":5513},[11424],{"type":418,"value":7334},{"type":413,"tag":623,"props":11426,"children":11427},{"style":671},[11428],{"type":418,"value":3493},{"type":413,"tag":623,"props":11430,"children":11431},{"style":1058},[11432],{"type":418,"value":2137},{"type":413,"tag":623,"props":11434,"children":11435},{"style":671},[11436],{"type":418,"value":1404},{"type":413,"tag":623,"props":11438,"children":11439},{"style":1058},[11440],{"type":418,"value":3033},{"type":413,"tag":623,"props":11442,"children":11443},{"style":671},[11444],{"type":418,"value":1084},{"type":413,"tag":623,"props":11446,"children":11448},{"class":625,"line":11447},51,[11449,11453,11457],{"type":413,"tag":623,"props":11450,"children":11451},{"style":671},[11452],{"type":418,"value":3327},{"type":413,"tag":623,"props":11454,"children":11455},{"style":1058},[11456],{"type":418,"value":5595},{"type":413,"tag":623,"props":11458,"children":11459},{"style":671},[11460],{"type":418,"value":2524},{"type":413,"tag":623,"props":11462,"children":11464},{"class":625,"line":11463},52,[11465],{"type":413,"tag":623,"props":11466,"children":11467},{"emptyLinePlaceholder":2790},[11468],{"type":418,"value":2793},{"type":413,"tag":623,"props":11470,"children":11472},{"class":625,"line":11471},53,[11473,11477,11481,11485,11489,11493,11497,11501,11505,11509],{"type":413,"tag":623,"props":11474,"children":11475},{"style":671},[11476],{"type":418,"value":2857},{"type":413,"tag":623,"props":11478,"children":11479},{"style":1058},[11480],{"type":418,"value":5472},{"type":413,"tag":623,"props":11482,"children":11483},{"style":671},[11484],{"type":418,"value":1404},{"type":413,"tag":623,"props":11486,"children":11487},{"style":1407},[11488],{"type":418,"value":7247},{"type":413,"tag":623,"props":11490,"children":11491},{"style":1058},[11492],{"type":418,"value":1018},{"type":413,"tag":623,"props":11494,"children":11495},{"style":671},[11496],{"type":418,"value":1023},{"type":413,"tag":623,"props":11498,"children":11499},{"style":635},[11500],{"type":418,"value":7555},{"type":413,"tag":623,"props":11502,"children":11503},{"style":671},[11504],{"type":418,"value":1023},{"type":413,"tag":623,"props":11506,"children":11507},{"style":671},[11508],{"type":418,"value":1037},{"type":413,"tag":623,"props":11510,"children":11511},{"style":671},[11512],{"type":418,"value":5507},{"type":413,"tag":623,"props":11514,"children":11516},{"class":625,"line":11515},54,[11517,11521,11525,11529,11533,11537],{"type":413,"tag":623,"props":11518,"children":11519},{"style":5513},[11520],{"type":418,"value":7069},{"type":413,"tag":623,"props":11522,"children":11523},{"style":671},[11524],{"type":418,"value":3493},{"type":413,"tag":623,"props":11526,"children":11527},{"style":1058},[11528],{"type":418,"value":1390},{"type":413,"tag":623,"props":11530,"children":11531},{"style":671},[11532],{"type":418,"value":1404},{"type":413,"tag":623,"props":11534,"children":11535},{"style":1058},[11536],{"type":418,"value":5665},{"type":413,"tag":623,"props":11538,"children":11539},{"style":671},[11540],{"type":418,"value":1084},{"type":413,"tag":623,"props":11542,"children":11544},{"class":625,"line":11543},55,[11545,11549,11553,11557,11561,11565],{"type":413,"tag":623,"props":11546,"children":11547},{"style":5513},[11548],{"type":418,"value":7306},{"type":413,"tag":623,"props":11550,"children":11551},{"style":671},[11552],{"type":418,"value":3493},{"type":413,"tag":623,"props":11554,"children":11555},{"style":671},[11556],{"type":418,"value":674},{"type":413,"tag":623,"props":11558,"children":11559},{"style":635},[11560],{"type":418,"value":7614},{"type":413,"tag":623,"props":11562,"children":11563},{"style":671},[11564],{"type":418,"value":1023},{"type":413,"tag":623,"props":11566,"children":11567},{"style":671},[11568],{"type":418,"value":1084},{"type":413,"tag":623,"props":11570,"children":11572},{"class":625,"line":11571},56,[11573,11577,11581,11585,11589,11593],{"type":413,"tag":623,"props":11574,"children":11575},{"style":5513},[11576],{"type":418,"value":7334},{"type":413,"tag":623,"props":11578,"children":11579},{"style":671},[11580],{"type":418,"value":3493},{"type":413,"tag":623,"props":11582,"children":11583},{"style":1058},[11584],{"type":418,"value":2615},{"type":413,"tag":623,"props":11586,"children":11587},{"style":671},[11588],{"type":418,"value":1404},{"type":413,"tag":623,"props":11590,"children":11591},{"style":1058},[11592],{"type":418,"value":5969},{"type":413,"tag":623,"props":11594,"children":11595},{"style":671},[11596],{"type":418,"value":1084},{"type":413,"tag":623,"props":11598,"children":11600},{"class":625,"line":11599},57,[11601,11605,11609],{"type":413,"tag":623,"props":11602,"children":11603},{"style":671},[11604],{"type":418,"value":3327},{"type":413,"tag":623,"props":11606,"children":11607},{"style":1058},[11608],{"type":418,"value":5595},{"type":413,"tag":623,"props":11610,"children":11611},{"style":671},[11612],{"type":418,"value":2524},{"type":413,"tag":623,"props":11614,"children":11616},{"class":625,"line":11615},58,[11617],{"type":413,"tag":623,"props":11618,"children":11619},{"emptyLinePlaceholder":2790},[11620],{"type":418,"value":2793},{"type":413,"tag":623,"props":11622,"children":11624},{"class":625,"line":11623},59,[11625,11629,11633,11637,11641,11645,11649,11653,11657,11661],{"type":413,"tag":623,"props":11626,"children":11627},{"style":671},[11628],{"type":418,"value":2857},{"type":413,"tag":623,"props":11630,"children":11631},{"style":1058},[11632],{"type":418,"value":5472},{"type":413,"tag":623,"props":11634,"children":11635},{"style":671},[11636],{"type":418,"value":1404},{"type":413,"tag":623,"props":11638,"children":11639},{"style":1407},[11640],{"type":418,"value":7247},{"type":413,"tag":623,"props":11642,"children":11643},{"style":1058},[11644],{"type":418,"value":1018},{"type":413,"tag":623,"props":11646,"children":11647},{"style":671},[11648],{"type":418,"value":1023},{"type":413,"tag":623,"props":11650,"children":11651},{"style":635},[11652],{"type":418,"value":8967},{"type":413,"tag":623,"props":11654,"children":11655},{"style":671},[11656],{"type":418,"value":1023},{"type":413,"tag":623,"props":11658,"children":11659},{"style":671},[11660],{"type":418,"value":1037},{"type":413,"tag":623,"props":11662,"children":11663},{"style":671},[11664],{"type":418,"value":5507},{"type":413,"tag":623,"props":11666,"children":11668},{"class":625,"line":11667},60,[11669,11673,11677,11681,11685,11689],{"type":413,"tag":623,"props":11670,"children":11671},{"style":5513},[11672],{"type":418,"value":7069},{"type":413,"tag":623,"props":11674,"children":11675},{"style":671},[11676],{"type":418,"value":3493},{"type":413,"tag":623,"props":11678,"children":11679},{"style":1058},[11680],{"type":418,"value":1390},{"type":413,"tag":623,"props":11682,"children":11683},{"style":671},[11684],{"type":418,"value":1404},{"type":413,"tag":623,"props":11686,"children":11687},{"style":1058},[11688],{"type":418,"value":5665},{"type":413,"tag":623,"props":11690,"children":11691},{"style":671},[11692],{"type":418,"value":1084},{"type":413,"tag":623,"props":11694,"children":11696},{"class":625,"line":11695},61,[11697,11701,11705,11709,11713,11717],{"type":413,"tag":623,"props":11698,"children":11699},{"style":5513},[11700],{"type":418,"value":7306},{"type":413,"tag":623,"props":11702,"children":11703},{"style":671},[11704],{"type":418,"value":3493},{"type":413,"tag":623,"props":11706,"children":11707},{"style":671},[11708],{"type":418,"value":674},{"type":413,"tag":623,"props":11710,"children":11711},{"style":635},[11712],{"type":418,"value":8886},{"type":413,"tag":623,"props":11714,"children":11715},{"style":671},[11716],{"type":418,"value":1023},{"type":413,"tag":623,"props":11718,"children":11719},{"style":671},[11720],{"type":418,"value":1084},{"type":413,"tag":623,"props":11722,"children":11724},{"class":625,"line":11723},62,[11725,11729,11733,11737,11741,11745,11749,11753,11757,11761,11765],{"type":413,"tag":623,"props":11726,"children":11727},{"style":5513},[11728],{"type":418,"value":7334},{"type":413,"tag":623,"props":11730,"children":11731},{"style":671},[11732],{"type":418,"value":3493},{"type":413,"tag":623,"props":11734,"children":11735},{"style":1058},[11736],{"type":418,"value":879},{"type":413,"tag":623,"props":11738,"children":11739},{"style":671},[11740],{"type":418,"value":1404},{"type":413,"tag":623,"props":11742,"children":11743},{"style":1407},[11744],{"type":418,"value":9057},{"type":413,"tag":623,"props":11746,"children":11747},{"style":1058},[11748],{"type":418,"value":1018},{"type":413,"tag":623,"props":11750,"children":11751},{"style":671},[11752],{"type":418,"value":1023},{"type":413,"tag":623,"props":11754,"children":11755},{"style":635},[11756],{"type":418,"value":9070},{"type":413,"tag":623,"props":11758,"children":11759},{"style":671},[11760],{"type":418,"value":1023},{"type":413,"tag":623,"props":11762,"children":11763},{"style":1058},[11764],{"type":418,"value":5595},{"type":413,"tag":623,"props":11766,"children":11767},{"style":671},[11768],{"type":418,"value":1084},{"type":413,"tag":623,"props":11770,"children":11772},{"class":625,"line":11771},63,[11773,11777,11781],{"type":413,"tag":623,"props":11774,"children":11775},{"style":671},[11776],{"type":418,"value":3327},{"type":413,"tag":623,"props":11778,"children":11779},{"style":1058},[11780],{"type":418,"value":5595},{"type":413,"tag":623,"props":11782,"children":11783},{"style":671},[11784],{"type":418,"value":2524},{"type":413,"tag":623,"props":11786,"children":11788},{"class":625,"line":11787},64,[11789],{"type":413,"tag":623,"props":11790,"children":11791},{"emptyLinePlaceholder":2790},[11792],{"type":418,"value":2793},{"type":413,"tag":623,"props":11794,"children":11796},{"class":625,"line":11795},65,[11797,11801,11805,11809,11813,11817,11821,11825,11829,11833,11837,11841,11845,11849],{"type":413,"tag":623,"props":11798,"children":11799},{"style":2854},[11800],{"type":418,"value":5454},{"type":413,"tag":623,"props":11802,"children":11803},{"style":1058},[11804],{"type":418,"value":9169},{"type":413,"tag":623,"props":11806,"children":11807},{"style":671},[11808],{"type":418,"value":1066},{"type":413,"tag":623,"props":11810,"children":11811},{"style":1407},[11812],{"type":418,"value":9125},{"type":413,"tag":623,"props":11814,"children":11815},{"style":1058},[11816],{"type":418,"value":1018},{"type":413,"tag":623,"props":11818,"children":11819},{"style":671},[11820],{"type":418,"value":1023},{"type":413,"tag":623,"props":11822,"children":11823},{"style":635},[11824],{"type":418,"value":9190},{"type":413,"tag":623,"props":11826,"children":11827},{"style":671},[11828],{"type":418,"value":1023},{"type":413,"tag":623,"props":11830,"children":11831},{"style":671},[11832],{"type":418,"value":1037},{"type":413,"tag":623,"props":11834,"children":11835},{"style":671},[11836],{"type":418,"value":674},{"type":413,"tag":623,"props":11838,"children":11839},{"style":635},[11840],{"type":418,"value":9207},{"type":413,"tag":623,"props":11842,"children":11843},{"style":671},[11844],{"type":418,"value":1023},{"type":413,"tag":623,"props":11846,"children":11847},{"style":1058},[11848],{"type":418,"value":5595},{"type":413,"tag":623,"props":11850,"children":11851},{"style":671},[11852],{"type":418,"value":2524},{"type":413,"tag":623,"props":11854,"children":11856},{"class":625,"line":11855},66,[11857,11861,11865,11869,11873,11877,11881,11885,11889,11893],{"type":413,"tag":623,"props":11858,"children":11859},{"style":671},[11860],{"type":418,"value":2857},{"type":413,"tag":623,"props":11862,"children":11863},{"style":1058},[11864],{"type":418,"value":5472},{"type":413,"tag":623,"props":11866,"children":11867},{"style":671},[11868],{"type":418,"value":1404},{"type":413,"tag":623,"props":11870,"children":11871},{"style":1407},[11872],{"type":418,"value":9239},{"type":413,"tag":623,"props":11874,"children":11875},{"style":1058},[11876],{"type":418,"value":1018},{"type":413,"tag":623,"props":11878,"children":11879},{"style":671},[11880],{"type":418,"value":1023},{"type":413,"tag":623,"props":11882,"children":11883},{"style":635},[11884],{"type":418,"value":9252},{"type":413,"tag":623,"props":11886,"children":11887},{"style":671},[11888],{"type":418,"value":1023},{"type":413,"tag":623,"props":11890,"children":11891},{"style":671},[11892],{"type":418,"value":1037},{"type":413,"tag":623,"props":11894,"children":11895},{"style":671},[11896],{"type":418,"value":5507},{"type":413,"tag":623,"props":11898,"children":11900},{"class":625,"line":11899},67,[11901,11905,11909,11913,11917,11921],{"type":413,"tag":623,"props":11902,"children":11903},{"style":5513},[11904],{"type":418,"value":7069},{"type":413,"tag":623,"props":11906,"children":11907},{"style":671},[11908],{"type":418,"value":3493},{"type":413,"tag":623,"props":11910,"children":11911},{"style":1058},[11912],{"type":418,"value":1390},{"type":413,"tag":623,"props":11914,"children":11915},{"style":671},[11916],{"type":418,"value":1404},{"type":413,"tag":623,"props":11918,"children":11919},{"style":1058},[11920],{"type":418,"value":5665},{"type":413,"tag":623,"props":11922,"children":11923},{"style":671},[11924],{"type":418,"value":1084},{"type":413,"tag":623,"props":11926,"children":11928},{"class":625,"line":11927},68,[11929,11933,11937,11941,11945,11949],{"type":413,"tag":623,"props":11930,"children":11931},{"style":5513},[11932],{"type":418,"value":9299},{"type":413,"tag":623,"props":11934,"children":11935},{"style":671},[11936],{"type":418,"value":3493},{"type":413,"tag":623,"props":11938,"children":11939},{"style":671},[11940],{"type":418,"value":674},{"type":413,"tag":623,"props":11942,"children":11943},{"style":635},[11944],{"type":418,"value":4782},{"type":413,"tag":623,"props":11946,"children":11947},{"style":671},[11948],{"type":418,"value":1023},{"type":413,"tag":623,"props":11950,"children":11951},{"style":671},[11952],{"type":418,"value":1084},{"type":413,"tag":623,"props":11954,"children":11956},{"class":625,"line":11955},69,[11957,11961,11965,11969,11973,11977],{"type":413,"tag":623,"props":11958,"children":11959},{"style":5513},[11960],{"type":418,"value":9327},{"type":413,"tag":623,"props":11962,"children":11963},{"style":671},[11964],{"type":418,"value":3493},{"type":413,"tag":623,"props":11966,"children":11967},{"style":671},[11968],{"type":418,"value":674},{"type":413,"tag":623,"props":11970,"children":11971},{"style":635},[11972],{"type":418,"value":9340},{"type":413,"tag":623,"props":11974,"children":11975},{"style":671},[11976],{"type":418,"value":1023},{"type":413,"tag":623,"props":11978,"children":11979},{"style":671},[11980],{"type":418,"value":1084},{"type":413,"tag":623,"props":11982,"children":11984},{"class":625,"line":11983},70,[11985,11989,11993,11997],{"type":413,"tag":623,"props":11986,"children":11987},{"style":5513},[11988],{"type":418,"value":9356},{"type":413,"tag":623,"props":11990,"children":11991},{"style":671},[11992],{"type":418,"value":3493},{"type":413,"tag":623,"props":11994,"children":11995},{"style":1058},[11996],{"type":418,"value":9365},{"type":413,"tag":623,"props":11998,"children":11999},{"style":671},[12000],{"type":418,"value":1084},{"type":413,"tag":623,"props":12002,"children":12004},{"class":625,"line":12003},71,[12005,12009,12013,12017,12021,12025],{"type":413,"tag":623,"props":12006,"children":12007},{"style":5513},[12008],{"type":418,"value":9377},{"type":413,"tag":623,"props":12010,"children":12011},{"style":671},[12012],{"type":418,"value":3493},{"type":413,"tag":623,"props":12014,"children":12015},{"style":671},[12016],{"type":418,"value":674},{"type":413,"tag":623,"props":12018,"children":12019},{"style":635},[12020],{"type":418,"value":3841},{"type":413,"tag":623,"props":12022,"children":12023},{"style":671},[12024],{"type":418,"value":1023},{"type":413,"tag":623,"props":12026,"children":12027},{"style":671},[12028],{"type":418,"value":1084},{"type":413,"tag":623,"props":12030,"children":12032},{"class":625,"line":12031},72,[12033,12037,12041,12045,12049,12053],{"type":413,"tag":623,"props":12034,"children":12035},{"style":5513},[12036],{"type":418,"value":9405},{"type":413,"tag":623,"props":12038,"children":12039},{"style":671},[12040],{"type":418,"value":3493},{"type":413,"tag":623,"props":12042,"children":12043},{"style":671},[12044],{"type":418,"value":674},{"type":413,"tag":623,"props":12046,"children":12047},{"style":635},[12048],{"type":418,"value":9418},{"type":413,"tag":623,"props":12050,"children":12051},{"style":671},[12052],{"type":418,"value":1023},{"type":413,"tag":623,"props":12054,"children":12055},{"style":671},[12056],{"type":418,"value":1084},{"type":413,"tag":623,"props":12058,"children":12060},{"class":625,"line":12059},73,[12061,12065,12069,12073,12077,12081],{"type":413,"tag":623,"props":12062,"children":12063},{"style":5513},[12064],{"type":418,"value":9434},{"type":413,"tag":623,"props":12066,"children":12067},{"style":671},[12068],{"type":418,"value":3493},{"type":413,"tag":623,"props":12070,"children":12071},{"style":671},[12072],{"type":418,"value":674},{"type":413,"tag":623,"props":12074,"children":12075},{"style":635},[12076],{"type":418,"value":9447},{"type":413,"tag":623,"props":12078,"children":12079},{"style":671},[12080],{"type":418,"value":1023},{"type":413,"tag":623,"props":12082,"children":12083},{"style":671},[12084],{"type":418,"value":1084},{"type":413,"tag":623,"props":12086,"children":12088},{"class":625,"line":12087},74,[12089,12093,12097,12101],{"type":413,"tag":623,"props":12090,"children":12091},{"style":5513},[12092],{"type":418,"value":9463},{"type":413,"tag":623,"props":12094,"children":12095},{"style":671},[12096],{"type":418,"value":3493},{"type":413,"tag":623,"props":12098,"children":12099},{"style":5580},[12100],{"type":418,"value":9472},{"type":413,"tag":623,"props":12102,"children":12103},{"style":671},[12104],{"type":418,"value":1084},{"type":413,"tag":623,"props":12106,"children":12108},{"class":625,"line":12107},75,[12109,12113,12117],{"type":413,"tag":623,"props":12110,"children":12111},{"style":671},[12112],{"type":418,"value":3327},{"type":413,"tag":623,"props":12114,"children":12115},{"style":1058},[12116],{"type":418,"value":5595},{"type":413,"tag":623,"props":12118,"children":12119},{"style":671},[12120],{"type":418,"value":2524},{"type":413,"tag":414,"props":12122,"children":12123},{},[12124,12125,12131],{"type":418,"value":4660},{"type":413,"tag":432,"props":12126,"children":12129},{"href":12127,"rel":12128},"https://github.com/TechWatching/AzureOIDC",[436],[12130],{"type":418,"value":4670},{"type":418,"value":1404},{"type":413,"tag":414,"props":12133,"children":12134},{},[12135],{"type":418,"value":12136},"I hope you enjoyed this article. Please feel free to share your thoughts in the comments, ask questions, or make suggestions. Keep learning.",{"type":413,"tag":4673,"props":12138,"children":12139},{},[12140],{"type":418,"value":4677},{"title":401,"searchDepth":1045,"depth":1045,"links":12142},[12143,12149,12150,12159,12160],{"id":4729,"depth":1045,"text":4732,"children":12144},[12145,12146,12147,12148],{"id":4754,"depth":1054,"text":4757},{"id":4814,"depth":1054,"text":4817},{"id":4897,"depth":1054,"text":4900},{"id":4999,"depth":1054,"text":5002},{"id":5014,"depth":1045,"text":5017},{"id":5082,"depth":1045,"text":5085,"children":12151},[12152,12153,12154,12156,12157,12158],{"id":5088,"depth":1054,"text":5091},{"id":5316,"depth":1054,"text":5319},{"id":5724,"depth":1054,"text":12155},"Create the identity in Azure Active Directory for the GitHub Actions workflow",{"id":6357,"depth":1054,"text":6360},{"id":6973,"depth":1054,"text":6976},{"id":7685,"depth":1054,"text":7688},{"id":9554,"depth":1045,"text":9557},{"id":4624,"depth":1045,"text":4627,"children":12161},[12162,12163,12164,12165],{"id":9780,"depth":1054,"text":9783},{"id":9796,"depth":1054,"text":9799},{"id":9830,"depth":1054,"text":9833},{"id":9927,"depth":1054,"text":9930},"content:1.posts:53.azure-ready-github-repository.md","1.posts/53.azure-ready-github-repository.md",{"_path":142,"_dir":399,"_draft":400,"_partial":400,"_locale":401,"title":141,"description":12169,"lead":12170,"date":12171,"image":12172,"badge":12174,"tags":12175,"body":12176,"_type":4691,"_id":16520,"_source":4693,"_file":16521,"_extension":4695},"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":12173},"/images/trees_1.jpg",{"label":266},[239,365,312,368,315,266,228],{"type":410,"children":12177,"toc":16491},[12178,12182,12205,12210,12215,12220,12226,12231,12236,12241,12247,12252,12262,12267,12288,12297,12302,12311,12316,12324,12333,12338,12344,12350,12355,12373,12378,12401,12407,12412,12421,12426,12449,12458,12463,12481,12493,12512,12521,12527,12532,12541,12546,12551,12567,12573,12579,12584,12593,12615,12623,12628,12634,12639,12855,12860,13012,13018,13040,13167,13173,13202,13406,13412,13432,13746,13763,13792,13857,13876,13882,13888,13909,13918,13926,13938,13944,13949,13974,14612,14617,14625,14631,14636,14649,14663,14781,14798,14811,15093,15106,15112,15124,15400,15406,15419,15432,15642,15647,16266,16283,16288,16293,16299,16304,16313,16326,16335,16343,16348,16357,16362,16371,16377,16383,16388,16420,16434,16440,16445,16466,16471,16476,16482,16487],{"type":413,"tag":414,"props":12179,"children":12180},{},[12181],{"type":418,"value":12169},{"type":413,"tag":443,"props":12183,"children":12184},{},[12185,12190,12195,12200],{"type":413,"tag":447,"props":12186,"children":12187},{},[12188],{"type":418,"value":12189},"there are plenty of languages and frameworks to choose from to build an application",{"type":413,"tag":447,"props":12191,"children":12192},{},[12193],{"type":418,"value":12194},"there are very powerful IDEs and tools to help us write, analyze, refactor, test and debug code",{"type":413,"tag":447,"props":12196,"children":12197},{},[12198],{"type":418,"value":12199},"there are many nice CI/CD platforms that allow us to package and deploy our applications anywhere",{"type":413,"tag":447,"props":12201,"children":12202},{},[12203],{"type":418,"value":12204},"thanks to cloud platforms and infrastructure as code we can provision infrastructure on-demand in an automated way",{"type":413,"tag":414,"props":12206,"children":12207},{},[12208],{"type":418,"value":12209},"Yet, sometimes it seems quite complex and time-consuming to deploy an application in the cloud.",{"type":413,"tag":414,"props":12211,"children":12212},{},[12213],{"type":418,"value":12214},"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":413,"tag":414,"props":12216,"children":12217},{},[12218],{"type":418,"value":12219},"Thanks to Nuke and Pulumi, I don't think so and that is what we are going to talk about in this article.",{"type":413,"tag":420,"props":12221,"children":12223},{"id":12222},"the-scenario",[12224],{"type":418,"value":12225},"The scenario",{"type":413,"tag":414,"props":12227,"children":12228},{},[12229],{"type":418,"value":12230},"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":413,"tag":414,"props":12232,"children":12233},{},[12234],{"type":418,"value":12235},"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":413,"tag":414,"props":12237,"children":12238},{},[12239],{"type":418,"value":12240},"To do that, I want to use my existing .NET skills and code everything with the language and tools I know and love.",{"type":413,"tag":420,"props":12242,"children":12244},{"id":12243},"steps-of-the-cicd-pipeline",[12245],{"type":418,"value":12246},"Steps of the CI/CD pipeline",{"type":413,"tag":414,"props":12248,"children":12249},{},[12250],{"type":418,"value":12251},"There are often two main steps (or stages or whatever you call them) in a CI / CD pipeline: the packaging and the deployment.",{"type":413,"tag":414,"props":12253,"children":12254},{},[12255],{"type":413,"tag":487,"props":12256,"children":12261},{"alt":12257,"className":12258,"src":12259,"width":12260},"Diagram with package and deploy steps.",[491,492],"/posts/images/pulumi_met_nuke_1.png",600,[],{"type":413,"tag":414,"props":12263,"children":12264},{},[12265],{"type":418,"value":12266},"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":413,"tag":496,"props":12268,"children":12269},{"icon":557},[12270],{"type":413,"tag":414,"props":12271,"children":12272},{},[12273,12275,12286],{"type":418,"value":12274},"A ",{"type":413,"tag":432,"props":12276,"children":12279},{"href":12277,"rel":12278},"https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-publish#description",[436],[12280],{"type":413,"tag":619,"props":12281,"children":12283},{"className":12282},[],[12284],{"type":418,"value":12285},"dotnet publish",{"type":418,"value":12287}," 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":413,"tag":414,"props":12289,"children":12290},{},[12291],{"type":413,"tag":487,"props":12292,"children":12296},{"alt":12293,"className":12294,"src":12295,"width":591},"Diagram with restore, compile, publish and deploy steps.",[491,492],"/posts/images/pulumi_met_nuke_2.png",[],{"type":413,"tag":414,"props":12298,"children":12299},{},[12300],{"type":418,"value":12301},"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":413,"tag":414,"props":12303,"children":12304},{},[12305],{"type":413,"tag":487,"props":12306,"children":12310},{"alt":12307,"className":12308,"src":12309,"width":591},"Diagram with restore, compile, publish, deploy and provision infra steps.",[491,492],"/posts/images/pulumi_met_nuke_3.png",[],{"type":413,"tag":414,"props":12312,"children":12313},{},[12314],{"type":418,"value":12315},"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":413,"tag":496,"props":12317,"children":12318},{"icon":4578},[12319],{"type":413,"tag":414,"props":12320,"children":12321},{},[12322],{"type":418,"value":12323},"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":413,"tag":414,"props":12325,"children":12326},{},[12327],{"type":413,"tag":487,"props":12328,"children":12332},{"alt":12329,"className":12330,"src":12331},"Diagram with clean, restore, compile, publish, deploy and provision infra steps.",[491,492],"/posts/images/pulumi_met_nuke_4.png",[],{"type":413,"tag":414,"props":12334,"children":12335},{},[12336],{"type":418,"value":12337},"Now, that we know the different steps of our pipeline, let's get to the code.",{"type":413,"tag":420,"props":12339,"children":12341},{"id":12340},"getting-started-with-the-code",[12342],{"type":418,"value":12343},"Getting started with the code",{"type":413,"tag":600,"props":12345,"children":12347},{"id":12346},"code-organization",[12348],{"type":418,"value":12349},"Code organization",{"type":413,"tag":414,"props":12351,"children":12352},{},[12353],{"type":418,"value":12354},"I put all the code in the same Git repository because:",{"type":413,"tag":443,"props":12356,"children":12357},{},[12358,12363,12368],{"type":413,"tag":447,"props":12359,"children":12360},{},[12361],{"type":418,"value":12362},"it makes sense as everything is linked",{"type":413,"tag":447,"props":12364,"children":12365},{},[12366],{"type":418,"value":12367},"it's easier to maintain (all the code in one place)",{"type":413,"tag":447,"props":12369,"children":12370},{},[12371],{"type":418,"value":12372},"it's easier to version (one tag on one commit in one repository)",{"type":413,"tag":414,"props":12374,"children":12375},{},[12376],{"type":418,"value":12377},"I chose to organize my repository with the following folders:",{"type":413,"tag":443,"props":12379,"children":12380},{},[12381,12386,12391,12396],{"type":413,"tag":447,"props":12382,"children":12383},{},[12384],{"type":418,"value":12385},"📁 src ➡️ for the application code of the API",{"type":413,"tag":447,"props":12387,"children":12388},{},[12389],{"type":418,"value":12390},"📁 infra ➡️ for the infrastructure code that provisions the App Service",{"type":413,"tag":447,"props":12392,"children":12393},{},[12394],{"type":418,"value":12395},"📁 build ➡️ for pipeline code that builds and deploys the application",{"type":413,"tag":447,"props":12397,"children":12398},{},[12399],{"type":418,"value":12400},"📁 artifacts ➡️ for the package created by the pipeline",{"type":413,"tag":600,"props":12402,"children":12404},{"id":12403},"create-the-c-projects",[12405],{"type":418,"value":12406},"Create the C# projects",{"type":413,"tag":414,"props":12408,"children":12409},{},[12410],{"type":418,"value":12411},"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":413,"tag":414,"props":12413,"children":12414},{},[12415],{"type":413,"tag":487,"props":12416,"children":12420},{"alt":12417,"className":12418,"src":12419},"IDE folder explorer of an ASP.NET Core API template.",[491,492],"/posts/images/pulumi_met_nuke_5.png",[],{"type":413,"tag":414,"props":12422,"children":12423},{},[12424],{"type":418,"value":12425},"I can initialize the infrastructure project using the Pulumi CLI new command with the azure C# template:",{"type":413,"tag":612,"props":12427,"children":12429},{"className":614,"code":12428,"language":616,"meta":401,"style":401},"pulumi new azure-csharp\n",[12430],{"type":413,"tag":619,"props":12431,"children":12432},{"__ignoreMap":401},[12433],{"type":413,"tag":623,"props":12434,"children":12435},{"class":625,"line":626},[12436,12440,12444],{"type":413,"tag":623,"props":12437,"children":12438},{"style":630},[12439],{"type":418,"value":311},{"type":413,"tag":623,"props":12441,"children":12442},{"style":635},[12443],{"type":418,"value":638},{"type":413,"tag":623,"props":12445,"children":12446},{"style":635},[12447],{"type":418,"value":12448}," azure-csharp\n",{"type":413,"tag":414,"props":12450,"children":12451},{},[12452],{"type":413,"tag":487,"props":12453,"children":12457},{"alt":12454,"className":12455,"src":12456},"IDE folder explorer focused on the infra folder containing the Pulumi project.",[491,492],"/posts/images/pulumi_met_nuke_6.png",[],{"type":413,"tag":414,"props":12459,"children":12460},{},[12461],{"type":418,"value":12462},"I will show later how to modify the code of the template to provision an App Service.",{"type":413,"tag":12464,"props":12465,"children":12466},"blockquote",{},[12467],{"type":413,"tag":414,"props":12468,"children":12469},{},[12470,12472,12479],{"type":418,"value":12471},"You can check Pulumi ",{"type":413,"tag":432,"props":12473,"children":12476},{"href":12474,"rel":12475},"https://www.pulumi.com/docs/get-started/azure/",[436],[12477],{"type":418,"value":12478},"Getting Started with Azure",{"type":418,"value":12480}," tutorial to see how to set up your environment and start creating Azure resources in C# (or in another language).",{"type":413,"tag":414,"props":12482,"children":12483},{},[12484,12486,12492],{"type":418,"value":12485},"To initialize the build project, we can use Nuke's .NET global tool as explained in the ",{"type":413,"tag":432,"props":12487,"children":12490},{"href":12488,"rel":12489},"https://nuke.build/docs/getting-started/setup/",[436],[12491],{"type":418,"value":4994},{"type":418,"value":3493},{"type":413,"tag":612,"props":12494,"children":12496},{"className":614,"code":12495,"language":616,"meta":401,"style":401},"nuke :setup\n",[12497],{"type":413,"tag":619,"props":12498,"children":12499},{"__ignoreMap":401},[12500],{"type":413,"tag":623,"props":12501,"children":12502},{"class":625,"line":626},[12503,12507],{"type":413,"tag":623,"props":12504,"children":12505},{"style":630},[12506],{"type":418,"value":364},{"type":413,"tag":623,"props":12508,"children":12509},{"style":635},[12510],{"type":418,"value":12511}," :setup\n",{"type":413,"tag":414,"props":12513,"children":12514},{},[12515],{"type":413,"tag":487,"props":12516,"children":12520},{"alt":12517,"className":12518,"src":12519},"IDE folder explorer focused on the build folder containing the Nuke project.",[491,492],"/posts/images/pulumi_met_nuke_7.png",[],{"type":413,"tag":600,"props":12522,"children":12524},{"id":12523},"everything-in-net",[12525],{"type":418,"value":12526},"Everything in .NET",{"type":413,"tag":414,"props":12528,"children":12529},{},[12530],{"type":418,"value":12531},"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":413,"tag":414,"props":12533,"children":12534},{},[12535],{"type":413,"tag":487,"props":12536,"children":12540},{"alt":12537,"className":12538,"src":12539},"IDE solution explorer witht the infra, build and api projects.",[491,492],"/posts/images/pulumi_met_nuke_8.png",[],{"type":413,"tag":414,"props":12542,"children":12543},{},[12544],{"type":418,"value":12545},"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":413,"tag":414,"props":12547,"children":12548},{},[12549],{"type":418,"value":12550},"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":413,"tag":496,"props":12552,"children":12553},{"icon":557},[12554],{"type":413,"tag":414,"props":12555,"children":12556},{},[12557,12559,12565],{"type":418,"value":12558},"I talk a lot about the benefits of using programming languages for infrastructure code in my article \"",{"type":413,"tag":432,"props":12560,"children":12563},{"href":12561,"rel":12562},"https://www.techwatching.dev/posts/pulumi-vs-terraform",[436],[12564],{"type":418,"value":114},{"type":418,"value":12566},"\" if you have not read it yet.",{"type":413,"tag":420,"props":12568,"children":12570},{"id":12569},"implementing-the-pipeline-steps-from-clean-to-publish",[12571],{"type":418,"value":12572},"Implementing the pipeline steps from Clean to Publish",{"type":413,"tag":600,"props":12574,"children":12576},{"id":12575},"the-nuke-pipeline",[12577],{"type":418,"value":12578},"The Nuke pipeline",{"type":413,"tag":414,"props":12580,"children":12581},{},[12582],{"type":418,"value":12583},"Here is what looks like the default build project after its creation:",{"type":413,"tag":414,"props":12585,"children":12586},{},[12587],{"type":413,"tag":487,"props":12588,"children":12592},{"alt":12589,"className":12590,"src":12591},"Buidl code file with the build steps opened in the IDE.",[491,492],"/posts/images/pulumi_met_nuke_9.png",[],{"type":413,"tag":414,"props":12594,"children":12595},{},[12596,12598,12604,12606,12613],{"type":418,"value":12597},"The main method is contained in a ",{"type":413,"tag":619,"props":12599,"children":12601},{"className":12600},[],[12602],{"type":418,"value":12603},"Build.cs",{"type":418,"value":12605}," file. This file contains the steps of the pipeline that are called ",{"type":413,"tag":432,"props":12607,"children":12610},{"href":12608,"rel":12609},"https://nuke.build/docs/fundamentals/targets/",[436],[12611],{"type":418,"value":12612},"Target",{"type":418,"value":12614}," in Nuke. We can set the dependencies between targets.",{"type":413,"tag":496,"props":12616,"children":12617},{"icon":4578},[12618],{"type":413,"tag":414,"props":12619,"children":12620},{},[12621],{"type":418,"value":12622},"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":413,"tag":414,"props":12624,"children":12625},{},[12626],{"type":418,"value":12627},"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":413,"tag":600,"props":12629,"children":12631},{"id":12630},"the-clean-target",[12632],{"type":418,"value":12633},"The Clean target",{"type":413,"tag":414,"props":12635,"children":12636},{},[12637],{"type":418,"value":12638},"We can define the Clean target like that:",{"type":413,"tag":612,"props":12640,"children":12642},{"className":981,"code":12641,"language":326,"meta":401,"style":401},"Target Clean => _ => _  \n    .Before(Restore)  \n    .Executes(() =>  \n    {  \n        SourceDirectory.GlobDirectories(\"*/bin\", \"*/obj\").ForEach(DeleteDirectory);  \n        EnsureCleanDirectory(ArtifactsDirectory);  \n    });\n",[12643],{"type":413,"tag":619,"props":12644,"children":12645},{"__ignoreMap":401},[12646,12676,12707,12732,12744,12822,12847],{"type":413,"tag":623,"props":12647,"children":12648},{"class":625,"line":626},[12649,12654,12658,12662,12667,12671],{"type":413,"tag":623,"props":12650,"children":12651},{"style":1058},[12652],{"type":418,"value":12653},"Target ",{"type":413,"tag":623,"props":12655,"children":12656},{"style":630},[12657],{"type":418,"value":1632},{"type":413,"tag":623,"props":12659,"children":12660},{"style":671},[12661],{"type":418,"value":2201},{"type":413,"tag":623,"props":12663,"children":12664},{"style":630},[12665],{"type":418,"value":12666}," _",{"type":413,"tag":623,"props":12668,"children":12669},{"style":671},[12670],{"type":418,"value":2201},{"type":413,"tag":623,"props":12672,"children":12673},{"style":1058},[12674],{"type":418,"value":12675}," _  \n",{"type":413,"tag":623,"props":12677,"children":12678},{"class":625,"line":1045},[12679,12684,12689,12693,12698,12702],{"type":413,"tag":623,"props":12680,"children":12681},{"style":671},[12682],{"type":418,"value":12683},"    .",{"type":413,"tag":623,"props":12685,"children":12686},{"style":1407},[12687],{"type":418,"value":12688},"Before",{"type":413,"tag":623,"props":12690,"children":12691},{"style":671},[12692],{"type":418,"value":1018},{"type":413,"tag":623,"props":12694,"children":12695},{"style":1058},[12696],{"type":418,"value":12697},"Restore",{"type":413,"tag":623,"props":12699,"children":12700},{"style":671},[12701],{"type":418,"value":5595},{"type":413,"tag":623,"props":12703,"children":12704},{"style":1058},[12705],{"type":418,"value":12706},"  \n",{"type":413,"tag":623,"props":12708,"children":12709},{"class":625,"line":1054},[12710,12714,12719,12724,12728],{"type":413,"tag":623,"props":12711,"children":12712},{"style":671},[12713],{"type":418,"value":12683},{"type":413,"tag":623,"props":12715,"children":12716},{"style":1407},[12717],{"type":418,"value":12718},"Executes",{"type":413,"tag":623,"props":12720,"children":12721},{"style":671},[12722],{"type":418,"value":12723},"(()",{"type":413,"tag":623,"props":12725,"children":12726},{"style":671},[12727],{"type":418,"value":2201},{"type":413,"tag":623,"props":12729,"children":12730},{"style":1058},[12731],{"type":418,"value":12706},{"type":413,"tag":623,"props":12733,"children":12734},{"class":625,"line":1087},[12735,12740],{"type":413,"tag":623,"props":12736,"children":12737},{"style":671},[12738],{"type":418,"value":12739},"    {",{"type":413,"tag":623,"props":12741,"children":12742},{"style":1058},[12743],{"type":418,"value":12706},{"type":413,"tag":623,"props":12745,"children":12746},{"class":625,"line":1104},[12747,12752,12756,12761,12765,12769,12774,12778,12782,12786,12791,12795,12799,12804,12808,12813,12818],{"type":413,"tag":623,"props":12748,"children":12749},{"style":1058},[12750],{"type":418,"value":12751},"        SourceDirectory",{"type":413,"tag":623,"props":12753,"children":12754},{"style":671},[12755],{"type":418,"value":1404},{"type":413,"tag":623,"props":12757,"children":12758},{"style":1407},[12759],{"type":418,"value":12760},"GlobDirectories",{"type":413,"tag":623,"props":12762,"children":12763},{"style":671},[12764],{"type":418,"value":1018},{"type":413,"tag":623,"props":12766,"children":12767},{"style":671},[12768],{"type":418,"value":1023},{"type":413,"tag":623,"props":12770,"children":12771},{"style":635},[12772],{"type":418,"value":12773},"*/bin",{"type":413,"tag":623,"props":12775,"children":12776},{"style":671},[12777],{"type":418,"value":1023},{"type":413,"tag":623,"props":12779,"children":12780},{"style":671},[12781],{"type":418,"value":1037},{"type":413,"tag":623,"props":12783,"children":12784},{"style":671},[12785],{"type":418,"value":674},{"type":413,"tag":623,"props":12787,"children":12788},{"style":635},[12789],{"type":418,"value":12790},"*/obj",{"type":413,"tag":623,"props":12792,"children":12793},{"style":671},[12794],{"type":418,"value":1023},{"type":413,"tag":623,"props":12796,"children":12797},{"style":671},[12798],{"type":418,"value":544},{"type":413,"tag":623,"props":12800,"children":12801},{"style":1407},[12802],{"type":418,"value":12803},"ForEach",{"type":413,"tag":623,"props":12805,"children":12806},{"style":671},[12807],{"type":418,"value":1018},{"type":413,"tag":623,"props":12809,"children":12810},{"style":1058},[12811],{"type":418,"value":12812},"DeleteDirectory",{"type":413,"tag":623,"props":12814,"children":12815},{"style":671},[12816],{"type":418,"value":12817},");",{"type":413,"tag":623,"props":12819,"children":12820},{"style":1058},[12821],{"type":418,"value":12706},{"type":413,"tag":623,"props":12823,"children":12824},{"class":625,"line":1113},[12825,12830,12834,12839,12843],{"type":413,"tag":623,"props":12826,"children":12827},{"style":1407},[12828],{"type":418,"value":12829},"        EnsureCleanDirectory",{"type":413,"tag":623,"props":12831,"children":12832},{"style":671},[12833],{"type":418,"value":1018},{"type":413,"tag":623,"props":12835,"children":12836},{"style":1058},[12837],{"type":418,"value":12838},"ArtifactsDirectory",{"type":413,"tag":623,"props":12840,"children":12841},{"style":671},[12842],{"type":418,"value":12817},{"type":413,"tag":623,"props":12844,"children":12845},{"style":1058},[12846],{"type":418,"value":12706},{"type":413,"tag":623,"props":12848,"children":12849},{"class":625,"line":1161},[12850],{"type":413,"tag":623,"props":12851,"children":12852},{"style":671},[12853],{"type":418,"value":12854},"    });\n",{"type":413,"tag":414,"props":12856,"children":12857},{},[12858],{"type":418,"value":12859},"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":413,"tag":612,"props":12861,"children":12863},{"className":981,"code":12862,"language":326,"meta":401,"style":401},"AbsolutePath SourceDirectory => RootDirectory / \"src\";  \n  \nAbsolutePath InfrastructureDirectory => RootDirectory / \"infra\";  \n  \nAbsolutePath ArtifactsDirectory => RootDirectory / \"artifacts\";\n",[12864],{"type":413,"tag":619,"props":12865,"children":12866},{"__ignoreMap":401},[12867,12914,12921,12966,12973],{"type":413,"tag":623,"props":12868,"children":12869},{"class":625,"line":626},[12870,12875,12880,12884,12889,12893,12897,12902,12906,12910],{"type":413,"tag":623,"props":12871,"children":12872},{"style":1058},[12873],{"type":418,"value":12874},"AbsolutePath ",{"type":413,"tag":623,"props":12876,"children":12877},{"style":630},[12878],{"type":418,"value":12879},"SourceDirectory",{"type":413,"tag":623,"props":12881,"children":12882},{"style":671},[12883],{"type":418,"value":2201},{"type":413,"tag":623,"props":12885,"children":12886},{"style":1058},[12887],{"type":418,"value":12888}," RootDirectory ",{"type":413,"tag":623,"props":12890,"children":12891},{"style":671},[12892],{"type":418,"value":3332},{"type":413,"tag":623,"props":12894,"children":12895},{"style":671},[12896],{"type":418,"value":674},{"type":413,"tag":623,"props":12898,"children":12899},{"style":635},[12900],{"type":418,"value":12901},"src",{"type":413,"tag":623,"props":12903,"children":12904},{"style":671},[12905],{"type":418,"value":1023},{"type":413,"tag":623,"props":12907,"children":12908},{"style":671},[12909],{"type":418,"value":9626},{"type":413,"tag":623,"props":12911,"children":12912},{"style":1058},[12913],{"type":418,"value":12706},{"type":413,"tag":623,"props":12915,"children":12916},{"class":625,"line":1045},[12917],{"type":413,"tag":623,"props":12918,"children":12919},{"style":1058},[12920],{"type":418,"value":12706},{"type":413,"tag":623,"props":12922,"children":12923},{"class":625,"line":1054},[12924,12928,12933,12937,12941,12945,12949,12954,12958,12962],{"type":413,"tag":623,"props":12925,"children":12926},{"style":1058},[12927],{"type":418,"value":12874},{"type":413,"tag":623,"props":12929,"children":12930},{"style":630},[12931],{"type":418,"value":12932},"InfrastructureDirectory",{"type":413,"tag":623,"props":12934,"children":12935},{"style":671},[12936],{"type":418,"value":2201},{"type":413,"tag":623,"props":12938,"children":12939},{"style":1058},[12940],{"type":418,"value":12888},{"type":413,"tag":623,"props":12942,"children":12943},{"style":671},[12944],{"type":418,"value":3332},{"type":413,"tag":623,"props":12946,"children":12947},{"style":671},[12948],{"type":418,"value":674},{"type":413,"tag":623,"props":12950,"children":12951},{"style":635},[12952],{"type":418,"value":12953},"infra",{"type":413,"tag":623,"props":12955,"children":12956},{"style":671},[12957],{"type":418,"value":1023},{"type":413,"tag":623,"props":12959,"children":12960},{"style":671},[12961],{"type":418,"value":9626},{"type":413,"tag":623,"props":12963,"children":12964},{"style":1058},[12965],{"type":418,"value":12706},{"type":413,"tag":623,"props":12967,"children":12968},{"class":625,"line":1087},[12969],{"type":413,"tag":623,"props":12970,"children":12971},{"style":1058},[12972],{"type":418,"value":12706},{"type":413,"tag":623,"props":12974,"children":12975},{"class":625,"line":1104},[12976,12980,12984,12988,12992,12996,13000,13004,13008],{"type":413,"tag":623,"props":12977,"children":12978},{"style":1058},[12979],{"type":418,"value":12874},{"type":413,"tag":623,"props":12981,"children":12982},{"style":630},[12983],{"type":418,"value":12838},{"type":413,"tag":623,"props":12985,"children":12986},{"style":671},[12987],{"type":418,"value":2201},{"type":413,"tag":623,"props":12989,"children":12990},{"style":1058},[12991],{"type":418,"value":12888},{"type":413,"tag":623,"props":12993,"children":12994},{"style":671},[12995],{"type":418,"value":3332},{"type":413,"tag":623,"props":12997,"children":12998},{"style":671},[12999],{"type":418,"value":674},{"type":413,"tag":623,"props":13001,"children":13002},{"style":635},[13003],{"type":418,"value":1310},{"type":413,"tag":623,"props":13005,"children":13006},{"style":671},[13007],{"type":418,"value":1023},{"type":413,"tag":623,"props":13009,"children":13010},{"style":671},[13011],{"type":418,"value":2524},{"type":413,"tag":600,"props":13013,"children":13015},{"id":13014},"the-restore-target",[13016],{"type":418,"value":13017},"The Restore target",{"type":413,"tag":414,"props":13019,"children":13020},{},[13021,13023,13029,13031,13038],{"type":418,"value":13022},"To restore .NET dependencies, we can use the ",{"type":413,"tag":619,"props":13024,"children":13026},{"className":13025},[],[13027],{"type":418,"value":13028},"dotnet restore",{"type":418,"value":13030}," command. Nuke supports ",{"type":413,"tag":432,"props":13032,"children":13035},{"href":13033,"rel":13034},"https://nuke.build/docs/common/cli-tools/",[436],[13036],{"type":418,"value":13037},"executing CLI tools",{"type":418,"value":13039}," 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":413,"tag":612,"props":13041,"children":13043},{"className":981,"code":13042,"language":326,"meta":401,"style":401},"Target Restore => _ => _  \n    .Executes(() =>  \n    {  \n        DotNetRestore(_ => _.SetProjectFile(Solution));  \n    });\n",[13044],{"type":413,"tag":619,"props":13045,"children":13046},{"__ignoreMap":401},[13047,13074,13097,13108,13160],{"type":413,"tag":623,"props":13048,"children":13049},{"class":625,"line":626},[13050,13054,13058,13062,13066,13070],{"type":413,"tag":623,"props":13051,"children":13052},{"style":1058},[13053],{"type":418,"value":12653},{"type":413,"tag":623,"props":13055,"children":13056},{"style":630},[13057],{"type":418,"value":12697},{"type":413,"tag":623,"props":13059,"children":13060},{"style":671},[13061],{"type":418,"value":2201},{"type":413,"tag":623,"props":13063,"children":13064},{"style":630},[13065],{"type":418,"value":12666},{"type":413,"tag":623,"props":13067,"children":13068},{"style":671},[13069],{"type":418,"value":2201},{"type":413,"tag":623,"props":13071,"children":13072},{"style":1058},[13073],{"type":418,"value":12675},{"type":413,"tag":623,"props":13075,"children":13076},{"class":625,"line":1045},[13077,13081,13085,13089,13093],{"type":413,"tag":623,"props":13078,"children":13079},{"style":671},[13080],{"type":418,"value":12683},{"type":413,"tag":623,"props":13082,"children":13083},{"style":1407},[13084],{"type":418,"value":12718},{"type":413,"tag":623,"props":13086,"children":13087},{"style":671},[13088],{"type":418,"value":12723},{"type":413,"tag":623,"props":13090,"children":13091},{"style":671},[13092],{"type":418,"value":2201},{"type":413,"tag":623,"props":13094,"children":13095},{"style":1058},[13096],{"type":418,"value":12706},{"type":413,"tag":623,"props":13098,"children":13099},{"class":625,"line":1054},[13100,13104],{"type":413,"tag":623,"props":13101,"children":13102},{"style":671},[13103],{"type":418,"value":12739},{"type":413,"tag":623,"props":13105,"children":13106},{"style":1058},[13107],{"type":418,"value":12706},{"type":413,"tag":623,"props":13109,"children":13110},{"class":625,"line":1087},[13111,13116,13120,13125,13129,13133,13137,13142,13146,13151,13156],{"type":413,"tag":623,"props":13112,"children":13113},{"style":1407},[13114],{"type":418,"value":13115},"        DotNetRestore",{"type":413,"tag":623,"props":13117,"children":13118},{"style":671},[13119],{"type":418,"value":1018},{"type":413,"tag":623,"props":13121,"children":13122},{"style":630},[13123],{"type":418,"value":13124},"_",{"type":413,"tag":623,"props":13126,"children":13127},{"style":671},[13128],{"type":418,"value":2201},{"type":413,"tag":623,"props":13130,"children":13131},{"style":1058},[13132],{"type":418,"value":12666},{"type":413,"tag":623,"props":13134,"children":13135},{"style":671},[13136],{"type":418,"value":1404},{"type":413,"tag":623,"props":13138,"children":13139},{"style":1407},[13140],{"type":418,"value":13141},"SetProjectFile",{"type":413,"tag":623,"props":13143,"children":13144},{"style":671},[13145],{"type":418,"value":1018},{"type":413,"tag":623,"props":13147,"children":13148},{"style":1058},[13149],{"type":418,"value":13150},"Solution",{"type":413,"tag":623,"props":13152,"children":13153},{"style":671},[13154],{"type":418,"value":13155},"));",{"type":413,"tag":623,"props":13157,"children":13158},{"style":1058},[13159],{"type":418,"value":12706},{"type":413,"tag":623,"props":13161,"children":13162},{"class":625,"line":1104},[13163],{"type":413,"tag":623,"props":13164,"children":13165},{"style":671},[13166],{"type":418,"value":12854},{"type":413,"tag":600,"props":13168,"children":13170},{"id":13169},"the-compile-target",[13171],{"type":418,"value":13172},"The Compile target",{"type":413,"tag":414,"props":13174,"children":13175},{},[13176,13178,13184,13186,13192,13194,13200],{"type":418,"value":13177},"The compile target uses the ",{"type":413,"tag":619,"props":13179,"children":13181},{"className":13180},[],[13182],{"type":418,"value":13183},"dotnet build",{"type":418,"value":13185}," 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":413,"tag":619,"props":13187,"children":13189},{"className":13188},[],[13190],{"type":418,"value":13191},"--no-restore",{"type":418,"value":13193}," option using the ",{"type":413,"tag":619,"props":13195,"children":13197},{"className":13196},[],[13198],{"type":418,"value":13199},"EnableNoRestore",{"type":418,"value":13201}," auto-generated method.",{"type":413,"tag":612,"props":13203,"children":13205},{"className":981,"code":13204,"language":326,"meta":401,"style":401},"Target Compile => _ => _  \n    .DependsOn(Restore)  \n    .Executes(() =>  \n    {  \n        DotNetBuild(_ => _  \n            .SetProjectFile(Solution)  \n            .SetConfiguration(Configuration)  \n            .EnableNoRestore());  \n    });\n",[13206],{"type":413,"tag":619,"props":13207,"children":13208},{"__ignoreMap":401},[13209,13237,13265,13288,13299,13323,13351,13379,13399],{"type":413,"tag":623,"props":13210,"children":13211},{"class":625,"line":626},[13212,13216,13221,13225,13229,13233],{"type":413,"tag":623,"props":13213,"children":13214},{"style":1058},[13215],{"type":418,"value":12653},{"type":413,"tag":623,"props":13217,"children":13218},{"style":630},[13219],{"type":418,"value":13220},"Compile",{"type":413,"tag":623,"props":13222,"children":13223},{"style":671},[13224],{"type":418,"value":2201},{"type":413,"tag":623,"props":13226,"children":13227},{"style":630},[13228],{"type":418,"value":12666},{"type":413,"tag":623,"props":13230,"children":13231},{"style":671},[13232],{"type":418,"value":2201},{"type":413,"tag":623,"props":13234,"children":13235},{"style":1058},[13236],{"type":418,"value":12675},{"type":413,"tag":623,"props":13238,"children":13239},{"class":625,"line":1045},[13240,13244,13249,13253,13257,13261],{"type":413,"tag":623,"props":13241,"children":13242},{"style":671},[13243],{"type":418,"value":12683},{"type":413,"tag":623,"props":13245,"children":13246},{"style":1407},[13247],{"type":418,"value":13248},"DependsOn",{"type":413,"tag":623,"props":13250,"children":13251},{"style":671},[13252],{"type":418,"value":1018},{"type":413,"tag":623,"props":13254,"children":13255},{"style":1058},[13256],{"type":418,"value":12697},{"type":413,"tag":623,"props":13258,"children":13259},{"style":671},[13260],{"type":418,"value":5595},{"type":413,"tag":623,"props":13262,"children":13263},{"style":1058},[13264],{"type":418,"value":12706},{"type":413,"tag":623,"props":13266,"children":13267},{"class":625,"line":1054},[13268,13272,13276,13280,13284],{"type":413,"tag":623,"props":13269,"children":13270},{"style":671},[13271],{"type":418,"value":12683},{"type":413,"tag":623,"props":13273,"children":13274},{"style":1407},[13275],{"type":418,"value":12718},{"type":413,"tag":623,"props":13277,"children":13278},{"style":671},[13279],{"type":418,"value":12723},{"type":413,"tag":623,"props":13281,"children":13282},{"style":671},[13283],{"type":418,"value":2201},{"type":413,"tag":623,"props":13285,"children":13286},{"style":1058},[13287],{"type":418,"value":12706},{"type":413,"tag":623,"props":13289,"children":13290},{"class":625,"line":1087},[13291,13295],{"type":413,"tag":623,"props":13292,"children":13293},{"style":671},[13294],{"type":418,"value":12739},{"type":413,"tag":623,"props":13296,"children":13297},{"style":1058},[13298],{"type":418,"value":12706},{"type":413,"tag":623,"props":13300,"children":13301},{"class":625,"line":1104},[13302,13307,13311,13315,13319],{"type":413,"tag":623,"props":13303,"children":13304},{"style":1407},[13305],{"type":418,"value":13306},"        DotNetBuild",{"type":413,"tag":623,"props":13308,"children":13309},{"style":671},[13310],{"type":418,"value":1018},{"type":413,"tag":623,"props":13312,"children":13313},{"style":630},[13314],{"type":418,"value":13124},{"type":413,"tag":623,"props":13316,"children":13317},{"style":671},[13318],{"type":418,"value":2201},{"type":413,"tag":623,"props":13320,"children":13321},{"style":1058},[13322],{"type":418,"value":12675},{"type":413,"tag":623,"props":13324,"children":13325},{"class":625,"line":1113},[13326,13331,13335,13339,13343,13347],{"type":413,"tag":623,"props":13327,"children":13328},{"style":671},[13329],{"type":418,"value":13330},"            .",{"type":413,"tag":623,"props":13332,"children":13333},{"style":1407},[13334],{"type":418,"value":13141},{"type":413,"tag":623,"props":13336,"children":13337},{"style":671},[13338],{"type":418,"value":1018},{"type":413,"tag":623,"props":13340,"children":13341},{"style":1058},[13342],{"type":418,"value":13150},{"type":413,"tag":623,"props":13344,"children":13345},{"style":671},[13346],{"type":418,"value":5595},{"type":413,"tag":623,"props":13348,"children":13349},{"style":1058},[13350],{"type":418,"value":12706},{"type":413,"tag":623,"props":13352,"children":13353},{"class":625,"line":1161},[13354,13358,13363,13367,13371,13375],{"type":413,"tag":623,"props":13355,"children":13356},{"style":671},[13357],{"type":418,"value":13330},{"type":413,"tag":623,"props":13359,"children":13360},{"style":1407},[13361],{"type":418,"value":13362},"SetConfiguration",{"type":413,"tag":623,"props":13364,"children":13365},{"style":671},[13366],{"type":418,"value":1018},{"type":413,"tag":623,"props":13368,"children":13369},{"style":1058},[13370],{"type":418,"value":263},{"type":413,"tag":623,"props":13372,"children":13373},{"style":671},[13374],{"type":418,"value":5595},{"type":413,"tag":623,"props":13376,"children":13377},{"style":1058},[13378],{"type":418,"value":12706},{"type":413,"tag":623,"props":13380,"children":13381},{"class":625,"line":1207},[13382,13386,13390,13395],{"type":413,"tag":623,"props":13383,"children":13384},{"style":671},[13385],{"type":418,"value":13330},{"type":413,"tag":623,"props":13387,"children":13388},{"style":1407},[13389],{"type":418,"value":13199},{"type":413,"tag":623,"props":13391,"children":13392},{"style":671},[13393],{"type":418,"value":13394},"());",{"type":413,"tag":623,"props":13396,"children":13397},{"style":1058},[13398],{"type":418,"value":12706},{"type":413,"tag":623,"props":13400,"children":13401},{"class":625,"line":1251},[13402],{"type":413,"tag":623,"props":13403,"children":13404},{"style":671},[13405],{"type":418,"value":12854},{"type":413,"tag":600,"props":13407,"children":13409},{"id":13408},"the-publish-target",[13410],{"type":418,"value":13411},"The Publish target",{"type":413,"tag":414,"props":13413,"children":13414},{},[13415,13417,13422,13424,13430],{"type":418,"value":13416},"The publish target uses the ",{"type":413,"tag":619,"props":13418,"children":13420},{"className":13419},[],[13421],{"type":418,"value":12285},{"type":418,"value":13423}," command and then creates a zip ",{"type":413,"tag":619,"props":13425,"children":13427},{"className":13426},[],[13428],{"type":418,"value":13429},"api.zip",{"type":418,"value":13431}," of the resulting package in the artifacts directory.",{"type":413,"tag":612,"props":13433,"children":13435},{"className":981,"code":13434,"language":326,"meta":401,"style":401},"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",[13436],{"type":413,"tag":619,"props":13437,"children":13438},{"__ignoreMap":401},[13439,13467,13503,13526,13537,13561,13598,13625,13645,13674,13681,13739],{"type":413,"tag":623,"props":13440,"children":13441},{"class":625,"line":626},[13442,13446,13451,13455,13459,13463],{"type":413,"tag":623,"props":13443,"children":13444},{"style":1058},[13445],{"type":418,"value":12653},{"type":413,"tag":623,"props":13447,"children":13448},{"style":630},[13449],{"type":418,"value":13450},"Publish",{"type":413,"tag":623,"props":13452,"children":13453},{"style":671},[13454],{"type":418,"value":2201},{"type":413,"tag":623,"props":13456,"children":13457},{"style":630},[13458],{"type":418,"value":12666},{"type":413,"tag":623,"props":13460,"children":13461},{"style":671},[13462],{"type":418,"value":2201},{"type":413,"tag":623,"props":13464,"children":13465},{"style":1058},[13466],{"type":418,"value":12675},{"type":413,"tag":623,"props":13468,"children":13469},{"class":625,"line":1045},[13470,13474,13478,13482,13486,13490,13495,13499],{"type":413,"tag":623,"props":13471,"children":13472},{"style":671},[13473],{"type":418,"value":12683},{"type":413,"tag":623,"props":13475,"children":13476},{"style":1407},[13477],{"type":418,"value":13248},{"type":413,"tag":623,"props":13479,"children":13480},{"style":671},[13481],{"type":418,"value":1018},{"type":413,"tag":623,"props":13483,"children":13484},{"style":1058},[13485],{"type":418,"value":1632},{"type":413,"tag":623,"props":13487,"children":13488},{"style":671},[13489],{"type":418,"value":1037},{"type":413,"tag":623,"props":13491,"children":13492},{"style":1058},[13493],{"type":418,"value":13494}," Compile",{"type":413,"tag":623,"props":13496,"children":13497},{"style":671},[13498],{"type":418,"value":5595},{"type":413,"tag":623,"props":13500,"children":13501},{"style":1058},[13502],{"type":418,"value":12706},{"type":413,"tag":623,"props":13504,"children":13505},{"class":625,"line":1054},[13506,13510,13514,13518,13522],{"type":413,"tag":623,"props":13507,"children":13508},{"style":671},[13509],{"type":418,"value":12683},{"type":413,"tag":623,"props":13511,"children":13512},{"style":1407},[13513],{"type":418,"value":12718},{"type":413,"tag":623,"props":13515,"children":13516},{"style":671},[13517],{"type":418,"value":12723},{"type":413,"tag":623,"props":13519,"children":13520},{"style":671},[13521],{"type":418,"value":2201},{"type":413,"tag":623,"props":13523,"children":13524},{"style":1058},[13525],{"type":418,"value":12706},{"type":413,"tag":623,"props":13527,"children":13528},{"class":625,"line":1087},[13529,13533],{"type":413,"tag":623,"props":13530,"children":13531},{"style":671},[13532],{"type":418,"value":12739},{"type":413,"tag":623,"props":13534,"children":13535},{"style":1058},[13536],{"type":418,"value":12706},{"type":413,"tag":623,"props":13538,"children":13539},{"class":625,"line":1104},[13540,13545,13549,13553,13557],{"type":413,"tag":623,"props":13541,"children":13542},{"style":1407},[13543],{"type":418,"value":13544},"        DotNetPublish",{"type":413,"tag":623,"props":13546,"children":13547},{"style":671},[13548],{"type":418,"value":1018},{"type":413,"tag":623,"props":13550,"children":13551},{"style":630},[13552],{"type":418,"value":13124},{"type":413,"tag":623,"props":13554,"children":13555},{"style":671},[13556],{"type":418,"value":2201},{"type":413,"tag":623,"props":13558,"children":13559},{"style":1058},[13560],{"type":418,"value":12675},{"type":413,"tag":623,"props":13562,"children":13563},{"class":625,"line":1113},[13564,13568,13573,13577,13581,13585,13590,13594],{"type":413,"tag":623,"props":13565,"children":13566},{"style":671},[13567],{"type":418,"value":13330},{"type":413,"tag":623,"props":13569,"children":13570},{"style":1407},[13571],{"type":418,"value":13572},"SetProject",{"type":413,"tag":623,"props":13574,"children":13575},{"style":671},[13576],{"type":418,"value":1018},{"type":413,"tag":623,"props":13578,"children":13579},{"style":1058},[13580],{"type":418,"value":13150},{"type":413,"tag":623,"props":13582,"children":13583},{"style":671},[13584],{"type":418,"value":1404},{"type":413,"tag":623,"props":13586,"children":13587},{"style":1058},[13588],{"type":418,"value":13589},"CSharpEverything_Api",{"type":413,"tag":623,"props":13591,"children":13592},{"style":671},[13593],{"type":418,"value":5595},{"type":413,"tag":623,"props":13595,"children":13596},{"style":1058},[13597],{"type":418,"value":12706},{"type":413,"tag":623,"props":13599,"children":13600},{"class":625,"line":1161},[13601,13605,13609,13613,13617,13621],{"type":413,"tag":623,"props":13602,"children":13603},{"style":671},[13604],{"type":418,"value":13330},{"type":413,"tag":623,"props":13606,"children":13607},{"style":1407},[13608],{"type":418,"value":13362},{"type":413,"tag":623,"props":13610,"children":13611},{"style":671},[13612],{"type":418,"value":1018},{"type":413,"tag":623,"props":13614,"children":13615},{"style":1058},[13616],{"type":418,"value":263},{"type":413,"tag":623,"props":13618,"children":13619},{"style":671},[13620],{"type":418,"value":5595},{"type":413,"tag":623,"props":13622,"children":13623},{"style":1058},[13624],{"type":418,"value":12706},{"type":413,"tag":623,"props":13626,"children":13627},{"class":625,"line":1207},[13628,13632,13637,13641],{"type":413,"tag":623,"props":13629,"children":13630},{"style":671},[13631],{"type":418,"value":13330},{"type":413,"tag":623,"props":13633,"children":13634},{"style":1407},[13635],{"type":418,"value":13636},"EnableNoBuild",{"type":413,"tag":623,"props":13638,"children":13639},{"style":671},[13640],{"type":418,"value":6727},{"type":413,"tag":623,"props":13642,"children":13643},{"style":1058},[13644],{"type":418,"value":12706},{"type":413,"tag":623,"props":13646,"children":13647},{"class":625,"line":1251},[13648,13652,13657,13661,13666,13670],{"type":413,"tag":623,"props":13649,"children":13650},{"style":671},[13651],{"type":418,"value":13330},{"type":413,"tag":623,"props":13653,"children":13654},{"style":1407},[13655],{"type":418,"value":13656},"SetOutput",{"type":413,"tag":623,"props":13658,"children":13659},{"style":671},[13660],{"type":418,"value":1018},{"type":413,"tag":623,"props":13662,"children":13663},{"style":1058},[13664],{"type":418,"value":13665},"ApiPackageDirectory",{"type":413,"tag":623,"props":13667,"children":13668},{"style":671},[13669],{"type":418,"value":13155},{"type":413,"tag":623,"props":13671,"children":13672},{"style":1058},[13673],{"type":418,"value":12706},{"type":413,"tag":623,"props":13675,"children":13676},{"class":625,"line":1296},[13677],{"type":413,"tag":623,"props":13678,"children":13679},{"style":1058},[13680],{"type":418,"value":12706},{"type":413,"tag":623,"props":13682,"children":13683},{"class":625,"line":1337},[13684,13689,13693,13698,13702,13706,13710,13715,13719,13723,13727,13731,13735],{"type":413,"tag":623,"props":13685,"children":13686},{"style":1058},[13687],{"type":418,"value":13688},"        ZipFile",{"type":413,"tag":623,"props":13690,"children":13691},{"style":671},[13692],{"type":418,"value":1404},{"type":413,"tag":623,"props":13694,"children":13695},{"style":1407},[13696],{"type":418,"value":13697},"CreateFromDirectory",{"type":413,"tag":623,"props":13699,"children":13700},{"style":671},[13701],{"type":418,"value":1018},{"type":413,"tag":623,"props":13703,"children":13704},{"style":1058},[13705],{"type":418,"value":13665},{"type":413,"tag":623,"props":13707,"children":13708},{"style":671},[13709],{"type":418,"value":1037},{"type":413,"tag":623,"props":13711,"children":13712},{"style":1058},[13713],{"type":418,"value":13714}," ArtifactsDirectory ",{"type":413,"tag":623,"props":13716,"children":13717},{"style":671},[13718],{"type":418,"value":3332},{"type":413,"tag":623,"props":13720,"children":13721},{"style":671},[13722],{"type":418,"value":674},{"type":413,"tag":623,"props":13724,"children":13725},{"style":635},[13726],{"type":418,"value":13429},{"type":413,"tag":623,"props":13728,"children":13729},{"style":671},[13730],{"type":418,"value":1023},{"type":413,"tag":623,"props":13732,"children":13733},{"style":671},[13734],{"type":418,"value":12817},{"type":413,"tag":623,"props":13736,"children":13737},{"style":1058},[13738],{"type":418,"value":12706},{"type":413,"tag":623,"props":13740,"children":13741},{"class":625,"line":1346},[13742],{"type":413,"tag":623,"props":13743,"children":13744},{"style":671},[13745],{"type":418,"value":12854},{"type":413,"tag":496,"props":13747,"children":13748},{"icon":4578},[13749],{"type":413,"tag":414,"props":13750,"children":13751},{},[13752,13754,13761],{"type":418,"value":13753},"If you need more compressing archives options, you can check ",{"type":413,"tag":432,"props":13755,"children":13758},{"href":13756,"rel":13757},"https://nuke.build/docs/common/compression/#compressing-archives",[436],[13759],{"type":418,"value":13760},"Nuke documentation",{"type":418,"value":13762},", they have some utilities to do more complex scenarios.",{"type":413,"tag":414,"props":13764,"children":13765},{},[13766,13768,13775,13777,13782,13784,13790],{"type":418,"value":13767},"You may have noted on the line where I set the project that I have ",{"type":413,"tag":432,"props":13769,"children":13772},{"href":13770,"rel":13771},"https://nuke.build/docs/common/solution-project-model/#strong-typed-project-access",[436],[13773],{"type":418,"value":13774},"strong-typed access to the projects in my solution",{"type":418,"value":13776},". This is possible by adding this field with the ",{"type":413,"tag":619,"props":13778,"children":13780},{"className":13779},[],[13781],{"type":418,"value":13150},{"type":418,"value":13783}," attribute and its ",{"type":413,"tag":619,"props":13785,"children":13787},{"className":13786},[],[13788],{"type":418,"value":13789},"GenerateProjects",{"type":418,"value":13791}," property set to true.",{"type":413,"tag":612,"props":13793,"children":13795},{"className":981,"code":13794,"language":326,"meta":401,"style":401},"[Solution(GenerateProjects = true)]  \nreadonly Solution Solution;\n",[13796],{"type":413,"tag":619,"props":13797,"children":13798},{"__ignoreMap":401},[13799,13836],{"type":413,"tag":623,"props":13800,"children":13801},{"class":625,"line":626},[13802,13807,13811,13815,13819,13823,13827,13832],{"type":413,"tag":623,"props":13803,"children":13804},{"style":671},[13805],{"type":418,"value":13806},"[",{"type":413,"tag":623,"props":13808,"children":13809},{"style":630},[13810],{"type":418,"value":13150},{"type":413,"tag":623,"props":13812,"children":13813},{"style":671},[13814],{"type":418,"value":1018},{"type":413,"tag":623,"props":13816,"children":13817},{"style":630},[13818],{"type":418,"value":13789},{"type":413,"tag":623,"props":13820,"children":13821},{"style":671},[13822],{"type":418,"value":1004},{"type":413,"tag":623,"props":13824,"children":13825},{"style":5580},[13826],{"type":418,"value":9472},{"type":413,"tag":623,"props":13828,"children":13829},{"style":671},[13830],{"type":418,"value":13831},")]",{"type":413,"tag":623,"props":13833,"children":13834},{"style":1058},[13835],{"type":418,"value":12706},{"type":413,"tag":623,"props":13837,"children":13838},{"class":625,"line":1045},[13839,13844,13849,13853],{"type":413,"tag":623,"props":13840,"children":13841},{"style":2854},[13842],{"type":418,"value":13843},"readonly",{"type":413,"tag":623,"props":13845,"children":13846},{"style":630},[13847],{"type":418,"value":13848}," Solution",{"type":413,"tag":623,"props":13850,"children":13851},{"style":630},[13852],{"type":418,"value":13848},{"type":413,"tag":623,"props":13854,"children":13855},{"style":671},[13856],{"type":418,"value":2524},{"type":413,"tag":13858,"props":13859,"children":13861},"calout",{"icon":13860},"i-fluent-emoji-magic-wand",[13862],{"type":413,"tag":414,"props":13863,"children":13864},{},[13865,13867,13874],{"type":418,"value":13866},"It looks like magic but it's not! Nuke uses a ",{"type":413,"tag":432,"props":13868,"children":13871},{"href":13869,"rel":13870},"https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview",[436],[13872],{"type":418,"value":13873},"source generator",{"type":418,"value":13875},"  to do that behind the scenes.",{"type":413,"tag":420,"props":13877,"children":13879},{"id":13878},"provisioning-the-app-service-with-pulumi",[13880],{"type":418,"value":13881},"Provisioning the App Service with Pulumi",{"type":413,"tag":600,"props":13883,"children":13885},{"id":13884},"the-pulumi-project",[13886],{"type":418,"value":13887},"The Pulumi project",{"type":413,"tag":414,"props":13889,"children":13890},{},[13891,13893,13899,13901,13907],{"type":418,"value":13892},"By default, the infrastructure code is contained in the ",{"type":413,"tag":619,"props":13894,"children":13896},{"className":13895},[],[13897],{"type":418,"value":13898},"Program.cs",{"type":418,"value":13900}," file of our project. The resources to provision are declared in the lambda in parameter of the ",{"type":413,"tag":619,"props":13902,"children":13904},{"className":13903},[],[13905],{"type":418,"value":13906},"Deployment.RunAsync",{"type":418,"value":13908}," method.",{"type":413,"tag":414,"props":13910,"children":13911},{},[13912],{"type":413,"tag":487,"props":13913,"children":13917},{"alt":13914,"className":13915,"src":13916},"Pulumi program file opened in the IDE.",[491,492],"/posts/images/pulumi_met_nuke_10.png",[],{"type":413,"tag":496,"props":13919,"children":13920},{"icon":557},[13921],{"type":413,"tag":414,"props":13922,"children":13923},{},[13924],{"type":418,"value":13925},"The project uses the top-level statement feature of C#.",{"type":413,"tag":414,"props":13927,"children":13928},{},[13929,13931,13936],{"type":418,"value":13930},"As we don't have many resources to declare for our scenario we will keep all the code in the ",{"type":413,"tag":619,"props":13932,"children":13934},{"className":13933},[],[13935],{"type":418,"value":13898},{"type":418,"value":13937}," file but that is not what you would do in a more complex project.",{"type":413,"tag":600,"props":13939,"children":13941},{"id":13940},"azure-resources-to-provision",[13942],{"type":418,"value":13943},"Azure resources to provision",{"type":413,"tag":414,"props":13945,"children":13946},{},[13947],{"type":418,"value":13948},"There are 3 Azure resources we need to create in our stack (instance of a Pulumi program):",{"type":413,"tag":443,"props":13950,"children":13951},{},[13952,13957,13969],{"type":413,"tag":447,"props":13953,"children":13954},{},[13955],{"type":418,"value":13956},"a resource group to contain the different Azure resources",{"type":413,"tag":447,"props":13958,"children":13959},{},[13960,13962],{"type":418,"value":13961},"an App Service Plan which ",{"type":413,"tag":432,"props":13963,"children":13966},{"href":13964,"rel":13965},"https://learn.microsoft.com/en-us/azure/app-service/overview-hosting-plans",[436],[13967],{"type":418,"value":13968},"defines the set of compute resources for a web app to run",{"type":413,"tag":447,"props":13970,"children":13971},{},[13972],{"type":418,"value":13973},"a Web App / App Service which is where the API will be deployed",{"type":413,"tag":612,"props":13975,"children":13977},{"className":981,"code":13976,"language":326,"meta":401,"style":401},"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",[13978],{"type":413,"tag":619,"props":13979,"children":13980},{"__ignoreMap":401},[13981,14091,14098,14213,14224,14256,14289,14314,14325,14358,14391,14403,14415,14422,14538,14549,14580,14605],{"type":413,"tag":623,"props":13982,"children":13983},{"class":625,"line":626},[13984,13988,13993,13997,14001,14006,14010,14014,14019,14023,14028,14032,14037,14041,14046,14050,14054,14058,14062,14066,14070,14074,14079,14083,14087],{"type":413,"tag":623,"props":13985,"children":13986},{"style":630},[13987],{"type":418,"value":994},{"type":413,"tag":623,"props":13989,"children":13990},{"style":630},[13991],{"type":418,"value":13992}," resourceGroup",{"type":413,"tag":623,"props":13994,"children":13995},{"style":671},[13996],{"type":418,"value":1004},{"type":413,"tag":623,"props":13998,"children":13999},{"style":671},[14000],{"type":418,"value":638},{"type":413,"tag":623,"props":14002,"children":14003},{"style":630},[14004],{"type":418,"value":14005}," ResourceGroup",{"type":413,"tag":623,"props":14007,"children":14008},{"style":671},[14009],{"type":418,"value":1018},{"type":413,"tag":623,"props":14011,"children":14012},{"style":671},[14013],{"type":418,"value":2446},{"type":413,"tag":623,"props":14015,"children":14016},{"style":635},[14017],{"type":418,"value":14018},"rg-",{"type":413,"tag":623,"props":14020,"children":14021},{"style":671},[14022],{"type":418,"value":2456},{"type":413,"tag":623,"props":14024,"children":14025},{"style":1058},[14026],{"type":418,"value":14027},"Deployment",{"type":413,"tag":623,"props":14029,"children":14030},{"style":671},[14031],{"type":418,"value":1404},{"type":413,"tag":623,"props":14033,"children":14034},{"style":1058},[14035],{"type":418,"value":14036},"Instance",{"type":413,"tag":623,"props":14038,"children":14039},{"style":671},[14040],{"type":418,"value":1404},{"type":413,"tag":623,"props":14042,"children":14043},{"style":1058},[14044],{"type":418,"value":14045},"ProjectName",{"type":413,"tag":623,"props":14047,"children":14048},{"style":671},[14049],{"type":418,"value":3327},{"type":413,"tag":623,"props":14051,"children":14052},{"style":635},[14053],{"type":418,"value":3503},{"type":413,"tag":623,"props":14055,"children":14056},{"style":671},[14057],{"type":418,"value":2456},{"type":413,"tag":623,"props":14059,"children":14060},{"style":1058},[14061],{"type":418,"value":14027},{"type":413,"tag":623,"props":14063,"children":14064},{"style":671},[14065],{"type":418,"value":1404},{"type":413,"tag":623,"props":14067,"children":14068},{"style":1058},[14069],{"type":418,"value":14036},{"type":413,"tag":623,"props":14071,"children":14072},{"style":671},[14073],{"type":418,"value":1404},{"type":413,"tag":623,"props":14075,"children":14076},{"style":1058},[14077],{"type":418,"value":14078},"StackName",{"type":413,"tag":623,"props":14080,"children":14081},{"style":671},[14082],{"type":418,"value":2465},{"type":413,"tag":623,"props":14084,"children":14085},{"style":671},[14086],{"type":418,"value":12817},{"type":413,"tag":623,"props":14088,"children":14089},{"style":1058},[14090],{"type":418,"value":12706},{"type":413,"tag":623,"props":14092,"children":14093},{"class":625,"line":1045},[14094],{"type":413,"tag":623,"props":14095,"children":14096},{"style":1058},[14097],{"type":418,"value":12706},{"type":413,"tag":623,"props":14099,"children":14100},{"class":625,"line":1054},[14101,14105,14110,14114,14118,14123,14127,14131,14136,14140,14144,14148,14152,14156,14160,14164,14168,14172,14176,14180,14184,14188,14192,14196,14200,14204,14209],{"type":413,"tag":623,"props":14102,"children":14103},{"style":630},[14104],{"type":418,"value":994},{"type":413,"tag":623,"props":14106,"children":14107},{"style":630},[14108],{"type":418,"value":14109}," appServicePlan",{"type":413,"tag":623,"props":14111,"children":14112},{"style":671},[14113],{"type":418,"value":1004},{"type":413,"tag":623,"props":14115,"children":14116},{"style":671},[14117],{"type":418,"value":638},{"type":413,"tag":623,"props":14119,"children":14120},{"style":630},[14121],{"type":418,"value":14122}," AppServicePlan",{"type":413,"tag":623,"props":14124,"children":14125},{"style":671},[14126],{"type":418,"value":1018},{"type":413,"tag":623,"props":14128,"children":14129},{"style":671},[14130],{"type":418,"value":2446},{"type":413,"tag":623,"props":14132,"children":14133},{"style":635},[14134],{"type":418,"value":14135},"plan-",{"type":413,"tag":623,"props":14137,"children":14138},{"style":671},[14139],{"type":418,"value":2456},{"type":413,"tag":623,"props":14141,"children":14142},{"style":1058},[14143],{"type":418,"value":14027},{"type":413,"tag":623,"props":14145,"children":14146},{"style":671},[14147],{"type":418,"value":1404},{"type":413,"tag":623,"props":14149,"children":14150},{"style":1058},[14151],{"type":418,"value":14036},{"type":413,"tag":623,"props":14153,"children":14154},{"style":671},[14155],{"type":418,"value":1404},{"type":413,"tag":623,"props":14157,"children":14158},{"style":1058},[14159],{"type":418,"value":14045},{"type":413,"tag":623,"props":14161,"children":14162},{"style":671},[14163],{"type":418,"value":3327},{"type":413,"tag":623,"props":14165,"children":14166},{"style":635},[14167],{"type":418,"value":3503},{"type":413,"tag":623,"props":14169,"children":14170},{"style":671},[14171],{"type":418,"value":2456},{"type":413,"tag":623,"props":14173,"children":14174},{"style":1058},[14175],{"type":418,"value":14027},{"type":413,"tag":623,"props":14177,"children":14178},{"style":671},[14179],{"type":418,"value":1404},{"type":413,"tag":623,"props":14181,"children":14182},{"style":1058},[14183],{"type":418,"value":14036},{"type":413,"tag":623,"props":14185,"children":14186},{"style":671},[14187],{"type":418,"value":1404},{"type":413,"tag":623,"props":14189,"children":14190},{"style":1058},[14191],{"type":418,"value":14078},{"type":413,"tag":623,"props":14193,"children":14194},{"style":671},[14195],{"type":418,"value":2465},{"type":413,"tag":623,"props":14197,"children":14198},{"style":671},[14199],{"type":418,"value":1037},{"type":413,"tag":623,"props":14201,"children":14202},{"style":671},[14203],{"type":418,"value":638},{"type":413,"tag":623,"props":14205,"children":14206},{"style":630},[14207],{"type":418,"value":14208}," AppServicePlanArgs",{"type":413,"tag":623,"props":14210,"children":14211},{"style":1058},[14212],{"type":418,"value":12706},{"type":413,"tag":623,"props":14214,"children":14215},{"class":625,"line":1087},[14216,14220],{"type":413,"tag":623,"props":14217,"children":14218},{"style":671},[14219],{"type":418,"value":2456},{"type":413,"tag":623,"props":14221,"children":14222},{"style":1058},[14223],{"type":418,"value":12706},{"type":413,"tag":623,"props":14225,"children":14226},{"class":625,"line":1104},[14227,14232,14236,14240,14244,14248,14252],{"type":413,"tag":623,"props":14228,"children":14229},{"style":1058},[14230],{"type":418,"value":14231},"    ResourceGroupName ",{"type":413,"tag":623,"props":14233,"children":14234},{"style":671},[14235],{"type":418,"value":1066},{"type":413,"tag":623,"props":14237,"children":14238},{"style":1058},[14239],{"type":418,"value":13992},{"type":413,"tag":623,"props":14241,"children":14242},{"style":671},[14243],{"type":418,"value":1404},{"type":413,"tag":623,"props":14245,"children":14246},{"style":1058},[14247],{"type":418,"value":3350},{"type":413,"tag":623,"props":14249,"children":14250},{"style":671},[14251],{"type":418,"value":1037},{"type":413,"tag":623,"props":14253,"children":14254},{"style":1058},[14255],{"type":418,"value":12706},{"type":413,"tag":623,"props":14257,"children":14258},{"class":625,"line":1113},[14259,14264,14268,14272,14277,14281,14285],{"type":413,"tag":623,"props":14260,"children":14261},{"style":1058},[14262],{"type":418,"value":14263},"    Kind ",{"type":413,"tag":623,"props":14265,"children":14266},{"style":671},[14267],{"type":418,"value":1066},{"type":413,"tag":623,"props":14269,"children":14270},{"style":671},[14271],{"type":418,"value":674},{"type":413,"tag":623,"props":14273,"children":14274},{"style":635},[14275],{"type":418,"value":14276},"App",{"type":413,"tag":623,"props":14278,"children":14279},{"style":671},[14280],{"type":418,"value":1023},{"type":413,"tag":623,"props":14282,"children":14283},{"style":671},[14284],{"type":418,"value":1037},{"type":413,"tag":623,"props":14286,"children":14287},{"style":1058},[14288],{"type":418,"value":12706},{"type":413,"tag":623,"props":14290,"children":14291},{"class":625,"line":1161},[14292,14297,14301,14305,14310],{"type":413,"tag":623,"props":14293,"children":14294},{"style":1058},[14295],{"type":418,"value":14296},"    Sku ",{"type":413,"tag":623,"props":14298,"children":14299},{"style":671},[14300],{"type":418,"value":1066},{"type":413,"tag":623,"props":14302,"children":14303},{"style":671},[14304],{"type":418,"value":638},{"type":413,"tag":623,"props":14306,"children":14307},{"style":630},[14308],{"type":418,"value":14309}," SkuDescriptionArgs",{"type":413,"tag":623,"props":14311,"children":14312},{"style":1058},[14313],{"type":418,"value":12706},{"type":413,"tag":623,"props":14315,"children":14316},{"class":625,"line":1207},[14317,14321],{"type":413,"tag":623,"props":14318,"children":14319},{"style":671},[14320],{"type":418,"value":12739},{"type":413,"tag":623,"props":14322,"children":14323},{"style":1058},[14324],{"type":418,"value":12706},{"type":413,"tag":623,"props":14326,"children":14327},{"class":625,"line":1251},[14328,14333,14337,14341,14346,14350,14354],{"type":413,"tag":623,"props":14329,"children":14330},{"style":1058},[14331],{"type":418,"value":14332},"        Tier ",{"type":413,"tag":623,"props":14334,"children":14335},{"style":671},[14336],{"type":418,"value":1066},{"type":413,"tag":623,"props":14338,"children":14339},{"style":671},[14340],{"type":418,"value":674},{"type":413,"tag":623,"props":14342,"children":14343},{"style":635},[14344],{"type":418,"value":14345},"Basic",{"type":413,"tag":623,"props":14347,"children":14348},{"style":671},[14349],{"type":418,"value":1023},{"type":413,"tag":623,"props":14351,"children":14352},{"style":671},[14353],{"type":418,"value":1037},{"type":413,"tag":623,"props":14355,"children":14356},{"style":1058},[14357],{"type":418,"value":12706},{"type":413,"tag":623,"props":14359,"children":14360},{"class":625,"line":1296},[14361,14366,14370,14374,14379,14383,14387],{"type":413,"tag":623,"props":14362,"children":14363},{"style":1058},[14364],{"type":418,"value":14365},"        Name ",{"type":413,"tag":623,"props":14367,"children":14368},{"style":671},[14369],{"type":418,"value":1066},{"type":413,"tag":623,"props":14371,"children":14372},{"style":671},[14373],{"type":418,"value":674},{"type":413,"tag":623,"props":14375,"children":14376},{"style":635},[14377],{"type":418,"value":14378},"B1",{"type":413,"tag":623,"props":14380,"children":14381},{"style":671},[14382],{"type":418,"value":1023},{"type":413,"tag":623,"props":14384,"children":14385},{"style":671},[14386],{"type":418,"value":1037},{"type":413,"tag":623,"props":14388,"children":14389},{"style":1058},[14390],{"type":418,"value":12706},{"type":413,"tag":623,"props":14392,"children":14393},{"class":625,"line":1337},[14394,14399],{"type":413,"tag":623,"props":14395,"children":14396},{"style":671},[14397],{"type":418,"value":14398},"    },",{"type":413,"tag":623,"props":14400,"children":14401},{"style":1058},[14402],{"type":418,"value":12706},{"type":413,"tag":623,"props":14404,"children":14405},{"class":625,"line":1346},[14406,14411],{"type":413,"tag":623,"props":14407,"children":14408},{"style":671},[14409],{"type":418,"value":14410},"});",{"type":413,"tag":623,"props":14412,"children":14413},{"style":1058},[14414],{"type":418,"value":12706},{"type":413,"tag":623,"props":14416,"children":14417},{"class":625,"line":2100},[14418],{"type":413,"tag":623,"props":14419,"children":14420},{"style":1058},[14421],{"type":418,"value":12706},{"type":413,"tag":623,"props":14423,"children":14424},{"class":625,"line":2897},[14425,14429,14434,14438,14442,14447,14451,14455,14460,14464,14468,14472,14476,14480,14484,14488,14492,14496,14500,14504,14508,14512,14516,14520,14524,14528,14533],{"type":413,"tag":623,"props":14426,"children":14427},{"style":630},[14428],{"type":418,"value":994},{"type":413,"tag":623,"props":14430,"children":14431},{"style":630},[14432],{"type":418,"value":14433}," appService",{"type":413,"tag":623,"props":14435,"children":14436},{"style":671},[14437],{"type":418,"value":1004},{"type":413,"tag":623,"props":14439,"children":14440},{"style":671},[14441],{"type":418,"value":638},{"type":413,"tag":623,"props":14443,"children":14444},{"style":630},[14445],{"type":418,"value":14446}," WebApp",{"type":413,"tag":623,"props":14448,"children":14449},{"style":671},[14450],{"type":418,"value":1018},{"type":413,"tag":623,"props":14452,"children":14453},{"style":671},[14454],{"type":418,"value":2446},{"type":413,"tag":623,"props":14456,"children":14457},{"style":635},[14458],{"type":418,"value":14459},"app-",{"type":413,"tag":623,"props":14461,"children":14462},{"style":671},[14463],{"type":418,"value":2456},{"type":413,"tag":623,"props":14465,"children":14466},{"style":1058},[14467],{"type":418,"value":14027},{"type":413,"tag":623,"props":14469,"children":14470},{"style":671},[14471],{"type":418,"value":1404},{"type":413,"tag":623,"props":14473,"children":14474},{"style":1058},[14475],{"type":418,"value":14036},{"type":413,"tag":623,"props":14477,"children":14478},{"style":671},[14479],{"type":418,"value":1404},{"type":413,"tag":623,"props":14481,"children":14482},{"style":1058},[14483],{"type":418,"value":14045},{"type":413,"tag":623,"props":14485,"children":14486},{"style":671},[14487],{"type":418,"value":3327},{"type":413,"tag":623,"props":14489,"children":14490},{"style":635},[14491],{"type":418,"value":3503},{"type":413,"tag":623,"props":14493,"children":14494},{"style":671},[14495],{"type":418,"value":2456},{"type":413,"tag":623,"props":14497,"children":14498},{"style":1058},[14499],{"type":418,"value":14027},{"type":413,"tag":623,"props":14501,"children":14502},{"style":671},[14503],{"type":418,"value":1404},{"type":413,"tag":623,"props":14505,"children":14506},{"style":1058},[14507],{"type":418,"value":14036},{"type":413,"tag":623,"props":14509,"children":14510},{"style":671},[14511],{"type":418,"value":1404},{"type":413,"tag":623,"props":14513,"children":14514},{"style":1058},[14515],{"type":418,"value":14078},{"type":413,"tag":623,"props":14517,"children":14518},{"style":671},[14519],{"type":418,"value":2465},{"type":413,"tag":623,"props":14521,"children":14522},{"style":671},[14523],{"type":418,"value":1037},{"type":413,"tag":623,"props":14525,"children":14526},{"style":671},[14527],{"type":418,"value":638},{"type":413,"tag":623,"props":14529,"children":14530},{"style":630},[14531],{"type":418,"value":14532}," WebAppArgs",{"type":413,"tag":623,"props":14534,"children":14535},{"style":1058},[14536],{"type":418,"value":14537},"   \n",{"type":413,"tag":623,"props":14539,"children":14540},{"class":625,"line":2926},[14541,14545],{"type":413,"tag":623,"props":14542,"children":14543},{"style":671},[14544],{"type":418,"value":2456},{"type":413,"tag":623,"props":14546,"children":14547},{"style":1058},[14548],{"type":418,"value":14537},{"type":413,"tag":623,"props":14550,"children":14551},{"class":625,"line":2957},[14552,14556,14560,14564,14568,14572,14576],{"type":413,"tag":623,"props":14553,"children":14554},{"style":1058},[14555],{"type":418,"value":14231},{"type":413,"tag":623,"props":14557,"children":14558},{"style":671},[14559],{"type":418,"value":1066},{"type":413,"tag":623,"props":14561,"children":14562},{"style":1058},[14563],{"type":418,"value":13992},{"type":413,"tag":623,"props":14565,"children":14566},{"style":671},[14567],{"type":418,"value":1404},{"type":413,"tag":623,"props":14569,"children":14570},{"style":1058},[14571],{"type":418,"value":3350},{"type":413,"tag":623,"props":14573,"children":14574},{"style":671},[14575],{"type":418,"value":1037},{"type":413,"tag":623,"props":14577,"children":14578},{"style":1058},[14579],{"type":418,"value":12706},{"type":413,"tag":623,"props":14581,"children":14582},{"class":625,"line":2988},[14583,14588,14592,14596,14600],{"type":413,"tag":623,"props":14584,"children":14585},{"style":1058},[14586],{"type":418,"value":14587},"    ServerFarmId ",{"type":413,"tag":623,"props":14589,"children":14590},{"style":671},[14591],{"type":418,"value":1066},{"type":413,"tag":623,"props":14593,"children":14594},{"style":1058},[14595],{"type":418,"value":14109},{"type":413,"tag":623,"props":14597,"children":14598},{"style":671},[14599],{"type":418,"value":1404},{"type":413,"tag":623,"props":14601,"children":14602},{"style":1058},[14603],{"type":418,"value":14604},"Id  \n",{"type":413,"tag":623,"props":14606,"children":14607},{"class":625,"line":3045},[14608],{"type":413,"tag":623,"props":14609,"children":14610},{"style":671},[14611],{"type":418,"value":1352},{"type":413,"tag":414,"props":14613,"children":14614},{},[14615],{"type":418,"value":14616},"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":413,"tag":496,"props":14618,"children":14619},{"icon":4578},[14620],{"type":413,"tag":414,"props":14621,"children":14622},{},[14623],{"type":418,"value":14624},"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":413,"tag":600,"props":14626,"children":14628},{"id":14627},"stack-outputs",[14629],{"type":418,"value":14630},"Stack outputs",{"type":413,"tag":414,"props":14632,"children":14633},{},[14634],{"type":418,"value":14635},"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":413,"tag":414,"props":14637,"children":14638},{},[14639,14641,14647],{"type":418,"value":14640},"First, we will need to have the name of the provisioned App Service. That's easy it's the property Name of the ",{"type":413,"tag":619,"props":14642,"children":14644},{"className":14643},[],[14645],{"type":418,"value":14646},"appService",{"type":418,"value":14648}," variable.",{"type":413,"tag":414,"props":14650,"children":14651},{},[14652,14654,14661],{"type":418,"value":14653},"Second, because we are going to use the Kudu API to zip deploy our application to the App Service, we will need the ",{"type":413,"tag":432,"props":14655,"children":14658},{"href":14656,"rel":14657},"https://github.com/projectkudu/kudu/wiki/Deployment-credentials#site-credentials-aka-publish-profile-credentials",[436],[14659],{"type":418,"value":14660},"site credentials (aka the Publishing Profile Credentials)",{"type":418,"value":14662},". These can be retrieved in the Pulumi program using the following code:",{"type":413,"tag":612,"props":14664,"children":14666},{"className":981,"code":14665,"language":326,"meta":401,"style":401},"var publishingCredentials = ListWebAppPublishingCredentials.Invoke(new()  \n{  \n    ResourceGroupName = resourceGroup.Name,  \n    Name = appService.Name  \n});\n",[14667],{"type":413,"tag":619,"props":14668,"children":14669},{"__ignoreMap":401},[14670,14708,14719,14750,14774],{"type":413,"tag":623,"props":14671,"children":14672},{"class":625,"line":626},[14673,14677,14682,14686,14691,14695,14699,14704],{"type":413,"tag":623,"props":14674,"children":14675},{"style":630},[14676],{"type":418,"value":994},{"type":413,"tag":623,"props":14678,"children":14679},{"style":630},[14680],{"type":418,"value":14681}," publishingCredentials",{"type":413,"tag":623,"props":14683,"children":14684},{"style":671},[14685],{"type":418,"value":1004},{"type":413,"tag":623,"props":14687,"children":14688},{"style":1058},[14689],{"type":418,"value":14690}," ListWebAppPublishingCredentials",{"type":413,"tag":623,"props":14692,"children":14693},{"style":671},[14694],{"type":418,"value":1404},{"type":413,"tag":623,"props":14696,"children":14697},{"style":1407},[14698],{"type":418,"value":1410},{"type":413,"tag":623,"props":14700,"children":14701},{"style":671},[14702],{"type":418,"value":14703},"(new()",{"type":413,"tag":623,"props":14705,"children":14706},{"style":1058},[14707],{"type":418,"value":12706},{"type":413,"tag":623,"props":14709,"children":14710},{"class":625,"line":1045},[14711,14715],{"type":413,"tag":623,"props":14712,"children":14713},{"style":671},[14714],{"type":418,"value":2456},{"type":413,"tag":623,"props":14716,"children":14717},{"style":1058},[14718],{"type":418,"value":12706},{"type":413,"tag":623,"props":14720,"children":14721},{"class":625,"line":1054},[14722,14726,14730,14734,14738,14742,14746],{"type":413,"tag":623,"props":14723,"children":14724},{"style":1058},[14725],{"type":418,"value":14231},{"type":413,"tag":623,"props":14727,"children":14728},{"style":671},[14729],{"type":418,"value":1066},{"type":413,"tag":623,"props":14731,"children":14732},{"style":1058},[14733],{"type":418,"value":13992},{"type":413,"tag":623,"props":14735,"children":14736},{"style":671},[14737],{"type":418,"value":1404},{"type":413,"tag":623,"props":14739,"children":14740},{"style":1058},[14741],{"type":418,"value":3350},{"type":413,"tag":623,"props":14743,"children":14744},{"style":671},[14745],{"type":418,"value":1037},{"type":413,"tag":623,"props":14747,"children":14748},{"style":1058},[14749],{"type":418,"value":12706},{"type":413,"tag":623,"props":14751,"children":14752},{"class":625,"line":1087},[14753,14757,14761,14765,14769],{"type":413,"tag":623,"props":14754,"children":14755},{"style":1058},[14756],{"type":418,"value":1459},{"type":413,"tag":623,"props":14758,"children":14759},{"style":671},[14760],{"type":418,"value":1066},{"type":413,"tag":623,"props":14762,"children":14763},{"style":1058},[14764],{"type":418,"value":14433},{"type":413,"tag":623,"props":14766,"children":14767},{"style":671},[14768],{"type":418,"value":1404},{"type":413,"tag":623,"props":14770,"children":14771},{"style":1058},[14772],{"type":418,"value":14773},"Name  \n",{"type":413,"tag":623,"props":14775,"children":14776},{"class":625,"line":1104},[14777],{"type":413,"tag":623,"props":14778,"children":14779},{"style":671},[14780],{"type":418,"value":1352},{"type":413,"tag":496,"props":14782,"children":14783},{"icon":557},[14784],{"type":413,"tag":414,"props":14785,"children":14786},{},[14787,14789,14796],{"type":418,"value":14788},"Using the Kudu API is just one of the ",{"type":413,"tag":432,"props":14790,"children":14793},{"href":14791,"rel":14792},"https://learn.microsoft.com/en-us/azure/app-service/deploy-zip?tabs=cli#deploy-a-zip-package",[436],[14794],{"type":418,"value":14795},"many ways",{"type":418,"value":14797}," 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":413,"tag":414,"props":14799,"children":14800},{},[14801,14803,14809],{"type":418,"value":14802},"Pulumi, like Terraform, has this concept of stack ",{"type":413,"tag":432,"props":14804,"children":14807},{"href":14805,"rel":14806},"https://www.pulumi.com/docs/intro/concepts/stack/#outputs",[436],[14808],{"type":418,"value":5679},{"type":418,"value":14810},"  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":413,"tag":612,"props":14812,"children":14814},{"className":981,"code":14813,"language":326,"meta":401,"style":401},"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",[14815],{"type":413,"tag":619,"props":14816,"children":14817},{"__ignoreMap":401},[14818,14854,14865,14957,15046,15086],{"type":413,"tag":623,"props":14819,"children":14820},{"class":625,"line":626},[14821,14825,14829,14833,14837,14841,14845,14850],{"type":413,"tag":623,"props":14822,"children":14823},{"style":2485},[14824],{"type":418,"value":4413},{"type":413,"tag":623,"props":14826,"children":14827},{"style":671},[14828],{"type":418,"value":638},{"type":413,"tag":623,"props":14830,"children":14831},{"style":630},[14832],{"type":418,"value":4422},{"type":413,"tag":623,"props":14834,"children":14835},{"style":671},[14836],{"type":418,"value":4427},{"type":413,"tag":623,"props":14838,"children":14839},{"style":671},[14840],{"type":418,"value":4432},{"type":413,"tag":623,"props":14842,"children":14843},{"style":671},[14844],{"type":418,"value":1037},{"type":413,"tag":623,"props":14846,"children":14847},{"style":671},[14848],{"type":418,"value":14849}," object?>",{"type":413,"tag":623,"props":14851,"children":14852},{"style":1058},[14853],{"type":418,"value":12706},{"type":413,"tag":623,"props":14855,"children":14856},{"class":625,"line":1045},[14857,14861],{"type":413,"tag":623,"props":14858,"children":14859},{"style":671},[14860],{"type":418,"value":2456},{"type":413,"tag":623,"props":14862,"children":14863},{"style":1058},[14864],{"type":418,"value":12706},{"type":413,"tag":623,"props":14866,"children":14867},{"class":625,"line":1054},[14868,14872,14876,14881,14885,14889,14893,14897,14901,14906,14910,14915,14919,14923,14927,14931,14935,14939,14943,14948,14953],{"type":413,"tag":623,"props":14869,"children":14870},{"style":671},[14871],{"type":418,"value":4456},{"type":413,"tag":623,"props":14873,"children":14874},{"style":671},[14875],{"type":418,"value":1023},{"type":413,"tag":623,"props":14877,"children":14878},{"style":635},[14879],{"type":418,"value":14880},"publishingUsername",{"type":413,"tag":623,"props":14882,"children":14883},{"style":671},[14884],{"type":418,"value":1023},{"type":413,"tag":623,"props":14886,"children":14887},{"style":671},[14888],{"type":418,"value":1137},{"type":413,"tag":623,"props":14890,"children":14891},{"style":671},[14892],{"type":418,"value":1004},{"type":413,"tag":623,"props":14894,"children":14895},{"style":1058},[14896],{"type":418,"value":3003},{"type":413,"tag":623,"props":14898,"children":14899},{"style":671},[14900],{"type":418,"value":1404},{"type":413,"tag":623,"props":14902,"children":14903},{"style":1407},[14904],{"type":418,"value":14905},"CreateSecret",{"type":413,"tag":623,"props":14907,"children":14908},{"style":671},[14909],{"type":418,"value":1018},{"type":413,"tag":623,"props":14911,"children":14912},{"style":1058},[14913],{"type":418,"value":14914},"publishingCredentials",{"type":413,"tag":623,"props":14916,"children":14917},{"style":671},[14918],{"type":418,"value":1404},{"type":413,"tag":623,"props":14920,"children":14921},{"style":1407},[14922],{"type":418,"value":2187},{"type":413,"tag":623,"props":14924,"children":14925},{"style":671},[14926],{"type":418,"value":1018},{"type":413,"tag":623,"props":14928,"children":14929},{"style":630},[14930],{"type":418,"value":2196},{"type":413,"tag":623,"props":14932,"children":14933},{"style":671},[14934],{"type":418,"value":2201},{"type":413,"tag":623,"props":14936,"children":14937},{"style":1058},[14938],{"type":418,"value":2206},{"type":413,"tag":623,"props":14940,"children":14941},{"style":671},[14942],{"type":418,"value":1404},{"type":413,"tag":623,"props":14944,"children":14945},{"style":1058},[14946],{"type":418,"value":14947},"PublishingUserName",{"type":413,"tag":623,"props":14949,"children":14950},{"style":671},[14951],{"type":418,"value":14952},")),",{"type":413,"tag":623,"props":14954,"children":14955},{"style":1058},[14956],{"type":418,"value":12706},{"type":413,"tag":623,"props":14958,"children":14959},{"class":625,"line":1087},[14960,14964,14968,14973,14977,14981,14985,14989,14993,14997,15001,15005,15009,15013,15017,15021,15025,15029,15033,15038,15042],{"type":413,"tag":623,"props":14961,"children":14962},{"style":671},[14963],{"type":418,"value":4456},{"type":413,"tag":623,"props":14965,"children":14966},{"style":671},[14967],{"type":418,"value":1023},{"type":413,"tag":623,"props":14969,"children":14970},{"style":635},[14971],{"type":418,"value":14972},"publishingUserPassword",{"type":413,"tag":623,"props":14974,"children":14975},{"style":671},[14976],{"type":418,"value":1023},{"type":413,"tag":623,"props":14978,"children":14979},{"style":671},[14980],{"type":418,"value":1137},{"type":413,"tag":623,"props":14982,"children":14983},{"style":671},[14984],{"type":418,"value":1004},{"type":413,"tag":623,"props":14986,"children":14987},{"style":1058},[14988],{"type":418,"value":3003},{"type":413,"tag":623,"props":14990,"children":14991},{"style":671},[14992],{"type":418,"value":1404},{"type":413,"tag":623,"props":14994,"children":14995},{"style":1407},[14996],{"type":418,"value":14905},{"type":413,"tag":623,"props":14998,"children":14999},{"style":671},[15000],{"type":418,"value":1018},{"type":413,"tag":623,"props":15002,"children":15003},{"style":1058},[15004],{"type":418,"value":14914},{"type":413,"tag":623,"props":15006,"children":15007},{"style":671},[15008],{"type":418,"value":1404},{"type":413,"tag":623,"props":15010,"children":15011},{"style":1407},[15012],{"type":418,"value":2187},{"type":413,"tag":623,"props":15014,"children":15015},{"style":671},[15016],{"type":418,"value":1018},{"type":413,"tag":623,"props":15018,"children":15019},{"style":630},[15020],{"type":418,"value":2196},{"type":413,"tag":623,"props":15022,"children":15023},{"style":671},[15024],{"type":418,"value":2201},{"type":413,"tag":623,"props":15026,"children":15027},{"style":1058},[15028],{"type":418,"value":2206},{"type":413,"tag":623,"props":15030,"children":15031},{"style":671},[15032],{"type":418,"value":1404},{"type":413,"tag":623,"props":15034,"children":15035},{"style":1058},[15036],{"type":418,"value":15037},"PublishingPassword",{"type":413,"tag":623,"props":15039,"children":15040},{"style":671},[15041],{"type":418,"value":14952},{"type":413,"tag":623,"props":15043,"children":15044},{"style":1058},[15045],{"type":418,"value":12706},{"type":413,"tag":623,"props":15047,"children":15048},{"class":625,"line":1104},[15049,15053,15057,15062,15066,15070,15074,15078,15082],{"type":413,"tag":623,"props":15050,"children":15051},{"style":671},[15052],{"type":418,"value":4456},{"type":413,"tag":623,"props":15054,"children":15055},{"style":671},[15056],{"type":418,"value":1023},{"type":413,"tag":623,"props":15058,"children":15059},{"style":635},[15060],{"type":418,"value":15061},"appServiceName",{"type":413,"tag":623,"props":15063,"children":15064},{"style":671},[15065],{"type":418,"value":1023},{"type":413,"tag":623,"props":15067,"children":15068},{"style":671},[15069],{"type":418,"value":1137},{"type":413,"tag":623,"props":15071,"children":15072},{"style":671},[15073],{"type":418,"value":1004},{"type":413,"tag":623,"props":15075,"children":15076},{"style":1058},[15077],{"type":418,"value":14433},{"type":413,"tag":623,"props":15079,"children":15080},{"style":671},[15081],{"type":418,"value":1404},{"type":413,"tag":623,"props":15083,"children":15084},{"style":1058},[15085],{"type":418,"value":14773},{"type":413,"tag":623,"props":15087,"children":15088},{"class":625,"line":1113},[15089],{"type":413,"tag":623,"props":15090,"children":15091},{"style":671},[15092],{"type":418,"value":4562},{"type":413,"tag":414,"props":15094,"children":15095},{},[15096,15098,15104],{"type":418,"value":15097},"You might notice that we use the ",{"type":413,"tag":619,"props":15099,"children":15101},{"className":15100},[],[15102],{"type":418,"value":15103},"Output.CreateSecret",{"type":418,"value":15105}," 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":413,"tag":600,"props":15107,"children":15109},{"id":15108},"implementing-the-provision-infrastructure-step",[15110],{"type":418,"value":15111},"Implementing the Provision Infrastructure step",{"type":413,"tag":414,"props":15113,"children":15114},{},[15115,15117,15122],{"type":418,"value":15116},"To deploy the infrastructure, we can use the ",{"type":413,"tag":619,"props":15118,"children":15120},{"className":15119},[],[15121],{"type":418,"value":4573},{"type":418,"value":15123}," command. We will write the code in a fluent way as we did with the dotnet CLI:",{"type":413,"tag":612,"props":15125,"children":15127},{"className":981,"code":15126,"language":326,"meta":401,"style":401},"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",[15128],{"type":413,"tag":619,"props":15129,"children":15130},{"__ignoreMap":401},[15131,15170,15177,15205,15242,15265,15276,15309,15337,15373,15393],{"type":413,"tag":623,"props":15132,"children":15133},{"class":625,"line":626},[15134,15138,15142,15146,15150,15154,15158,15162,15166],{"type":413,"tag":623,"props":15135,"children":15136},{"style":1058},[15137],{"type":418,"value":12874},{"type":413,"tag":623,"props":15139,"children":15140},{"style":630},[15141],{"type":418,"value":12932},{"type":413,"tag":623,"props":15143,"children":15144},{"style":671},[15145],{"type":418,"value":2201},{"type":413,"tag":623,"props":15147,"children":15148},{"style":1058},[15149],{"type":418,"value":12888},{"type":413,"tag":623,"props":15151,"children":15152},{"style":671},[15153],{"type":418,"value":3332},{"type":413,"tag":623,"props":15155,"children":15156},{"style":671},[15157],{"type":418,"value":674},{"type":413,"tag":623,"props":15159,"children":15160},{"style":635},[15161],{"type":418,"value":12953},{"type":413,"tag":623,"props":15163,"children":15164},{"style":671},[15165],{"type":418,"value":1023},{"type":413,"tag":623,"props":15167,"children":15168},{"style":671},[15169],{"type":418,"value":2524},{"type":413,"tag":623,"props":15171,"children":15172},{"class":625,"line":1045},[15173],{"type":413,"tag":623,"props":15174,"children":15175},{"emptyLinePlaceholder":2790},[15176],{"type":418,"value":2793},{"type":413,"tag":623,"props":15178,"children":15179},{"class":625,"line":1054},[15180,15184,15189,15193,15197,15201],{"type":413,"tag":623,"props":15181,"children":15182},{"style":1058},[15183],{"type":418,"value":12653},{"type":413,"tag":623,"props":15185,"children":15186},{"style":630},[15187],{"type":418,"value":15188},"ProvisionInfra",{"type":413,"tag":623,"props":15190,"children":15191},{"style":671},[15192],{"type":418,"value":2201},{"type":413,"tag":623,"props":15194,"children":15195},{"style":630},[15196],{"type":418,"value":12666},{"type":413,"tag":623,"props":15198,"children":15199},{"style":671},[15200],{"type":418,"value":2201},{"type":413,"tag":623,"props":15202,"children":15203},{"style":1058},[15204],{"type":418,"value":12675},{"type":413,"tag":623,"props":15206,"children":15207},{"class":625,"line":1087},[15208,15212,15217,15221,15225,15230,15234,15238],{"type":413,"tag":623,"props":15209,"children":15210},{"style":671},[15211],{"type":418,"value":12683},{"type":413,"tag":623,"props":15213,"children":15214},{"style":1407},[15215],{"type":418,"value":15216},"Description",{"type":413,"tag":623,"props":15218,"children":15219},{"style":671},[15220],{"type":418,"value":1018},{"type":413,"tag":623,"props":15222,"children":15223},{"style":671},[15224],{"type":418,"value":1023},{"type":413,"tag":623,"props":15226,"children":15227},{"style":635},[15228],{"type":418,"value":15229},"Provision the infrastructure on Azure",{"type":413,"tag":623,"props":15231,"children":15232},{"style":671},[15233],{"type":418,"value":1023},{"type":413,"tag":623,"props":15235,"children":15236},{"style":671},[15237],{"type":418,"value":5595},{"type":413,"tag":623,"props":15239,"children":15240},{"style":1058},[15241],{"type":418,"value":12706},{"type":413,"tag":623,"props":15243,"children":15244},{"class":625,"line":1104},[15245,15249,15253,15257,15261],{"type":413,"tag":623,"props":15246,"children":15247},{"style":671},[15248],{"type":418,"value":12683},{"type":413,"tag":623,"props":15250,"children":15251},{"style":1407},[15252],{"type":418,"value":12718},{"type":413,"tag":623,"props":15254,"children":15255},{"style":671},[15256],{"type":418,"value":12723},{"type":413,"tag":623,"props":15258,"children":15259},{"style":671},[15260],{"type":418,"value":2201},{"type":413,"tag":623,"props":15262,"children":15263},{"style":1058},[15264],{"type":418,"value":12706},{"type":413,"tag":623,"props":15266,"children":15267},{"class":625,"line":1113},[15268,15272],{"type":413,"tag":623,"props":15269,"children":15270},{"style":671},[15271],{"type":418,"value":12739},{"type":413,"tag":623,"props":15273,"children":15274},{"style":1058},[15275],{"type":418,"value":12706},{"type":413,"tag":623,"props":15277,"children":15278},{"class":625,"line":1161},[15279,15284,15288,15293,15297,15301,15305],{"type":413,"tag":623,"props":15280,"children":15281},{"style":1058},[15282],{"type":418,"value":15283},"        PulumiTasks",{"type":413,"tag":623,"props":15285,"children":15286},{"style":671},[15287],{"type":418,"value":1404},{"type":413,"tag":623,"props":15289,"children":15290},{"style":1407},[15291],{"type":418,"value":15292},"PulumiUp",{"type":413,"tag":623,"props":15294,"children":15295},{"style":671},[15296],{"type":418,"value":1018},{"type":413,"tag":623,"props":15298,"children":15299},{"style":630},[15300],{"type":418,"value":13124},{"type":413,"tag":623,"props":15302,"children":15303},{"style":671},[15304],{"type":418,"value":2201},{"type":413,"tag":623,"props":15306,"children":15307},{"style":1058},[15308],{"type":418,"value":12675},{"type":413,"tag":623,"props":15310,"children":15311},{"class":625,"line":1207},[15312,15316,15321,15325,15329,15333],{"type":413,"tag":623,"props":15313,"children":15314},{"style":671},[15315],{"type":418,"value":13330},{"type":413,"tag":623,"props":15317,"children":15318},{"style":1407},[15319],{"type":418,"value":15320},"SetCwd",{"type":413,"tag":623,"props":15322,"children":15323},{"style":671},[15324],{"type":418,"value":1018},{"type":413,"tag":623,"props":15326,"children":15327},{"style":1058},[15328],{"type":418,"value":12932},{"type":413,"tag":623,"props":15330,"children":15331},{"style":671},[15332],{"type":418,"value":5595},{"type":413,"tag":623,"props":15334,"children":15335},{"style":1058},[15336],{"type":418,"value":12706},{"type":413,"tag":623,"props":15338,"children":15339},{"class":625,"line":1251},[15340,15344,15349,15353,15357,15361,15365,15369],{"type":413,"tag":623,"props":15341,"children":15342},{"style":671},[15343],{"type":418,"value":13330},{"type":413,"tag":623,"props":15345,"children":15346},{"style":1407},[15347],{"type":418,"value":15348},"SetStack",{"type":413,"tag":623,"props":15350,"children":15351},{"style":671},[15352],{"type":418,"value":1018},{"type":413,"tag":623,"props":15354,"children":15355},{"style":671},[15356],{"type":418,"value":1023},{"type":413,"tag":623,"props":15358,"children":15359},{"style":635},[15360],{"type":418,"value":747},{"type":413,"tag":623,"props":15362,"children":15363},{"style":671},[15364],{"type":418,"value":1023},{"type":413,"tag":623,"props":15366,"children":15367},{"style":671},[15368],{"type":418,"value":5595},{"type":413,"tag":623,"props":15370,"children":15371},{"style":1058},[15372],{"type":418,"value":12706},{"type":413,"tag":623,"props":15374,"children":15375},{"class":625,"line":1296},[15376,15380,15385,15389],{"type":413,"tag":623,"props":15377,"children":15378},{"style":671},[15379],{"type":418,"value":13330},{"type":413,"tag":623,"props":15381,"children":15382},{"style":1407},[15383],{"type":418,"value":15384},"EnableSkipPreview",{"type":413,"tag":623,"props":15386,"children":15387},{"style":671},[15388],{"type":418,"value":13394},{"type":413,"tag":623,"props":15390,"children":15391},{"style":1058},[15392],{"type":418,"value":12706},{"type":413,"tag":623,"props":15394,"children":15395},{"class":625,"line":1337},[15396],{"type":413,"tag":623,"props":15397,"children":15398},{"style":671},[15399],{"type":418,"value":12854},{"type":413,"tag":420,"props":15401,"children":15403},{"id":15402},"deploying-the-aspnet-core-api-to-azure-app-service",[15404],{"type":418,"value":15405},"Deploying the ASP.NET Core API to Azure App Service",{"type":413,"tag":414,"props":15407,"children":15408},{},[15409,15411,15417],{"type":418,"value":15410},"I previously said we were going to use the Kudu API to deploy our application. You can check the ",{"type":413,"tag":432,"props":15412,"children":15415},{"href":15413,"rel":15414},"https://github.com/projectkudu/kudu/wiki/Deploying-from-a-zip-file-or-url",[436],[15416],{"type":418,"value":4994},{"type":418,"value":15418}," about that but concretely we will do a POST request to the zipdeploy endpoint using Basic authentication.",{"type":413,"tag":414,"props":15420,"children":15421},{},[15422,15424,15430],{"type":418,"value":15423},"To retrieve a stack output, we can use the ",{"type":413,"tag":619,"props":15425,"children":15427},{"className":15426},[],[15428],{"type":418,"value":15429},"pulumi stack output",{"type":418,"value":15431}," command. To avoid duplicating the code I wrote a short method:",{"type":413,"tag":612,"props":15433,"children":15435},{"className":981,"code":15434,"language":326,"meta":401,"style":401},"string GetPulumiOutput(string outputName)  \n{  \n    return PulumiTasks.PulumiStackOutput(_ => _  \n            .SetCwd(InfrastructureDirectory)  \n            .SetPropertyName(outputName)  \n            .EnableShowSecrets()\n            .DisableProcessLogOutput())  \n        .StdToText();  \n}\n",[15436],{"type":413,"tag":619,"props":15437,"children":15438},{"__ignoreMap":401},[15439,15472,15483,15520,15547,15576,15592,15612,15634],{"type":413,"tag":623,"props":15440,"children":15441},{"class":625,"line":626},[15442,15446,15451,15455,15459,15464,15468],{"type":413,"tag":623,"props":15443,"children":15444},{"style":671},[15445],{"type":418,"value":4432},{"type":413,"tag":623,"props":15447,"children":15448},{"style":1407},[15449],{"type":418,"value":15450}," GetPulumiOutput",{"type":413,"tag":623,"props":15452,"children":15453},{"style":671},[15454],{"type":418,"value":1018},{"type":413,"tag":623,"props":15456,"children":15457},{"style":671},[15458],{"type":418,"value":4432},{"type":413,"tag":623,"props":15460,"children":15461},{"style":630},[15462],{"type":418,"value":15463}," outputName",{"type":413,"tag":623,"props":15465,"children":15466},{"style":671},[15467],{"type":418,"value":5595},{"type":413,"tag":623,"props":15469,"children":15470},{"style":1058},[15471],{"type":418,"value":12706},{"type":413,"tag":623,"props":15473,"children":15474},{"class":625,"line":1045},[15475,15479],{"type":413,"tag":623,"props":15476,"children":15477},{"style":671},[15478],{"type":418,"value":2456},{"type":413,"tag":623,"props":15480,"children":15481},{"style":1058},[15482],{"type":418,"value":12706},{"type":413,"tag":623,"props":15484,"children":15485},{"class":625,"line":1054},[15486,15490,15495,15499,15504,15508,15512,15516],{"type":413,"tag":623,"props":15487,"children":15488},{"style":2485},[15489],{"type":418,"value":2488},{"type":413,"tag":623,"props":15491,"children":15492},{"style":1058},[15493],{"type":418,"value":15494}," PulumiTasks",{"type":413,"tag":623,"props":15496,"children":15497},{"style":671},[15498],{"type":418,"value":1404},{"type":413,"tag":623,"props":15500,"children":15501},{"style":1407},[15502],{"type":418,"value":15503},"PulumiStackOutput",{"type":413,"tag":623,"props":15505,"children":15506},{"style":671},[15507],{"type":418,"value":1018},{"type":413,"tag":623,"props":15509,"children":15510},{"style":630},[15511],{"type":418,"value":13124},{"type":413,"tag":623,"props":15513,"children":15514},{"style":671},[15515],{"type":418,"value":2201},{"type":413,"tag":623,"props":15517,"children":15518},{"style":1058},[15519],{"type":418,"value":12675},{"type":413,"tag":623,"props":15521,"children":15522},{"class":625,"line":1087},[15523,15527,15531,15535,15539,15543],{"type":413,"tag":623,"props":15524,"children":15525},{"style":671},[15526],{"type":418,"value":13330},{"type":413,"tag":623,"props":15528,"children":15529},{"style":1407},[15530],{"type":418,"value":15320},{"type":413,"tag":623,"props":15532,"children":15533},{"style":671},[15534],{"type":418,"value":1018},{"type":413,"tag":623,"props":15536,"children":15537},{"style":1058},[15538],{"type":418,"value":12932},{"type":413,"tag":623,"props":15540,"children":15541},{"style":671},[15542],{"type":418,"value":5595},{"type":413,"tag":623,"props":15544,"children":15545},{"style":1058},[15546],{"type":418,"value":12706},{"type":413,"tag":623,"props":15548,"children":15549},{"class":625,"line":1104},[15550,15554,15559,15563,15568,15572],{"type":413,"tag":623,"props":15551,"children":15552},{"style":671},[15553],{"type":418,"value":13330},{"type":413,"tag":623,"props":15555,"children":15556},{"style":1407},[15557],{"type":418,"value":15558},"SetPropertyName",{"type":413,"tag":623,"props":15560,"children":15561},{"style":671},[15562],{"type":418,"value":1018},{"type":413,"tag":623,"props":15564,"children":15565},{"style":1058},[15566],{"type":418,"value":15567},"outputName",{"type":413,"tag":623,"props":15569,"children":15570},{"style":671},[15571],{"type":418,"value":5595},{"type":413,"tag":623,"props":15573,"children":15574},{"style":1058},[15575],{"type":418,"value":12706},{"type":413,"tag":623,"props":15577,"children":15578},{"class":625,"line":1113},[15579,15583,15588],{"type":413,"tag":623,"props":15580,"children":15581},{"style":671},[15582],{"type":418,"value":13330},{"type":413,"tag":623,"props":15584,"children":15585},{"style":1407},[15586],{"type":418,"value":15587},"EnableShowSecrets",{"type":413,"tag":623,"props":15589,"children":15590},{"style":671},[15591],{"type":418,"value":1604},{"type":413,"tag":623,"props":15593,"children":15594},{"class":625,"line":1161},[15595,15599,15604,15608],{"type":413,"tag":623,"props":15596,"children":15597},{"style":671},[15598],{"type":418,"value":13330},{"type":413,"tag":623,"props":15600,"children":15601},{"style":1407},[15602],{"type":418,"value":15603},"DisableProcessLogOutput",{"type":413,"tag":623,"props":15605,"children":15606},{"style":671},[15607],{"type":418,"value":6843},{"type":413,"tag":623,"props":15609,"children":15610},{"style":1058},[15611],{"type":418,"value":12706},{"type":413,"tag":623,"props":15613,"children":15614},{"class":625,"line":1207},[15615,15620,15625,15630],{"type":413,"tag":623,"props":15616,"children":15617},{"style":671},[15618],{"type":418,"value":15619},"        .",{"type":413,"tag":623,"props":15621,"children":15622},{"style":1407},[15623],{"type":418,"value":15624},"StdToText",{"type":413,"tag":623,"props":15626,"children":15627},{"style":671},[15628],{"type":418,"value":15629},"();",{"type":413,"tag":623,"props":15631,"children":15632},{"style":1058},[15633],{"type":418,"value":12706},{"type":413,"tag":623,"props":15635,"children":15636},{"class":625,"line":1251},[15637],{"type":413,"tag":623,"props":15638,"children":15639},{"style":671},[15640],{"type":418,"value":15641},"}\n",{"type":413,"tag":414,"props":15643,"children":15644},{},[15645],{"type":418,"value":15646},"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":413,"tag":612,"props":15648,"children":15650},{"className":981,"code":15649,"language":326,"meta":401,"style":401},"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",[15651],{"type":413,"tag":619,"props":15652,"children":15653},{"__ignoreMap":401},[15654,15682,15709,15737,15770,15781,15826,15870,15967,15974,16047,16085,16156,16229,16259],{"type":413,"tag":623,"props":15655,"children":15656},{"class":625,"line":626},[15657,15661,15666,15670,15674,15678],{"type":413,"tag":623,"props":15658,"children":15659},{"style":1058},[15660],{"type":418,"value":12653},{"type":413,"tag":623,"props":15662,"children":15663},{"style":630},[15664],{"type":418,"value":15665},"Deploy",{"type":413,"tag":623,"props":15667,"children":15668},{"style":671},[15669],{"type":418,"value":2201},{"type":413,"tag":623,"props":15671,"children":15672},{"style":630},[15673],{"type":418,"value":12666},{"type":413,"tag":623,"props":15675,"children":15676},{"style":671},[15677],{"type":418,"value":2201},{"type":413,"tag":623,"props":15679,"children":15680},{"style":1058},[15681],{"type":418,"value":12675},{"type":413,"tag":623,"props":15683,"children":15684},{"class":625,"line":1045},[15685,15689,15693,15697,15701,15705],{"type":413,"tag":623,"props":15686,"children":15687},{"style":671},[15688],{"type":418,"value":12683},{"type":413,"tag":623,"props":15690,"children":15691},{"style":1407},[15692],{"type":418,"value":13248},{"type":413,"tag":623,"props":15694,"children":15695},{"style":671},[15696],{"type":418,"value":1018},{"type":413,"tag":623,"props":15698,"children":15699},{"style":1058},[15700],{"type":418,"value":13450},{"type":413,"tag":623,"props":15702,"children":15703},{"style":671},[15704],{"type":418,"value":5595},{"type":413,"tag":623,"props":15706,"children":15707},{"style":1058},[15708],{"type":418,"value":12706},{"type":413,"tag":623,"props":15710,"children":15711},{"class":625,"line":1054},[15712,15716,15721,15725,15729,15733],{"type":413,"tag":623,"props":15713,"children":15714},{"style":671},[15715],{"type":418,"value":12683},{"type":413,"tag":623,"props":15717,"children":15718},{"style":1407},[15719],{"type":418,"value":15720},"After",{"type":413,"tag":623,"props":15722,"children":15723},{"style":671},[15724],{"type":418,"value":1018},{"type":413,"tag":623,"props":15726,"children":15727},{"style":1058},[15728],{"type":418,"value":15188},{"type":413,"tag":623,"props":15730,"children":15731},{"style":671},[15732],{"type":418,"value":5595},{"type":413,"tag":623,"props":15734,"children":15735},{"style":1058},[15736],{"type":418,"value":12706},{"type":413,"tag":623,"props":15738,"children":15739},{"class":625,"line":1087},[15740,15744,15748,15752,15757,15762,15766],{"type":413,"tag":623,"props":15741,"children":15742},{"style":671},[15743],{"type":418,"value":12683},{"type":413,"tag":623,"props":15745,"children":15746},{"style":1407},[15747],{"type":418,"value":12718},{"type":413,"tag":623,"props":15749,"children":15750},{"style":671},[15751],{"type":418,"value":1018},{"type":413,"tag":623,"props":15753,"children":15754},{"style":2854},[15755],{"type":418,"value":15756},"async",{"type":413,"tag":623,"props":15758,"children":15759},{"style":671},[15760],{"type":418,"value":15761}," ()",{"type":413,"tag":623,"props":15763,"children":15764},{"style":671},[15765],{"type":418,"value":2201},{"type":413,"tag":623,"props":15767,"children":15768},{"style":1058},[15769],{"type":418,"value":12706},{"type":413,"tag":623,"props":15771,"children":15772},{"class":625,"line":1104},[15773,15777],{"type":413,"tag":623,"props":15774,"children":15775},{"style":671},[15776],{"type":418,"value":12739},{"type":413,"tag":623,"props":15778,"children":15779},{"style":1058},[15780],{"type":418,"value":12706},{"type":413,"tag":623,"props":15782,"children":15783},{"class":625,"line":1113},[15784,15789,15794,15798,15802,15806,15810,15814,15818,15822],{"type":413,"tag":623,"props":15785,"children":15786},{"style":630},[15787],{"type":418,"value":15788},"        var",{"type":413,"tag":623,"props":15790,"children":15791},{"style":630},[15792],{"type":418,"value":15793}," publishingUsername",{"type":413,"tag":623,"props":15795,"children":15796},{"style":671},[15797],{"type":418,"value":1004},{"type":413,"tag":623,"props":15799,"children":15800},{"style":1407},[15801],{"type":418,"value":15450},{"type":413,"tag":623,"props":15803,"children":15804},{"style":671},[15805],{"type":418,"value":1018},{"type":413,"tag":623,"props":15807,"children":15808},{"style":671},[15809],{"type":418,"value":1023},{"type":413,"tag":623,"props":15811,"children":15812},{"style":635},[15813],{"type":418,"value":14880},{"type":413,"tag":623,"props":15815,"children":15816},{"style":671},[15817],{"type":418,"value":1023},{"type":413,"tag":623,"props":15819,"children":15820},{"style":671},[15821],{"type":418,"value":12817},{"type":413,"tag":623,"props":15823,"children":15824},{"style":1058},[15825],{"type":418,"value":12706},{"type":413,"tag":623,"props":15827,"children":15828},{"class":625,"line":1161},[15829,15833,15838,15842,15846,15850,15854,15858,15862,15866],{"type":413,"tag":623,"props":15830,"children":15831},{"style":630},[15832],{"type":418,"value":15788},{"type":413,"tag":623,"props":15834,"children":15835},{"style":630},[15836],{"type":418,"value":15837}," publishingUserPassword",{"type":413,"tag":623,"props":15839,"children":15840},{"style":671},[15841],{"type":418,"value":1004},{"type":413,"tag":623,"props":15843,"children":15844},{"style":1407},[15845],{"type":418,"value":15450},{"type":413,"tag":623,"props":15847,"children":15848},{"style":671},[15849],{"type":418,"value":1018},{"type":413,"tag":623,"props":15851,"children":15852},{"style":671},[15853],{"type":418,"value":1023},{"type":413,"tag":623,"props":15855,"children":15856},{"style":635},[15857],{"type":418,"value":14972},{"type":413,"tag":623,"props":15859,"children":15860},{"style":671},[15861],{"type":418,"value":1023},{"type":413,"tag":623,"props":15863,"children":15864},{"style":671},[15865],{"type":418,"value":12817},{"type":413,"tag":623,"props":15867,"children":15868},{"style":1058},[15869],{"type":418,"value":12706},{"type":413,"tag":623,"props":15871,"children":15872},{"class":625,"line":1207},[15873,15877,15882,15886,15891,15895,15900,15904,15909,15913,15918,15922,15927,15931,15935,15939,15943,15947,15951,15955,15959,15963],{"type":413,"tag":623,"props":15874,"children":15875},{"style":630},[15876],{"type":418,"value":15788},{"type":413,"tag":623,"props":15878,"children":15879},{"style":630},[15880],{"type":418,"value":15881}," base64Auth",{"type":413,"tag":623,"props":15883,"children":15884},{"style":671},[15885],{"type":418,"value":1004},{"type":413,"tag":623,"props":15887,"children":15888},{"style":1058},[15889],{"type":418,"value":15890}," Convert",{"type":413,"tag":623,"props":15892,"children":15893},{"style":671},[15894],{"type":418,"value":1404},{"type":413,"tag":623,"props":15896,"children":15897},{"style":1407},[15898],{"type":418,"value":15899},"ToBase64String",{"type":413,"tag":623,"props":15901,"children":15902},{"style":671},[15903],{"type":418,"value":1018},{"type":413,"tag":623,"props":15905,"children":15906},{"style":1058},[15907],{"type":418,"value":15908},"Encoding",{"type":413,"tag":623,"props":15910,"children":15911},{"style":671},[15912],{"type":418,"value":1404},{"type":413,"tag":623,"props":15914,"children":15915},{"style":1058},[15916],{"type":418,"value":15917},"Default",{"type":413,"tag":623,"props":15919,"children":15920},{"style":671},[15921],{"type":418,"value":1404},{"type":413,"tag":623,"props":15923,"children":15924},{"style":1407},[15925],{"type":418,"value":15926},"GetBytes",{"type":413,"tag":623,"props":15928,"children":15929},{"style":671},[15930],{"type":418,"value":1018},{"type":413,"tag":623,"props":15932,"children":15933},{"style":671},[15934],{"type":418,"value":4498},{"type":413,"tag":623,"props":15936,"children":15937},{"style":1058},[15938],{"type":418,"value":14880},{"type":413,"tag":623,"props":15940,"children":15941},{"style":671},[15942],{"type":418,"value":3327},{"type":413,"tag":623,"props":15944,"children":15945},{"style":635},[15946],{"type":418,"value":3493},{"type":413,"tag":623,"props":15948,"children":15949},{"style":671},[15950],{"type":418,"value":2456},{"type":413,"tag":623,"props":15952,"children":15953},{"style":1058},[15954],{"type":418,"value":14972},{"type":413,"tag":623,"props":15956,"children":15957},{"style":671},[15958],{"type":418,"value":2465},{"type":413,"tag":623,"props":15960,"children":15961},{"style":671},[15962],{"type":418,"value":13155},{"type":413,"tag":623,"props":15964,"children":15965},{"style":1058},[15966],{"type":418,"value":12706},{"type":413,"tag":623,"props":15968,"children":15969},{"class":625,"line":1251},[15970],{"type":413,"tag":623,"props":15971,"children":15972},{"style":1058},[15973],{"type":418,"value":12706},{"type":413,"tag":623,"props":15975,"children":15976},{"class":625,"line":1296},[15977,15982,15987,15992,15997,16001,16005,16009,16014,16018,16023,16027,16031,16035,16039,16043],{"type":413,"tag":623,"props":15978,"children":15979},{"style":671},[15980],{"type":418,"value":15981},"        await",{"type":413,"tag":623,"props":15983,"children":15984},{"style":2485},[15985],{"type":418,"value":15986}," using",{"type":413,"tag":623,"props":15988,"children":15989},{"style":630},[15990],{"type":418,"value":15991}," var",{"type":413,"tag":623,"props":15993,"children":15994},{"style":630},[15995],{"type":418,"value":15996}," package",{"type":413,"tag":623,"props":15998,"children":15999},{"style":671},[16000],{"type":418,"value":1004},{"type":413,"tag":623,"props":16002,"children":16003},{"style":1058},[16004],{"type":418,"value":3866},{"type":413,"tag":623,"props":16006,"children":16007},{"style":671},[16008],{"type":418,"value":1404},{"type":413,"tag":623,"props":16010,"children":16011},{"style":1407},[16012],{"type":418,"value":16013},"OpenRead",{"type":413,"tag":623,"props":16015,"children":16016},{"style":671},[16017],{"type":418,"value":1018},{"type":413,"tag":623,"props":16019,"children":16020},{"style":1058},[16021],{"type":418,"value":16022},"ArtifactsDirectory ",{"type":413,"tag":623,"props":16024,"children":16025},{"style":671},[16026],{"type":418,"value":3332},{"type":413,"tag":623,"props":16028,"children":16029},{"style":671},[16030],{"type":418,"value":674},{"type":413,"tag":623,"props":16032,"children":16033},{"style":635},[16034],{"type":418,"value":13429},{"type":413,"tag":623,"props":16036,"children":16037},{"style":671},[16038],{"type":418,"value":1023},{"type":413,"tag":623,"props":16040,"children":16041},{"style":671},[16042],{"type":418,"value":12817},{"type":413,"tag":623,"props":16044,"children":16045},{"style":1058},[16046],{"type":418,"value":12706},{"type":413,"tag":623,"props":16048,"children":16049},{"class":625,"line":1337},[16050,16055,16059,16064,16068,16072,16077,16081],{"type":413,"tag":623,"props":16051,"children":16052},{"style":2485},[16053],{"type":418,"value":16054},"        using",{"type":413,"tag":623,"props":16056,"children":16057},{"style":630},[16058],{"type":418,"value":15991},{"type":413,"tag":623,"props":16060,"children":16061},{"style":630},[16062],{"type":418,"value":16063}," httpClient",{"type":413,"tag":623,"props":16065,"children":16066},{"style":671},[16067],{"type":418,"value":1004},{"type":413,"tag":623,"props":16069,"children":16070},{"style":671},[16071],{"type":418,"value":638},{"type":413,"tag":623,"props":16073,"children":16074},{"style":630},[16075],{"type":418,"value":16076}," HttpClient",{"type":413,"tag":623,"props":16078,"children":16079},{"style":671},[16080],{"type":418,"value":15629},{"type":413,"tag":623,"props":16082,"children":16083},{"style":1058},[16084],{"type":418,"value":12706},{"type":413,"tag":623,"props":16086,"children":16087},{"class":625,"line":1346},[16088,16093,16097,16102,16106,16111,16115,16119,16124,16128,16132,16136,16140,16144,16148,16152],{"type":413,"tag":623,"props":16089,"children":16090},{"style":1058},[16091],{"type":418,"value":16092},"        httpClient",{"type":413,"tag":623,"props":16094,"children":16095},{"style":671},[16096],{"type":418,"value":1404},{"type":413,"tag":623,"props":16098,"children":16099},{"style":1058},[16100],{"type":418,"value":16101},"DefaultRequestHeaders",{"type":413,"tag":623,"props":16103,"children":16104},{"style":671},[16105],{"type":418,"value":1404},{"type":413,"tag":623,"props":16107,"children":16108},{"style":1058},[16109],{"type":418,"value":16110},"Authorization ",{"type":413,"tag":623,"props":16112,"children":16113},{"style":671},[16114],{"type":418,"value":1066},{"type":413,"tag":623,"props":16116,"children":16117},{"style":671},[16118],{"type":418,"value":638},{"type":413,"tag":623,"props":16120,"children":16121},{"style":630},[16122],{"type":418,"value":16123}," AuthenticationHeaderValue",{"type":413,"tag":623,"props":16125,"children":16126},{"style":671},[16127],{"type":418,"value":1018},{"type":413,"tag":623,"props":16129,"children":16130},{"style":671},[16131],{"type":418,"value":1023},{"type":413,"tag":623,"props":16133,"children":16134},{"style":635},[16135],{"type":418,"value":14345},{"type":413,"tag":623,"props":16137,"children":16138},{"style":671},[16139],{"type":418,"value":1023},{"type":413,"tag":623,"props":16141,"children":16142},{"style":671},[16143],{"type":418,"value":1037},{"type":413,"tag":623,"props":16145,"children":16146},{"style":1058},[16147],{"type":418,"value":15881},{"type":413,"tag":623,"props":16149,"children":16150},{"style":671},[16151],{"type":418,"value":12817},{"type":413,"tag":623,"props":16153,"children":16154},{"style":1058},[16155],{"type":418,"value":12706},{"type":413,"tag":623,"props":16157,"children":16158},{"class":625,"line":2100},[16159,16163,16167,16171,16176,16180,16184,16189,16193,16198,16203,16207,16212,16217,16221,16225],{"type":413,"tag":623,"props":16160,"children":16161},{"style":671},[16162],{"type":418,"value":15981},{"type":413,"tag":623,"props":16164,"children":16165},{"style":1058},[16166],{"type":418,"value":16063},{"type":413,"tag":623,"props":16168,"children":16169},{"style":671},[16170],{"type":418,"value":1404},{"type":413,"tag":623,"props":16172,"children":16173},{"style":1407},[16174],{"type":418,"value":16175},"PostAsync",{"type":413,"tag":623,"props":16177,"children":16178},{"style":671},[16179],{"type":418,"value":1018},{"type":413,"tag":623,"props":16181,"children":16182},{"style":671},[16183],{"type":418,"value":2446},{"type":413,"tag":623,"props":16185,"children":16186},{"style":635},[16187],{"type":418,"value":16188},"https://",{"type":413,"tag":623,"props":16190,"children":16191},{"style":671},[16192],{"type":418,"value":2456},{"type":413,"tag":623,"props":16194,"children":16195},{"style":1407},[16196],{"type":418,"value":16197},"GetPulumiOutput",{"type":413,"tag":623,"props":16199,"children":16200},{"style":671},[16201],{"type":418,"value":16202},"(\"",{"type":413,"tag":623,"props":16204,"children":16205},{"style":635},[16206],{"type":418,"value":15061},{"type":413,"tag":623,"props":16208,"children":16209},{"style":671},[16210],{"type":418,"value":16211},"\")}",{"type":413,"tag":623,"props":16213,"children":16214},{"style":635},[16215],{"type":418,"value":16216},".scm.azurewebsites.net/api/zipdeploy",{"type":413,"tag":623,"props":16218,"children":16219},{"style":671},[16220],{"type":418,"value":1023},{"type":413,"tag":623,"props":16222,"children":16223},{"style":671},[16224],{"type":418,"value":1037},{"type":413,"tag":623,"props":16226,"children":16227},{"style":1058},[16228],{"type":418,"value":12706},{"type":413,"tag":623,"props":16230,"children":16231},{"class":625,"line":2897},[16232,16237,16242,16246,16251,16255],{"type":413,"tag":623,"props":16233,"children":16234},{"style":671},[16235],{"type":418,"value":16236},"            new",{"type":413,"tag":623,"props":16238,"children":16239},{"style":630},[16240],{"type":418,"value":16241}," StreamContent",{"type":413,"tag":623,"props":16243,"children":16244},{"style":671},[16245],{"type":418,"value":1018},{"type":413,"tag":623,"props":16247,"children":16248},{"style":1058},[16249],{"type":418,"value":16250},"package",{"type":413,"tag":623,"props":16252,"children":16253},{"style":671},[16254],{"type":418,"value":13155},{"type":413,"tag":623,"props":16256,"children":16257},{"style":1058},[16258],{"type":418,"value":12706},{"type":413,"tag":623,"props":16260,"children":16261},{"class":625,"line":2926},[16262],{"type":413,"tag":623,"props":16263,"children":16264},{"style":671},[16265],{"type":418,"value":12854},{"type":413,"tag":496,"props":16267,"children":16268},{"icon":4578},[16269],{"type":413,"tag":414,"props":16270,"children":16271},{},[16272,16274,16281],{"type":418,"value":16273},"George Dangl already wrote a nice ",{"type":413,"tag":432,"props":16275,"children":16278},{"href":16276,"rel":16277},"https://blog.dangl.me/archive/lets-use-nuke-to-quickly-deploy-an-app-to-azure-via-zip-deployment/",[436],[16279],{"type":418,"value":16280},"article",{"type":418,"value":16282}," 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":413,"tag":414,"props":16284,"children":16285},{},[16286],{"type":418,"value":16287},"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":413,"tag":414,"props":16289,"children":16290},{},[16291],{"type":418,"value":16292},"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":413,"tag":420,"props":16294,"children":16296},{"id":16295},"final-pipeline",[16297],{"type":418,"value":16298},"Final pipeline",{"type":413,"tag":414,"props":16300,"children":16301},{},[16302],{"type":418,"value":16303},"If I fold everything, the pipeline code we created looks like that:",{"type":413,"tag":414,"props":16305,"children":16306},{},[16307],{"type":413,"tag":487,"props":16308,"children":16312},{"alt":16309,"className":16310,"src":16311},"Complete Nuke pipeline in the Build.cs file.",[491,492],"/posts/images/pulumi_met_nuke_11.png",[],{"type":413,"tag":414,"props":16314,"children":16315},{},[16316,16318,16324],{"type":418,"value":16317},"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":413,"tag":619,"props":16319,"children":16321},{"className":16320},[],[16322],{"type":418,"value":16323},"nuke --plan",{"type":418,"value":16325}," command to display a visual representation of the pipeline (how cool is that !?)",{"type":413,"tag":414,"props":16327,"children":16328},{},[16329],{"type":413,"tag":487,"props":16330,"children":16334},{"alt":16331,"className":16332,"src":16333},"Nuke execution plan displayed as a graph.",[491,492],"/posts/images/pulumi_met_nuke_12.png",[],{"type":413,"tag":496,"props":16336,"children":16337},{"icon":557},[16338],{"type":413,"tag":414,"props":16339,"children":16340},{},[16341],{"type":418,"value":16342},"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":413,"tag":414,"props":16344,"children":16345},{},[16346],{"type":418,"value":16347},"Let's execute the complete pipeline:",{"type":413,"tag":414,"props":16349,"children":16350},{},[16351],{"type":413,"tag":487,"props":16352,"children":16356},{"alt":16353,"className":16354,"src":16355,"width":12260},"Pipeline output in terminal showing the different steps and their status.",[491,492],"/posts/images/pulumi_met_nuke_13.png",[],{"type":413,"tag":414,"props":16358,"children":16359},{},[16360],{"type":418,"value":16361},"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":413,"tag":414,"props":16363,"children":16364},{},[16365],{"type":413,"tag":487,"props":16366,"children":16370},{"alt":16367,"className":16368,"src":16369,"width":591},"Weather API weather forecast endpoint response opened in a browser.",[491,492],"/posts/images/pulumi_met_nuke_14.png",[],{"type":413,"tag":420,"props":16372,"children":16374},{"id":16373},"conclusion",[16375],{"type":418,"value":16376},"Conclusion",{"type":413,"tag":600,"props":16378,"children":16380},{"id":16379},"improvements-to-the-example-pipeline",[16381],{"type":418,"value":16382},"Improvements to the example pipeline",{"type":413,"tag":414,"props":16384,"children":16385},{},[16386],{"type":418,"value":16387},"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":413,"tag":414,"props":16389,"children":16390},{},[16391,16393,16400,16402,16409,16411,16418],{"type":418,"value":16392},"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":413,"tag":432,"props":16394,"children":16397},{"href":16395,"rel":16396},"https://learn.microsoft.com/en-us/cli/azure/webapp/deployment/source?view=azure-cli-latest#az-webapp-deployment-source-config-zip",[436],[16398],{"type":418,"value":16399},"a command",{"type":418,"value":16401}," for that. But my preferred option would be to use the ",{"type":413,"tag":432,"props":16403,"children":16406},{"href":16404,"rel":16405},"https://learn.microsoft.com/en-us/dotnet/api/overview/azure/resource-manager?view=azure-dotnet",[436],[16407],{"type":418,"value":16408},"Azure Resource Manager libraries for .NET",{"type":418,"value":16410},". Unfortunately this SDK is quite new and miss ",{"type":413,"tag":432,"props":16412,"children":16415},{"href":16413,"rel":16414},"https://github.com/Azure/azure-sdk-for-net/issues/30577",[436],[16416],{"type":418,"value":16417},"samples",{"type":418,"value":16419}," on how to do that.",{"type":413,"tag":414,"props":16421,"children":16422},{},[16423,16425,16432],{"type":418,"value":16424},"Speaking of SDK, Pulumi has an API called the ",{"type":413,"tag":432,"props":16426,"children":16429},{"href":16427,"rel":16428},"https://www.pulumi.com/automation/",[436],[16430],{"type":418,"value":16431},"Automation API",{"type":418,"value":16433}," 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":413,"tag":600,"props":16435,"children":16437},{"id":16436},"about-nuke-and-cicd",[16438],{"type":418,"value":16439},"About Nuke and CI/CD",{"type":413,"tag":414,"props":16441,"children":16442},{},[16443],{"type":418,"value":16444},"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":413,"tag":414,"props":16446,"children":16447},{},[16448,16450,16456,16457,16464],{"type":418,"value":16449},"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":413,"tag":432,"props":16451,"children":16454},{"href":16452,"rel":16453},"https://nuke.build/docs/introduction/",[436],[16455],{"type":418,"value":4994},{"type":418,"value":1770},{"type":413,"tag":432,"props":16458,"children":16461},{"href":16459,"rel":16460},"https://nuke.build/resources/",[436],[16462],{"type":418,"value":16463},"at some resources",{"type":418,"value":16465}," and start playing with it by yourself.",{"type":413,"tag":414,"props":16467,"children":16468},{},[16469],{"type":418,"value":16470},"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":413,"tag":414,"props":16472,"children":16473},{},[16474],{"type":418,"value":16475},"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":413,"tag":600,"props":16477,"children":16479},{"id":16478},"pulumi-nuke-the-perfect-combo",[16480],{"type":418,"value":16481},"Pulumi & Nuke, the perfect combo?",{"type":413,"tag":414,"props":16483,"children":16484},{},[16485],{"type":418,"value":16486},"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":413,"tag":4673,"props":16488,"children":16489},{},[16490],{"type":418,"value":4677},{"title":401,"searchDepth":1045,"depth":1045,"links":16492},[16493,16494,16495,16500,16507,16513,16514,16515],{"id":12222,"depth":1045,"text":12225},{"id":12243,"depth":1045,"text":12246},{"id":12340,"depth":1045,"text":12343,"children":16496},[16497,16498,16499],{"id":12346,"depth":1054,"text":12349},{"id":12403,"depth":1054,"text":12406},{"id":12523,"depth":1054,"text":12526},{"id":12569,"depth":1045,"text":12572,"children":16501},[16502,16503,16504,16505,16506],{"id":12575,"depth":1054,"text":12578},{"id":12630,"depth":1054,"text":12633},{"id":13014,"depth":1054,"text":13017},{"id":13169,"depth":1054,"text":13172},{"id":13408,"depth":1054,"text":13411},{"id":13878,"depth":1045,"text":13881,"children":16508},[16509,16510,16511,16512],{"id":13884,"depth":1054,"text":13887},{"id":13940,"depth":1054,"text":13943},{"id":14627,"depth":1054,"text":14630},{"id":15108,"depth":1054,"text":15111},{"id":15402,"depth":1045,"text":15405},{"id":16295,"depth":1045,"text":16298},{"id":16373,"depth":1045,"text":16376,"children":16516},[16517,16518,16519],{"id":16379,"depth":1054,"text":16382},{"id":16436,"depth":1054,"text":16439},{"id":16478,"depth":1054,"text":16481},"content:1.posts:45.when-pulumi-met-nuke.md","1.posts/45.when-pulumi-met-nuke.md",{"_path":115,"_dir":399,"_draft":400,"_partial":400,"_locale":401,"title":114,"description":16523,"lead":16524,"date":16525,"image":16526,"badge":16528,"tags":16529,"body":16530,"_type":4691,"_id":17797,"_source":4693,"_file":17798,"_extension":4695},"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":16527},"/images/clouds_1.jpg",{"label":266},[315,312,355,266],{"type":410,"children":16531,"toc":17766},[16532,16536,16541,16547,16552,16557,16629,16638,16643,16649,16654,16660,16682,16700,16709,16730,16746,16752,16757,16766,16778,16795,16808,16831,16840,16845,16863,16869,16898,16907,16930,16946,16960,16983,16992,16998,17003,17017,17022,17054,17074,17083,17089,17103,17109,17115,17120,17125,17134,17139,17145,17150,17159,17164,17180,17193,17206,17215,17221,17226,17231,17249,17260,17269,17274,17280,17294,17312,17320,17337,17342,17351,17356,17362,17376,17385,17390,17411,17417,17422,17427,17433,17438,17443,17452,17457,17463,17468,17473,17482,17487,17492,17500,17506,17511,17519,17524,17538,17550,17556,17561,17566,17572,17594,17600,17606,17611,17639,17648,17654,17659,17664,17669,17675,17680,17685,17690,17695,17701,17706,17711,17723,17728,17734,17739,17744,17749,17761],{"type":413,"tag":414,"props":16533,"children":16534},{},[16535],{"type":418,"value":16523},{"type":413,"tag":414,"props":16537,"children":16538},{},[16539],{"type":418,"value":16540},"But first, let's talk about what makes a good Infrastructure as Code solution.",{"type":413,"tag":420,"props":16542,"children":16544},{"id":16543},"what-makes-a-good-infrastructure-as-code-solution",[16545],{"type":418,"value":16546},"What makes a good Infrastructure as Code solution?",{"type":413,"tag":414,"props":16548,"children":16549},{},[16550],{"type":418,"value":16551},"There is no universal answer to this question, but I can give you the characteristics I am looking for in an IaC solution.",{"type":413,"tag":414,"props":16553,"children":16554},{},[16555],{"type":418,"value":16556},"In my opinion, an IaC solution should be:",{"type":413,"tag":443,"props":16558,"children":16559},{},[16560,16570,16579,16589,16599,16609,16619],{"type":413,"tag":447,"props":16561,"children":16562},{},[16563,16568],{"type":413,"tag":521,"props":16564,"children":16565},{},[16566],{"type":418,"value":16567},"declarative",{"type":418,"value":16569},". I think it is important to focus on what infrastructure we want to provision rather than how to provision it",{"type":413,"tag":447,"props":16571,"children":16572},{},[16573,16577],{"type":413,"tag":521,"props":16574,"children":16575},{},[16576],{"type":418,"value":334},{"type":418,"value":16578},". Beyond being a good thing, open source favors the adoption of technology and makes an ecosystem healthy with contributions from the community",{"type":413,"tag":447,"props":16580,"children":16581},{},[16582,16587],{"type":413,"tag":521,"props":16583,"children":16584},{},[16585],{"type":418,"value":16586},"multi providers",{"type":418,"value":16588},". 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":413,"tag":447,"props":16590,"children":16591},{},[16592,16597],{"type":413,"tag":521,"props":16593,"children":16594},{},[16595],{"type":418,"value":16596},"easy to learn, easy to use, and easy to be productive with",{"type":418,"value":16598},". I love learning new things but even more when it's easy and I can be quickly productive with new technology.",{"type":413,"tag":447,"props":16600,"children":16601},{},[16602,16607],{"type":413,"tag":521,"props":16603,"children":16604},{},[16605],{"type":418,"value":16606},"up to date with main cloud providers' resources and features",{"type":418,"value":16608},". 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":413,"tag":447,"props":16610,"children":16611},{},[16612,16617],{"type":413,"tag":521,"props":16613,"children":16614},{},[16615],{"type":418,"value":16616},"flexible and customizable",{"type":418,"value":16618},". 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":413,"tag":447,"props":16620,"children":16621},{},[16622,16627],{"type":413,"tag":521,"props":16623,"children":16624},{},[16625],{"type":418,"value":16626},"secure",{"type":418,"value":16628},". Security in IT is of paramount importance (especially when dealing with infrastructure) and therefore should be built-in",{"type":413,"tag":414,"props":16630,"children":16631},{},[16632],{"type":413,"tag":487,"props":16633,"children":16637},{"alt":16634,"className":16635,"src":16636},"Sun shining through the clouds in the sky.",[491,492],"/posts/images/pulumivstf_cloud_1.jpg",[],{"type":413,"tag":414,"props":16639,"children":16640},{},[16641],{"type":418,"value":16642},"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":413,"tag":420,"props":16644,"children":16646},{"id":16645},"pulumi-a-modern-iac-tool-with-many-built-in-features",[16647],{"type":418,"value":16648},"Pulumi, a modern IaC tool with many built-in features",{"type":413,"tag":414,"props":16650,"children":16651},{},[16652],{"type":418,"value":16653},"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":413,"tag":600,"props":16655,"children":16657},{"id":16656},"state-backends-and-security",[16658],{"type":418,"value":16659},"State, backends, and security",{"type":413,"tag":414,"props":16661,"children":16662},{},[16663,16665,16672,16673,16680],{"type":418,"value":16664},"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":413,"tag":432,"props":16666,"children":16669},{"href":16667,"rel":16668},"https://cloud.hashicorp.com/products/terraform",[436],[16670],{"type":418,"value":16671},"Terraform Cloud",{"type":418,"value":1778},{"type":413,"tag":432,"props":16674,"children":16677},{"href":16675,"rel":16676},"https://www.pulumi.com/docs/intro/pulumi-service/",[436],[16678],{"type":418,"value":16679},"Pulumi Service",{"type":418,"value":16681}," are self-managed backends that offer similar functionalities (deployment history, collaboration functionalities, RBAC for an organization...).",{"type":413,"tag":414,"props":16683,"children":16684},{},[16685,16687,16692,16694,16699],{"type":418,"value":16686},"The state stored in a backend contains sensitive data (secrets like connection strings 🔑) that you need to secure. Whatever the backend you choose, ",{"type":413,"tag":521,"props":16688,"children":16689},{},[16690],{"type":418,"value":16691},"when using Pulumi the secrets in your state are always encrypted",{"type":418,"value":16693}," 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":413,"tag":521,"props":16695,"children":16696},{},[16697],{"type":418,"value":16698},"security should be built-in and not something you have to pay for",{"type":418,"value":1404},{"type":413,"tag":414,"props":16701,"children":16702},{},[16703],{"type":413,"tag":487,"props":16704,"children":16708},{"alt":16705,"className":16706,"src":16707},"A padlock sitting on top of a computer keyboard.",[491,492],"/posts/images/pulumivstf_security_1.jpg",[],{"type":413,"tag":414,"props":16710,"children":16711},{},[16712,16714,16721,16723,16728],{"type":418,"value":16713},"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":413,"tag":432,"props":16715,"children":16718},{"href":16716,"rel":16717},"https://docs.microsoft.com/en-us/azure/storage/common/shared-key-authorization-prevent?tabs=portal",[436],[16719],{"type":418,"value":16720},"shared key authorization",{"type":418,"value":16722}," and use only Azure AD authorization to reduce the security risk.) I am just saying that ",{"type":413,"tag":521,"props":16724,"children":16725},{},[16726],{"type":418,"value":16727},"Pulumi is secure by default, Terraform is not",{"type":418,"value":16729}," and extra work is required.",{"type":413,"tag":496,"props":16731,"children":16732},{"icon":4578},[16733],{"type":413,"tag":414,"props":16734,"children":16735},{},[16736,16738,16744],{"type":418,"value":16737},"If you want to learn more about state, backend, security, and how Terraform handles state compared to Pulumi you can check this ",{"type":413,"tag":432,"props":16739,"children":16742},{"href":16740,"rel":16741},"https://www.techwatching.dev/posts/pulumi-azure-backend",[436],[16743],{"type":418,"value":16280},{"type":418,"value":16745}," 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":413,"tag":600,"props":16747,"children":16749},{"id":16748},"iac-brownfield-development",[16750],{"type":418,"value":16751},"IaC brownfield development",{"type":413,"tag":414,"props":16753,"children":16754},{},[16755],{"type":418,"value":16756},"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":413,"tag":414,"props":16758,"children":16759},{},[16760],{"type":413,"tag":487,"props":16761,"children":16765},{"alt":16762,"className":16763,"src":16764},"A view of the ocean from the top of a hill with different types of soils.",[491,492],"/posts/images/pulumivstf_brownfield.jpg",[],{"type":413,"tag":414,"props":16767,"children":16768},{},[16769,16771,16776],{"type":418,"value":16770},"Both Terraform and Pulumi have an ",{"type":413,"tag":619,"props":16772,"children":16774},{"className":16773},[],[16775],{"type":418,"value":5403},{"type":418,"value":16777},"  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":413,"tag":496,"props":16779,"children":16780},{"icon":557},[16781],{"type":413,"tag":414,"props":16782,"children":16783},{},[16784,16786,16793],{"type":418,"value":16785},"Besides, Microsoft has recently announced a new tool ",{"type":413,"tag":432,"props":16787,"children":16790},{"href":16788,"rel":16789},"https://github.com/Azure/aztfy",[436],[16791],{"type":418,"value":16792},"Azure Terrafy",{"type":418,"value":16794}," 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":413,"tag":414,"props":16796,"children":16797},{},[16798,16800,16806],{"type":418,"value":16799},"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":413,"tag":619,"props":16801,"children":16803},{"className":16802},[],[16804],{"type":418,"value":16805},"tfstate",{"type":418,"value":16807}," 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":413,"tag":414,"props":16809,"children":16810},{},[16811,16813,16820,16822,16829],{"type":418,"value":16812},"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":413,"tag":432,"props":16814,"children":16817},{"href":16815,"rel":16816},"https://www.pulumi.com/docs/converters/",[436],[16818],{"type":418,"value":16819},"conversion tools",{"type":418,"value":16821}," to help you with that. For instance, you can use the ",{"type":413,"tag":432,"props":16823,"children":16826},{"href":16824,"rel":16825},"https://www.pulumi.com/arm2pulumi/",[436],[16827],{"type":418,"value":16828},"arm2pulumi",{"type":418,"value":16830}," 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":413,"tag":414,"props":16832,"children":16833},{},[16834],{"type":413,"tag":487,"props":16835,"children":16839},{"alt":16836,"className":16837,"src":16838},"Demo of using ARM to Pulumi converter on Pulumi website.",[491,492],"/posts/images/pulumivstf_converter_1.png",[],{"type":413,"tag":414,"props":16841,"children":16842},{},[16843],{"type":418,"value":16844},"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":413,"tag":496,"props":16846,"children":16848},{"icon":16847},"i-heroicons-document-text",[16849],{"type":413,"tag":414,"props":16850,"children":16851},{},[16852,16854,16861],{"type":418,"value":16853},"Pulumi has a very ",{"type":413,"tag":432,"props":16855,"children":16858},{"href":16856,"rel":16857},"https://www.pulumi.com/docs/guides/adopting/",[436],[16859],{"type":418,"value":16860},"well-written documentation",{"type":418,"value":16862}," about \"adopting Pulumi\" if you want to deep dive into working with existing infrastructure.",{"type":413,"tag":600,"props":16864,"children":16866},{"id":16865},"environments-and-configuration",[16867],{"type":418,"value":16868},"Environments and configuration",{"type":413,"tag":414,"props":16870,"children":16871},{},[16872,16874,16881,16883,16889,16890,16896],{"type":418,"value":16873},"Thanks to its concept of ",{"type":413,"tag":432,"props":16875,"children":16878},{"href":16876,"rel":16877},"https://www.pulumi.com/docs/intro/concepts/stack/",[436],[16879],{"type":418,"value":16880},"stack",{"type":418,"value":16882},", 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":413,"tag":619,"props":16884,"children":16886},{"className":16885},[],[16887],{"type":418,"value":16888},"Pulumi.development.yaml",{"type":418,"value":8741},{"type":413,"tag":619,"props":16891,"children":16893},{"className":16892},[],[16894],{"type":418,"value":16895},"Pulumi.production.yaml",{"type":418,"value":16897},". 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":413,"tag":414,"props":16899,"children":16900},{},[16901],{"type":413,"tag":487,"props":16902,"children":16906},{"alt":16903,"className":16904,"src":16905},"Pulumi.dev.yaml file with encrypted settings.",[491,492],"/posts/images/pulumivstf_configuration_1.png",[],{"type":413,"tag":414,"props":16908,"children":16909},{},[16910,16912,16919,16921,16928],{"type":418,"value":16911},"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":413,"tag":432,"props":16913,"children":16916},{"href":16914,"rel":16915},"https://terragrunt.gruntwork.io/",[436],[16917],{"type":418,"value":16918},"Terragrunt",{"type":418,"value":16920}," or ",{"type":413,"tag":432,"props":16922,"children":16925},{"href":16923,"rel":16924},"https://terraspace.cloud/",[436],[16926],{"type":418,"value":16927},"Terraspace",{"type":418,"value":16929},") were created by the community to keep the infrastructure code DRY and structured.",{"type":413,"tag":496,"props":16931,"children":16932},{"icon":16847},[16933],{"type":413,"tag":414,"props":16934,"children":16935},{},[16936,16938,16945],{"type":418,"value":16937},"If you want to know the differences between Terraform, Terragrunt, and Terraspace, BlogOps (the creator of Terraspace) has an interesting article on the ",{"type":413,"tag":432,"props":16939,"children":16942},{"href":16940,"rel":16941},"https://blog.boltops.com/2020/09/28/terraform-vs-terragrunt-vs-terraspace/",[436],[16943],{"type":418,"value":16944},"topic",{"type":418,"value":1404},{"type":413,"tag":414,"props":16947,"children":16948},{},[16949,16951,16958],{"type":418,"value":16950},"In 2017, Terraform also introduced its built-in way of managing different environments with ",{"type":413,"tag":432,"props":16952,"children":16955},{"href":16953,"rel":16954},"https://www.terraform.io/language/state/workspaces",[436],[16956],{"type":418,"value":16957},"Terraform workspaces",{"type":418,"value":16959},". 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":413,"tag":414,"props":16961,"children":16962},{},[16963,16965,16972,16974,16981],{"type":418,"value":16964},"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":413,"tag":432,"props":16966,"children":16969},{"href":16967,"rel":16968},"https://gruntwork.io/",[436],[16970],{"type":418,"value":16971},"Gruntwork",{"type":418,"value":16973},", the company behind Terragrunt has a ",{"type":413,"tag":432,"props":16975,"children":16978},{"href":16976,"rel":16977},"https://blog.gruntwork.io/a-comprehensive-guide-to-managing-secrets-in-your-terraform-code-1d586955ace1",[436],[16979],{"type":418,"value":16980},"detailed article",{"type":418,"value":16982}," 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":413,"tag":414,"props":16984,"children":16985},{},[16986],{"type":413,"tag":487,"props":16987,"children":16991},{"alt":16988,"className":16989,"src":16990},"Table showing options for managing secrets in Terraform from Gruntwork website.",[491,492],"/posts/images/pulumivstf_security_2.png",[],{"type":413,"tag":600,"props":16993,"children":16995},{"id":16994},"embedded-iac-through-an-api",[16996],{"type":418,"value":16997},"Embedded IaC through an API",{"type":413,"tag":414,"props":16999,"children":17000},{},[17001],{"type":418,"value":17002},"To provision infrastructure using Terraform or Pulumi, you can use their respective CLI.",{"type":413,"tag":414,"props":17004,"children":17005},{},[17006,17008,17015],{"type":418,"value":17007},"If you are using Terraform Cloud, you can also perform Terraform runs (called ",{"type":413,"tag":432,"props":17009,"children":17012},{"href":17010,"rel":17011},"https://www.terraform.io/cloud-docs/run#terraform-runs-and-remote-operations",[436],[17013],{"type":418,"value":17014},"remote operations",{"type":418,"value":17016}," 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":413,"tag":414,"props":17018,"children":17019},{},[17020],{"type":418,"value":17021},"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":413,"tag":443,"props":17023,"children":17024},{},[17025,17039,17044,17049],{"type":413,"tag":447,"props":17026,"children":17027},{},[17028,17030,17037],{"type":418,"value":17029},"provide self-service infrastructure to development teams in your organization (through ",{"type":413,"tag":432,"props":17031,"children":17034},{"href":17032,"rel":17033},"https://www.pulumi.com/blog/organizational-patterns-developer-portal/",[436],[17035],{"type":418,"value":17036},"developer portals",{"type":418,"value":17038}," for example)",{"type":413,"tag":447,"props":17040,"children":17041},{},[17042],{"type":418,"value":17043},"integrate provisioning of infrastructure into your platforms and tools (whether it is your custom framework, CLI, or CI/CD workflow)",{"type":413,"tag":447,"props":17045,"children":17046},{},[17047],{"type":418,"value":17048},"automate infrastructure provisioning for your custom needs",{"type":413,"tag":447,"props":17050,"children":17051},{},[17052],{"type":418,"value":17053},"do complex deployments that mix infrastructure and application code such as database migrations",{"type":413,"tag":414,"props":17055,"children":17056},{},[17057,17059,17064,17066,17072],{"type":418,"value":17058},"And for that, Pulumi has an ",{"type":413,"tag":432,"props":17060,"children":17062},{"href":16427,"rel":17061},[436],[17063],{"type":418,"value":16431},{"type":418,"value":17065}," 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":413,"tag":432,"props":17067,"children":17070},{"href":17068,"rel":17069},"https://www.pulumi.com/docs/guides/automation-api/",[436],[17071],{"type":418,"value":4994},{"type":418,"value":17073}," to learn more about it.",{"type":413,"tag":414,"props":17075,"children":17076},{},[17077],{"type":413,"tag":487,"props":17078,"children":17082},{"alt":17079,"className":17080,"src":17081},"Diagram explaining Pulumi Automation API.",[491,492],"/posts/images/pulumivstf_automationapi.png",[],{"type":413,"tag":600,"props":17084,"children":17086},{"id":17085},"what-else",[17087],{"type":418,"value":17088},"What else?",{"type":413,"tag":414,"props":17090,"children":17091},{},[17092,17094,17101],{"type":418,"value":17093},"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":413,"tag":432,"props":17095,"children":17098},{"href":17096,"rel":17097},"https://www.pulumi.com/docs/intro/vs/terraform/",[436],[17099],{"type":418,"value":17100},"Pulumi vs. Terraform page",{"type":418,"value":17102}," 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":413,"tag":420,"props":17104,"children":17106},{"id":17105},"providers",[17107],{"type":418,"value":17108},"Providers",{"type":413,"tag":600,"props":17110,"children":17112},{"id":17111},"why-providers",[17113],{"type":418,"value":17114},"Why providers?",{"type":413,"tag":414,"props":17116,"children":17117},{},[17118],{"type":418,"value":17119},"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":413,"tag":414,"props":17121,"children":17122},{},[17123],{"type":418,"value":17124},"You can see how this works in Pulumi on this schema from the documentation:",{"type":413,"tag":414,"props":17126,"children":17127},{},[17128],{"type":413,"tag":487,"props":17129,"children":17133},{"alt":17130,"className":17131,"src":17132},"Diagram of how Pulumi works.",[491,492],"/posts/images/pulumivstf_architecture.png",[],{"type":413,"tag":414,"props":17135,"children":17136},{},[17137],{"type":418,"value":17138},"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":413,"tag":600,"props":17140,"children":17142},{"id":17141},"how-does-pulumi-benefit-from-terraform-ecosystem",[17143],{"type":418,"value":17144},"How does Pulumi benefit from Terraform ecosystem?",{"type":413,"tag":414,"props":17146,"children":17147},{},[17148],{"type":418,"value":17149},"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":413,"tag":414,"props":17151,"children":17152},{},[17153],{"type":413,"tag":487,"props":17154,"children":17158},{"alt":17155,"className":17156,"src":17157,"width":591},"Pulumi Terraform Bridge repository on GitHub.",[491,492],"/posts/images/pulumivstf_bridge.png",[],{"type":413,"tag":414,"props":17160,"children":17161},{},[17162],{"type":418,"value":17163},"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":413,"tag":496,"props":17165,"children":17166},{"icon":16847},[17167],{"type":413,"tag":414,"props":17168,"children":17169},{},[17170,17172,17178],{"type":418,"value":17171},"If you are looking for a better explanation about how Pulumi \"bridges\" Terraform providers you can look at this ",{"type":413,"tag":432,"props":17173,"children":17176},{"href":17174,"rel":17175},"https://www.leebriggs.co.uk/blog/2021/11/06/pulumi-faqs.html#doesnt-pulumi-use-terraform-under-the-hood-",[436],[17177],{"type":418,"value":16280},{"type":418,"value":17179}," from Lee Briggs who work at Pulumi.",{"type":413,"tag":414,"props":17181,"children":17182},{},[17183,17185,17192],{"type":418,"value":17184},"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":413,"tag":432,"props":17186,"children":17189},{"href":17187,"rel":17188},"https://github.com/pulumi/pulumi-terraform-bridge",[436],[17190],{"type":418,"value":17191},"Pulumi Terraform Bridge",{"type":418,"value":1404},{"type":413,"tag":414,"props":17194,"children":17195},{},[17196,17198,17205],{"type":418,"value":17197},"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":413,"tag":432,"props":17199,"children":17202},{"href":17200,"rel":17201},"https://www.pulumi.com/docs/intro/vs/terraform/#providers-terraform",[436],[17203],{"type":418,"value":17204},"Pulumi documentation",{"type":418,"value":1404},{"type":413,"tag":414,"props":17207,"children":17208},{},[17209],{"type":413,"tag":487,"props":17210,"children":17214},{"alt":17211,"className":17212,"src":17213},"Pulumi documentation about using Terraform Providers.",[491,492],"/posts/images/pulumivstf_providers_1.png",[],{"type":413,"tag":600,"props":17216,"children":17218},{"id":17217},"the-problem-with-terraform-providers",[17219],{"type":418,"value":17220},"The problem with Terraform providers",{"type":413,"tag":414,"props":17222,"children":17223},{},[17224],{"type":418,"value":17225},"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":413,"tag":414,"props":17227,"children":17228},{},[17229],{"type":418,"value":17230},"It has the following inconveniences for a provider:",{"type":413,"tag":443,"props":17232,"children":17233},{},[17234,17239,17244],{"type":413,"tag":447,"props":17235,"children":17236},{},[17237],{"type":418,"value":17238},"it is more likely to contain bugs",{"type":413,"tag":447,"props":17240,"children":17241},{},[17242],{"type":418,"value":17243},"it doesn't have full coverage of its corresponding API",{"type":413,"tag":447,"props":17245,"children":17246},{},[17247],{"type":418,"value":17248},"it is always a little behind an API because it takes time for new resources or new features to be added",{"type":413,"tag":414,"props":17250,"children":17251},{},[17252,17254,17258],{"type":418,"value":17253},"At the beginning of the article I explain that I wanted my IaC solution should be ",{"type":413,"tag":706,"props":17255,"children":17256},{},[17257],{"type":418,"value":16606},{"type":418,"value":17259}," 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":413,"tag":414,"props":17261,"children":17262},{},[17263],{"type":413,"tag":487,"props":17264,"children":17268},{"alt":17265,"className":17266,"src":17267},"A bunch of clocks that are sitting on a shelf.",[491,492],"/posts/images/pulumivstf_clocks.jpg",[],{"type":413,"tag":414,"props":17270,"children":17271},{},[17272],{"type":418,"value":17273},"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":413,"tag":600,"props":17275,"children":17277},{"id":17276},"what-are-pulumi-native-providers",[17278],{"type":418,"value":17279},"What are  Pulumi native providers?",{"type":413,"tag":414,"props":17281,"children":17282},{},[17283,17285,17292],{"type":418,"value":17284},"In order to solve the problem described in the previous section, Pulumi introduced the concept of ",{"type":413,"tag":432,"props":17286,"children":17289},{"href":17287,"rel":17288},"https://www.pulumi.com/blog/pulumiup-native-providers/",[436],[17290],{"type":418,"value":17291},"native providers",{"type":418,"value":17293}," 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":413,"tag":443,"props":17295,"children":17296},{},[17297,17302,17307],{"type":413,"tag":447,"props":17298,"children":17299},{},[17300],{"type":418,"value":17301},"100% API coverage, so all resources available including the ones in preview",{"type":413,"tag":447,"props":17303,"children":17304},{},[17305],{"type":418,"value":17306},"Providers are always up-to-date with the APIs",{"type":413,"tag":447,"props":17308,"children":17309},{},[17310],{"type":418,"value":17311},"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":413,"tag":414,"props":17313,"children":17314},{},[17315],{"type":413,"tag":521,"props":17316,"children":17317},{},[17318],{"type":418,"value":17319},"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":413,"tag":496,"props":17321,"children":17322},{"icon":557},[17323],{"type":413,"tag":414,"props":17324,"children":17325},{},[17326,17328,17335],{"type":418,"value":17327},"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":413,"tag":432,"props":17329,"children":17332},{"href":17330,"rel":17331},"https://techcommunity.microsoft.com/t5/azure-tools-blog/announcing-azure-terrafy-and-azapi-terraform-provider-previews/ba-p/3270937",[436],[17333],{"type":418,"value":17334},"AzAPI",{"type":418,"value":17336},", 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":413,"tag":414,"props":17338,"children":17339},{},[17340],{"type":418,"value":17341},"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":413,"tag":414,"props":17343,"children":17344},{},[17345],{"type":413,"tag":487,"props":17346,"children":17350},{"alt":17347,"className":17348,"src":17349},"List of Pulumi native Providers on Pulumi website.",[491,492],"/posts/images/pulumivstf_providers_2.png",[],{"type":413,"tag":414,"props":17352,"children":17353},{},[17354],{"type":418,"value":17355},"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":413,"tag":600,"props":17357,"children":17359},{"id":17358},"more-about-providers",[17360],{"type":418,"value":17361},"More about providers",{"type":413,"tag":414,"props":17363,"children":17364},{},[17365,17367,17374],{"type":418,"value":17366},"If you have looked at ",{"type":413,"tag":432,"props":17368,"children":17371},{"href":17369,"rel":17370},"https://registry.terraform.io/browse/providers",[436],[17372],{"type":418,"value":17373},"Terraform registry",{"type":418,"value":17375},", 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":413,"tag":414,"props":17377,"children":17378},{},[17379],{"type":413,"tag":487,"props":17380,"children":17384},{"alt":17381,"className":17382,"src":17383},"A collection of office supplies laid out on a table.",[491,492],"/posts/images/pulumivstf_tools_1.jpg",[],{"type":413,"tag":414,"props":17386,"children":17387},{},[17388],{"type":418,"value":17389},"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":413,"tag":414,"props":17391,"children":17392},{},[17393,17395,17402,17404,17410],{"type":418,"value":17394},"For that, Pulumi has a concept of ",{"type":413,"tag":432,"props":17396,"children":17399},{"href":17397,"rel":17398},"https://www.pulumi.com/docs/intro/concepts/resources/dynamic-providers/",[436],[17400],{"type":418,"value":17401},"Dynamic Providers",{"type":418,"value":17403}," 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":413,"tag":432,"props":17405,"children":17408},{"href":17406,"rel":17407},"https://www.pulumi.com/blog/dynamic-providers/#sample-use-cases",[436],[17409],{"type":418,"value":16280},{"type":418,"value":544},{"type":413,"tag":420,"props":17412,"children":17414},{"id":17413},"using-programming-languages-the-best-way-to-do-iac",[17415],{"type":418,"value":17416},"Using programming languages: the best way to do IaC",{"type":413,"tag":414,"props":17418,"children":17419},{},[17420],{"type":418,"value":17421},"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":413,"tag":414,"props":17423,"children":17424},{},[17425],{"type":418,"value":17426},"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":413,"tag":600,"props":17428,"children":17430},{"id":17429},"better-aligned-with-the-devops-culture",[17431],{"type":418,"value":17432},"Better aligned with the DevOps culture",{"type":413,"tag":414,"props":17434,"children":17435},{},[17436],{"type":418,"value":17437},"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":413,"tag":414,"props":17439,"children":17440},{},[17441],{"type":418,"value":17442},"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":413,"tag":414,"props":17444,"children":17445},{},[17446],{"type":413,"tag":487,"props":17447,"children":17451},{"alt":17448,"className":17449,"src":17450},"The words \"together is the way\" spelled with cubes.",[491,492],"/posts/images/pulumivstf_together.jpg",[],{"type":413,"tag":414,"props":17453,"children":17454},{},[17455],{"type":418,"value":17456},"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":413,"tag":600,"props":17458,"children":17460},{"id":17459},"be-productive-faster-and-more",[17461],{"type":418,"value":17462},"Be productive faster and more",{"type":413,"tag":414,"props":17464,"children":17465},{},[17466],{"type":418,"value":17467},"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":413,"tag":414,"props":17469,"children":17470},{},[17471],{"type":418,"value":17472},"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":413,"tag":414,"props":17474,"children":17475},{},[17476],{"type":413,"tag":487,"props":17477,"children":17481},{"alt":17478,"className":17479,"src":17480},"A black and white banner that says everyone can code.",[491,492],"/posts/images/pulumivstf_eveyonecancode.jpg",[],{"type":413,"tag":414,"props":17483,"children":17484},{},[17485],{"type":418,"value":17486},"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":413,"tag":414,"props":17488,"children":17489},{},[17490],{"type":418,"value":17491},"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":413,"tag":496,"props":17493,"children":17494},{"icon":557},[17495],{"type":413,"tag":414,"props":17496,"children":17497},{},[17498],{"type":418,"value":17499},"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":413,"tag":600,"props":17501,"children":17503},{"id":17502},"a-better-developer-experience",[17504],{"type":418,"value":17505},"A better developer experience",{"type":413,"tag":414,"props":17507,"children":17508},{},[17509],{"type":418,"value":17510},"The developer experience is so much better when using programming languages instead of a DSL.",{"type":413,"tag":496,"props":17512,"children":17513},{"icon":4578},[17514],{"type":413,"tag":414,"props":17515,"children":17516},{},[17517],{"type":418,"value":17518},"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":413,"tag":414,"props":17520,"children":17521},{},[17522],{"type":418,"value":17523},"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":413,"tag":414,"props":17525,"children":17526},{},[17527,17529,17536],{"type":418,"value":17528},"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":413,"tag":432,"props":17530,"children":17533},{"href":17531,"rel":17532},"https://www.techwatching.dev/posts/pulumi-watch",[436],[17534],{"type":418,"value":17535},"my article",{"type":418,"value":17537}," on the topic).",{"type":413,"tag":414,"props":17539,"children":17540},{},[17541,17543,17549],{"type":418,"value":17542},"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":413,"tag":432,"props":17544,"children":17547},{"href":17545,"rel":17546},"https://www.pulumi.com/docs/guides/testing/",[436],[17548],{"type":418,"value":4994},{"type":418,"value":1404},{"type":413,"tag":600,"props":17551,"children":17553},{"id":17552},"more-capabilities",[17554],{"type":418,"value":17555},"More capabilities",{"type":413,"tag":414,"props":17557,"children":17558},{},[17559],{"type":418,"value":17560},"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":413,"tag":414,"props":17562,"children":17563},{},[17564],{"type":418,"value":17565},"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":413,"tag":600,"props":17567,"children":17569},{"id":17568},"the-future",[17570],{"type":418,"value":17571},"The future",{"type":413,"tag":414,"props":17573,"children":17574},{},[17575,17577,17584,17586,17592],{"type":418,"value":17576},"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":413,"tag":432,"props":17578,"children":17581},{"href":17579,"rel":17580},"https://aws.amazon.com/cdk/",[436],[17582],{"type":418,"value":17583},"AWS CDK",{"type":418,"value":17585}," 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":413,"tag":432,"props":17587,"children":17590},{"href":17588,"rel":17589},"https://www.terraform.io/cdktf#project-maturity",[436],[17591],{"type":418,"value":4994},{"type":418,"value":17593},". Pulumi has a clear head start but it's very interesting to see the whole IaC ecosystem evolving in this direction.",{"type":413,"tag":420,"props":17595,"children":17597},{"id":17596},"final-thoughts",[17598],{"type":418,"value":17599},"Final thoughts",{"type":413,"tag":600,"props":17601,"children":17603},{"id":17602},"summary",[17604],{"type":418,"value":17605},"Summary",{"type":413,"tag":414,"props":17607,"children":17608},{},[17609],{"type":418,"value":17610},"To sum up, I will choose Pulumi over Terraform for my next project because:",{"type":413,"tag":443,"props":17612,"children":17613},{},[17614,17619,17624,17629,17634],{"type":413,"tag":447,"props":17615,"children":17616},{},[17617],{"type":418,"value":17618},"it has 100% resource coverage of my cloud provider and more flexibility",{"type":413,"tag":447,"props":17620,"children":17621},{},[17622],{"type":418,"value":17623},"it allows me to be more productive using languages, tools, and libraries I am already familiar with",{"type":413,"tag":447,"props":17625,"children":17626},{},[17627],{"type":418,"value":17628},"it has more built-in functionalities and is more modern",{"type":413,"tag":447,"props":17630,"children":17631},{},[17632],{"type":418,"value":17633},"it is easier to learn and use (something that my teammates will appreciate)",{"type":413,"tag":447,"props":17635,"children":17636},{},[17637],{"type":418,"value":17638},"it is secure by default",{"type":413,"tag":414,"props":17640,"children":17641},{},[17642],{"type":413,"tag":487,"props":17643,"children":17647},{"alt":17644,"className":17645,"src":17646},"A hand moving a white chess piece to make the back king fall on a chess board.",[491,492],"/posts/images/pulumivstf_chess.jpg",[],{"type":413,"tag":600,"props":17649,"children":17651},{"id":17650},"disclaimers",[17652],{"type":418,"value":17653},"Disclaimers",{"type":413,"tag":414,"props":17655,"children":17656},{},[17657],{"type":418,"value":17658},"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":413,"tag":414,"props":17660,"children":17661},{},[17662],{"type":418,"value":17663},"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":413,"tag":414,"props":17665,"children":17666},{},[17667],{"type":418,"value":17668},"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":413,"tag":600,"props":17670,"children":17672},{"id":17671},"is-terraform-bad",[17673],{"type":418,"value":17674},"Is Terraform bad?",{"type":413,"tag":414,"props":17676,"children":17677},{},[17678],{"type":418,"value":17679},"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":413,"tag":414,"props":17681,"children":17682},{},[17683],{"type":418,"value":17684},"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":413,"tag":414,"props":17686,"children":17687},{},[17688],{"type":418,"value":17689},"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":413,"tag":414,"props":17691,"children":17692},{},[17693],{"type":418,"value":17694},"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":413,"tag":600,"props":17696,"children":17698},{"id":17697},"is-pulumi-perfect",[17699],{"type":418,"value":17700},"Is Pulumi perfect?",{"type":413,"tag":414,"props":17702,"children":17703},{},[17704],{"type":418,"value":17705},"No Pulumi is not perfect, no IaC solution is, there are always things to improve.",{"type":413,"tag":414,"props":17707,"children":17708},{},[17709],{"type":418,"value":17710},"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":413,"tag":414,"props":17712,"children":17713},{},[17714,17716,17722],{"type":418,"value":17715},"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":413,"tag":432,"props":17717,"children":17720},{"href":17718,"rel":17719},"https://github.com/pulumi/pulumi/issues/8963",[436],[17721],{"type":418,"value":542},{"type":418,"value":544},{"type":413,"tag":414,"props":17724,"children":17725},{},[17726],{"type":418,"value":17727},"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":413,"tag":600,"props":17729,"children":17731},{"id":17730},"which-infrastructure-as-code-solution-should-you-choose",[17732],{"type":418,"value":17733},"Which Infrastructure as Code solution should you choose?",{"type":413,"tag":414,"props":17735,"children":17736},{},[17737],{"type":418,"value":17738},"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":413,"tag":414,"props":17740,"children":17741},{},[17742],{"type":418,"value":17743},"If you have already invested a lot of time learning HCL/Terraform and are happy with it, you should probably continue using Terraform.",{"type":413,"tag":414,"props":17745,"children":17746},{},[17747],{"type":418,"value":17748},"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":413,"tag":414,"props":17750,"children":17751},{},[17752,17754,17759],{"type":418,"value":17753},"I would like to emphasize one thing: ",{"type":413,"tag":521,"props":17755,"children":17756},{},[17757],{"type":418,"value":17758},"when choosing an IaC solution, make an informed decision.",{"type":418,"value":17760}," 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":413,"tag":414,"props":17762,"children":17763},{},[17764],{"type":418,"value":17765},"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":401,"searchDepth":1045,"depth":1045,"links":17767},[17768,17769,17776,17783,17790],{"id":16543,"depth":1045,"text":16546},{"id":16645,"depth":1045,"text":16648,"children":17770},[17771,17772,17773,17774,17775],{"id":16656,"depth":1054,"text":16659},{"id":16748,"depth":1054,"text":16751},{"id":16865,"depth":1054,"text":16868},{"id":16994,"depth":1054,"text":16997},{"id":17085,"depth":1054,"text":17088},{"id":17105,"depth":1045,"text":17108,"children":17777},[17778,17779,17780,17781,17782],{"id":17111,"depth":1054,"text":17114},{"id":17141,"depth":1054,"text":17144},{"id":17217,"depth":1054,"text":17220},{"id":17276,"depth":1054,"text":17279},{"id":17358,"depth":1054,"text":17361},{"id":17413,"depth":1045,"text":17416,"children":17784},[17785,17786,17787,17788,17789],{"id":17429,"depth":1054,"text":17432},{"id":17459,"depth":1054,"text":17462},{"id":17502,"depth":1054,"text":17505},{"id":17552,"depth":1054,"text":17555},{"id":17568,"depth":1054,"text":17571},{"id":17596,"depth":1045,"text":17599,"children":17791},[17792,17793,17794,17795,17796],{"id":17602,"depth":1054,"text":17605},{"id":17650,"depth":1054,"text":17653},{"id":17671,"depth":1054,"text":17674},{"id":17697,"depth":1054,"text":17700},{"id":17730,"depth":1054,"text":17733},"content:1.posts:36.pulumi-vs-terraform.md","1.posts/36.pulumi-vs-terraform.md",{"_path":112,"_dir":399,"_draft":400,"_partial":400,"_locale":401,"title":111,"description":17800,"lead":17801,"date":17802,"image":17803,"badge":17805,"tags":17806,"body":17807,"_type":4691,"_id":20669,"_source":4693,"_file":20670,"_extension":4695},"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":17804},"/images/lockers_1.jpg",{"label":266},[252,299,228,239,312,315],{"type":410,"children":17808,"toc":20651},[17809,17813,17819,17841,17854,17862,17867,17873,17878,18153,18183,18189,18194,18492,18504,18692,18698,18703,18716,18817,18837,18842,18847,18873,18878,18884,18898,18907,18937,18945,18961,18973,18979,19007,19012,19024,19140,19152,19181,19193,19232,19241,19268,19321,19327,19346,19352,19365,19441,19474,19479,19646,19652,19657,19835,19840,19858,19864,19883,19972,19983,20137,20157,20162,20545,20550,20583,20589,20607,20616,20620,20632,20637,20642,20647],{"type":413,"tag":414,"props":17810,"children":17811},{},[17812],{"type":418,"value":17800},{"type":413,"tag":420,"props":17814,"children":17816},{"id":17815},"why-this-article",[17817],{"type":418,"value":17818},"Why this article?",{"type":413,"tag":414,"props":17820,"children":17821},{},[17822,17824,17831,17833,17839],{"type":418,"value":17823},"In ",{"type":413,"tag":432,"props":17825,"children":17828},{"href":17826,"rel":17827},"https://www.techwatching.dev/posts/sqlclient-active-directory-authent",[436],[17829],{"type":418,"value":17830},"a previous article",{"type":418,"value":17832},", 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":413,"tag":619,"props":17834,"children":17836},{"className":17835},[],[17837],{"type":418,"value":17838},"Microsoft.Data.SqlClient",{"type":418,"value":17840}," 'Active Directory Default' authentication mode to be more precise), and not on the configuration of the Azure AD authentication itself.",{"type":413,"tag":414,"props":17842,"children":17843},{},[17844,17846,17852],{"type":418,"value":17845},"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":413,"tag":432,"props":17847,"children":17849},{"href":5041,"rel":17848},[436],[17850],{"type":418,"value":17851},"enjoy very much Azure CLI",{"type":418,"value":17853},"), 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":413,"tag":496,"props":17855,"children":17856},{"icon":557},[17857],{"type":413,"tag":414,"props":17858,"children":17859},{},[17860],{"type":418,"value":17861},"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":413,"tag":414,"props":17863,"children":17864},{},[17865],{"type":418,"value":17866},"Now, let's get to the heart of the matter.",{"type":413,"tag":420,"props":17868,"children":17870},{"id":17869},"an-azure-ad-user-as-our-sql-server-administrator",[17871],{"type":418,"value":17872},"An Azure AD user as our SQL Server administrator",{"type":413,"tag":414,"props":17874,"children":17875},{},[17876],{"type":418,"value":17877},"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":413,"tag":612,"props":17879,"children":17881},{"className":981,"code":17880,"language":326,"meta":401,"style":401},"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",[17882],{"type":413,"tag":619,"props":17883,"children":17884},{"__ignoreMap":401},[17885,17913,17963,18013,18020,18075,18082,18102,18122,18146],{"type":413,"tag":623,"props":17886,"children":17887},{"class":625,"line":626},[17888,17892,17896,17900,17904,17909],{"type":413,"tag":623,"props":17889,"children":17890},{"style":630},[17891],{"type":418,"value":994},{"type":413,"tag":623,"props":17893,"children":17894},{"style":630},[17895],{"type":418,"value":879},{"type":413,"tag":623,"props":17897,"children":17898},{"style":671},[17899],{"type":418,"value":1004},{"type":413,"tag":623,"props":17901,"children":17902},{"style":671},[17903],{"type":418,"value":638},{"type":413,"tag":623,"props":17905,"children":17906},{"style":630},[17907],{"type":418,"value":17908}," Config",{"type":413,"tag":623,"props":17910,"children":17911},{"style":671},[17912],{"type":418,"value":2159},{"type":413,"tag":623,"props":17914,"children":17915},{"class":625,"line":1045},[17916,17920,17925,17929,17933,17937,17942,17946,17950,17955,17959],{"type":413,"tag":623,"props":17917,"children":17918},{"style":630},[17919],{"type":418,"value":994},{"type":413,"tag":623,"props":17921,"children":17922},{"style":630},[17923],{"type":418,"value":17924}," sqlAdAdminLogin",{"type":413,"tag":623,"props":17926,"children":17927},{"style":671},[17928],{"type":418,"value":1004},{"type":413,"tag":623,"props":17930,"children":17931},{"style":1058},[17932],{"type":418,"value":879},{"type":413,"tag":623,"props":17934,"children":17935},{"style":671},[17936],{"type":418,"value":1404},{"type":413,"tag":623,"props":17938,"children":17939},{"style":1407},[17940],{"type":418,"value":17941},"Require",{"type":413,"tag":623,"props":17943,"children":17944},{"style":671},[17945],{"type":418,"value":1018},{"type":413,"tag":623,"props":17947,"children":17948},{"style":671},[17949],{"type":418,"value":1023},{"type":413,"tag":623,"props":17951,"children":17952},{"style":635},[17953],{"type":418,"value":17954},"sqlAdAdmin",{"type":413,"tag":623,"props":17956,"children":17957},{"style":671},[17958],{"type":418,"value":1023},{"type":413,"tag":623,"props":17960,"children":17961},{"style":671},[17962],{"type":418,"value":2220},{"type":413,"tag":623,"props":17964,"children":17965},{"class":625,"line":1054},[17966,17970,17975,17979,17983,17987,17992,17996,18000,18005,18009],{"type":413,"tag":623,"props":17967,"children":17968},{"style":630},[17969],{"type":418,"value":994},{"type":413,"tag":623,"props":17971,"children":17972},{"style":630},[17973],{"type":418,"value":17974}," sqlAdAdminPassword",{"type":413,"tag":623,"props":17976,"children":17977},{"style":671},[17978],{"type":418,"value":1004},{"type":413,"tag":623,"props":17980,"children":17981},{"style":1058},[17982],{"type":418,"value":879},{"type":413,"tag":623,"props":17984,"children":17985},{"style":671},[17986],{"type":418,"value":1404},{"type":413,"tag":623,"props":17988,"children":17989},{"style":1407},[17990],{"type":418,"value":17991},"RequireSecret",{"type":413,"tag":623,"props":17993,"children":17994},{"style":671},[17995],{"type":418,"value":1018},{"type":413,"tag":623,"props":17997,"children":17998},{"style":671},[17999],{"type":418,"value":1023},{"type":413,"tag":623,"props":18001,"children":18002},{"style":635},[18003],{"type":418,"value":18004},"sqlAdPassword",{"type":413,"tag":623,"props":18006,"children":18007},{"style":671},[18008],{"type":418,"value":1023},{"type":413,"tag":623,"props":18010,"children":18011},{"style":671},[18012],{"type":418,"value":2220},{"type":413,"tag":623,"props":18014,"children":18015},{"class":625,"line":1087},[18016],{"type":413,"tag":623,"props":18017,"children":18018},{"emptyLinePlaceholder":2790},[18019],{"type":418,"value":2793},{"type":413,"tag":623,"props":18021,"children":18022},{"class":625,"line":1104},[18023,18027,18032,18036,18040,18045,18049,18053,18058,18062,18066,18070],{"type":413,"tag":623,"props":18024,"children":18025},{"style":630},[18026],{"type":418,"value":994},{"type":413,"tag":623,"props":18028,"children":18029},{"style":630},[18030],{"type":418,"value":18031}," sqlAdAdmin",{"type":413,"tag":623,"props":18033,"children":18034},{"style":671},[18035],{"type":418,"value":1004},{"type":413,"tag":623,"props":18037,"children":18038},{"style":671},[18039],{"type":418,"value":638},{"type":413,"tag":623,"props":18041,"children":18042},{"style":630},[18043],{"type":418,"value":18044}," User",{"type":413,"tag":623,"props":18046,"children":18047},{"style":671},[18048],{"type":418,"value":1018},{"type":413,"tag":623,"props":18050,"children":18051},{"style":671},[18052],{"type":418,"value":1023},{"type":413,"tag":623,"props":18054,"children":18055},{"style":635},[18056],{"type":418,"value":18057},"sqlAdmin",{"type":413,"tag":623,"props":18059,"children":18060},{"style":671},[18061],{"type":418,"value":1023},{"type":413,"tag":623,"props":18063,"children":18064},{"style":671},[18065],{"type":418,"value":1037},{"type":413,"tag":623,"props":18067,"children":18068},{"style":671},[18069],{"type":418,"value":638},{"type":413,"tag":623,"props":18071,"children":18072},{"style":630},[18073],{"type":418,"value":18074}," UserArgs\n",{"type":413,"tag":623,"props":18076,"children":18077},{"class":625,"line":1113},[18078],{"type":413,"tag":623,"props":18079,"children":18080},{"style":671},[18081],{"type":418,"value":1051},{"type":413,"tag":623,"props":18083,"children":18084},{"class":625,"line":1161},[18085,18090,18094,18098],{"type":413,"tag":623,"props":18086,"children":18087},{"style":1058},[18088],{"type":418,"value":18089},"    UserPrincipalName ",{"type":413,"tag":623,"props":18091,"children":18092},{"style":671},[18093],{"type":418,"value":1066},{"type":413,"tag":623,"props":18095,"children":18096},{"style":1058},[18097],{"type":418,"value":17924},{"type":413,"tag":623,"props":18099,"children":18100},{"style":671},[18101],{"type":418,"value":1084},{"type":413,"tag":623,"props":18103,"children":18104},{"class":625,"line":1207},[18105,18110,18114,18118],{"type":413,"tag":623,"props":18106,"children":18107},{"style":1058},[18108],{"type":418,"value":18109},"    Password ",{"type":413,"tag":623,"props":18111,"children":18112},{"style":671},[18113],{"type":418,"value":1066},{"type":413,"tag":623,"props":18115,"children":18116},{"style":1058},[18117],{"type":418,"value":17974},{"type":413,"tag":623,"props":18119,"children":18120},{"style":671},[18121],{"type":418,"value":1084},{"type":413,"tag":623,"props":18123,"children":18124},{"class":625,"line":1251},[18125,18129,18133,18137,18142],{"type":413,"tag":623,"props":18126,"children":18127},{"style":1058},[18128],{"type":418,"value":2668},{"type":413,"tag":623,"props":18130,"children":18131},{"style":671},[18132],{"type":418,"value":1066},{"type":413,"tag":623,"props":18134,"children":18135},{"style":671},[18136],{"type":418,"value":674},{"type":413,"tag":623,"props":18138,"children":18139},{"style":635},[18140],{"type":418,"value":18141},"Global SQL Admin",{"type":413,"tag":623,"props":18143,"children":18144},{"style":671},[18145],{"type":418,"value":684},{"type":413,"tag":623,"props":18147,"children":18148},{"class":625,"line":1296},[18149],{"type":413,"tag":623,"props":18150,"children":18151},{"style":671},[18152],{"type":418,"value":1352},{"type":413,"tag":414,"props":18154,"children":18155},{},[18156,18158,18164,18166,18172,18174,18181],{"type":418,"value":18157},"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":413,"tag":432,"props":18159,"children":18162},{"href":18160,"rel":18161},"https://www.pulumi.com/docs/intro/concepts/config/",[436],[18163],{"type":418,"value":262},{"type":418,"value":18165}," 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":413,"tag":619,"props":18167,"children":18169},{"className":18168},[],[18170],{"type":418,"value":18171},"config.RequireSecret",{"type":418,"value":18173}," method. Indeed to avoid exposing a secret in the configuration file or the state file, Pulumi has ",{"type":413,"tag":432,"props":18175,"children":18178},{"href":18176,"rel":18177},"https://www.pulumi.com/docs/intro/concepts/secrets/",[436],[18179],{"type":418,"value":18180},"built-in support for secret encryption and decryption",{"type":418,"value":18182}," (not sure Terraform folks can say the same thing 😉).",{"type":413,"tag":420,"props":18184,"children":18186},{"id":18185},"create-the-azure-sql-server-and-its-database",[18187],{"type":418,"value":18188},"Create the Azure SQL Server and its database.",{"type":413,"tag":414,"props":18190,"children":18191},{},[18192],{"type":418,"value":18193},"Now that we have our administrator account, we can create the Azure SQL Server:",{"type":413,"tag":612,"props":18195,"children":18197},{"className":981,"code":18196,"language":326,"meta":401,"style":401},"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",[18198],{"type":413,"tag":619,"props":18199,"children":18200},{"__ignoreMap":401},[18201,18280,18287,18314,18335,18342,18371,18399,18419,18449,18478,18485],{"type":413,"tag":623,"props":18202,"children":18203},{"class":625,"line":626},[18204,18208,18213,18217,18221,18226,18230,18234,18239,18243,18247,18251,18255,18259,18263,18267,18271,18275],{"type":413,"tag":623,"props":18205,"children":18206},{"style":630},[18207],{"type":418,"value":994},{"type":413,"tag":623,"props":18209,"children":18210},{"style":630},[18211],{"type":418,"value":18212}," sqlServer",{"type":413,"tag":623,"props":18214,"children":18215},{"style":671},[18216],{"type":418,"value":1004},{"type":413,"tag":623,"props":18218,"children":18219},{"style":671},[18220],{"type":418,"value":638},{"type":413,"tag":623,"props":18222,"children":18223},{"style":630},[18224],{"type":418,"value":18225}," Server",{"type":413,"tag":623,"props":18227,"children":18228},{"style":671},[18229],{"type":418,"value":1018},{"type":413,"tag":623,"props":18231,"children":18232},{"style":671},[18233],{"type":418,"value":2446},{"type":413,"tag":623,"props":18235,"children":18236},{"style":635},[18237],{"type":418,"value":18238},"sql-sqlDbWithAzureAd-",{"type":413,"tag":623,"props":18240,"children":18241},{"style":671},[18242],{"type":418,"value":2456},{"type":413,"tag":623,"props":18244,"children":18245},{"style":1058},[18246],{"type":418,"value":14027},{"type":413,"tag":623,"props":18248,"children":18249},{"style":671},[18250],{"type":418,"value":1404},{"type":413,"tag":623,"props":18252,"children":18253},{"style":1058},[18254],{"type":418,"value":14036},{"type":413,"tag":623,"props":18256,"children":18257},{"style":671},[18258],{"type":418,"value":1404},{"type":413,"tag":623,"props":18260,"children":18261},{"style":1058},[18262],{"type":418,"value":14078},{"type":413,"tag":623,"props":18264,"children":18265},{"style":671},[18266],{"type":418,"value":2465},{"type":413,"tag":623,"props":18268,"children":18269},{"style":671},[18270],{"type":418,"value":1037},{"type":413,"tag":623,"props":18272,"children":18273},{"style":671},[18274],{"type":418,"value":638},{"type":413,"tag":623,"props":18276,"children":18277},{"style":630},[18278],{"type":418,"value":18279}," ServerArgs\n",{"type":413,"tag":623,"props":18281,"children":18282},{"class":625,"line":1045},[18283],{"type":413,"tag":623,"props":18284,"children":18285},{"style":671},[18286],{"type":418,"value":1051},{"type":413,"tag":623,"props":18288,"children":18289},{"class":625,"line":1054},[18290,18294,18298,18302,18306,18310],{"type":413,"tag":623,"props":18291,"children":18292},{"style":1058},[18293],{"type":418,"value":14231},{"type":413,"tag":623,"props":18295,"children":18296},{"style":671},[18297],{"type":418,"value":1066},{"type":413,"tag":623,"props":18299,"children":18300},{"style":1058},[18301],{"type":418,"value":13992},{"type":413,"tag":623,"props":18303,"children":18304},{"style":671},[18305],{"type":418,"value":1404},{"type":413,"tag":623,"props":18307,"children":18308},{"style":1058},[18309],{"type":418,"value":3350},{"type":413,"tag":623,"props":18311,"children":18312},{"style":671},[18313],{"type":418,"value":1084},{"type":413,"tag":623,"props":18315,"children":18316},{"class":625,"line":1087},[18317,18322,18326,18330],{"type":413,"tag":623,"props":18318,"children":18319},{"style":1058},[18320],{"type":418,"value":18321},"    Administrators ",{"type":413,"tag":623,"props":18323,"children":18324},{"style":671},[18325],{"type":418,"value":1066},{"type":413,"tag":623,"props":18327,"children":18328},{"style":671},[18329],{"type":418,"value":638},{"type":413,"tag":623,"props":18331,"children":18332},{"style":630},[18333],{"type":418,"value":18334}," ServerExternalAdministratorArgs\n",{"type":413,"tag":623,"props":18336,"children":18337},{"class":625,"line":1104},[18338],{"type":413,"tag":623,"props":18339,"children":18340},{"style":671},[18341],{"type":418,"value":1110},{"type":413,"tag":623,"props":18343,"children":18344},{"class":625,"line":1113},[18345,18350,18354,18358,18362,18367],{"type":413,"tag":623,"props":18346,"children":18347},{"style":1058},[18348],{"type":418,"value":18349},"        Login ",{"type":413,"tag":623,"props":18351,"children":18352},{"style":671},[18353],{"type":418,"value":1066},{"type":413,"tag":623,"props":18355,"children":18356},{"style":1058},[18357],{"type":418,"value":18031},{"type":413,"tag":623,"props":18359,"children":18360},{"style":671},[18361],{"type":418,"value":1404},{"type":413,"tag":623,"props":18363,"children":18364},{"style":1058},[18365],{"type":418,"value":18366},"UserPrincipalName",{"type":413,"tag":623,"props":18368,"children":18369},{"style":671},[18370],{"type":418,"value":1084},{"type":413,"tag":623,"props":18372,"children":18373},{"class":625,"line":1161},[18374,18379,18383,18387,18391,18395],{"type":413,"tag":623,"props":18375,"children":18376},{"style":1058},[18377],{"type":418,"value":18378},"        Sid ",{"type":413,"tag":623,"props":18380,"children":18381},{"style":671},[18382],{"type":418,"value":1066},{"type":413,"tag":623,"props":18384,"children":18385},{"style":1058},[18386],{"type":418,"value":18031},{"type":413,"tag":623,"props":18388,"children":18389},{"style":671},[18390],{"type":418,"value":1404},{"type":413,"tag":623,"props":18392,"children":18393},{"style":1058},[18394],{"type":418,"value":1447},{"type":413,"tag":623,"props":18396,"children":18397},{"style":671},[18398],{"type":418,"value":1084},{"type":413,"tag":623,"props":18400,"children":18401},{"class":625,"line":1207},[18402,18407,18411,18415],{"type":413,"tag":623,"props":18403,"children":18404},{"style":1058},[18405],{"type":418,"value":18406},"        AzureADOnlyAuthentication ",{"type":413,"tag":623,"props":18408,"children":18409},{"style":671},[18410],{"type":418,"value":1066},{"type":413,"tag":623,"props":18412,"children":18413},{"style":5580},[18414],{"type":418,"value":9472},{"type":413,"tag":623,"props":18416,"children":18417},{"style":671},[18418],{"type":418,"value":1084},{"type":413,"tag":623,"props":18420,"children":18421},{"class":625,"line":1251},[18422,18427,18431,18436,18440,18445],{"type":413,"tag":623,"props":18423,"children":18424},{"style":1058},[18425],{"type":418,"value":18426},"        AdministratorType ",{"type":413,"tag":623,"props":18428,"children":18429},{"style":671},[18430],{"type":418,"value":1066},{"type":413,"tag":623,"props":18432,"children":18433},{"style":1058},[18434],{"type":418,"value":18435}," AdministratorType",{"type":413,"tag":623,"props":18437,"children":18438},{"style":671},[18439],{"type":418,"value":1404},{"type":413,"tag":623,"props":18441,"children":18442},{"style":1058},[18443],{"type":418,"value":18444},"ActiveDirectory",{"type":413,"tag":623,"props":18446,"children":18447},{"style":671},[18448],{"type":418,"value":1084},{"type":413,"tag":623,"props":18450,"children":18451},{"class":625,"line":1296},[18452,18457,18461,18465,18469,18474],{"type":413,"tag":623,"props":18453,"children":18454},{"style":1058},[18455],{"type":418,"value":18456},"        PrincipalType ",{"type":413,"tag":623,"props":18458,"children":18459},{"style":671},[18460],{"type":418,"value":1066},{"type":413,"tag":623,"props":18462,"children":18463},{"style":1058},[18464],{"type":418,"value":2941},{"type":413,"tag":623,"props":18466,"children":18467},{"style":671},[18468],{"type":418,"value":1404},{"type":413,"tag":623,"props":18470,"children":18471},{"style":1058},[18472],{"type":418,"value":18473},"User",{"type":413,"tag":623,"props":18475,"children":18476},{"style":671},[18477],{"type":418,"value":1084},{"type":413,"tag":623,"props":18479,"children":18480},{"class":625,"line":1337},[18481],{"type":413,"tag":623,"props":18482,"children":18483},{"style":671},[18484],{"type":418,"value":1343},{"type":413,"tag":623,"props":18486,"children":18487},{"class":625,"line":1346},[18488],{"type":413,"tag":623,"props":18489,"children":18490},{"style":671},[18491],{"type":418,"value":1352},{"type":413,"tag":414,"props":18493,"children":18494},{},[18495,18497,18502],{"type":418,"value":18496},"Nothing special here: we are using the variable ",{"type":413,"tag":619,"props":18498,"children":18500},{"className":18499},[],[18501],{"type":418,"value":18057},{"type":418,"value":18503}," 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":413,"tag":612,"props":18505,"children":18507},{"className":981,"code":18506,"language":326,"meta":401,"style":401},"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",[18508],{"type":413,"tag":619,"props":18509,"children":18510},{"__ignoreMap":401},[18511,18566,18573,18600,18628,18648,18655,18678,18685],{"type":413,"tag":623,"props":18512,"children":18513},{"class":625,"line":626},[18514,18518,18523,18527,18531,18536,18540,18544,18549,18553,18557,18561],{"type":413,"tag":623,"props":18515,"children":18516},{"style":630},[18517],{"type":418,"value":994},{"type":413,"tag":623,"props":18519,"children":18520},{"style":630},[18521],{"type":418,"value":18522}," database",{"type":413,"tag":623,"props":18524,"children":18525},{"style":671},[18526],{"type":418,"value":1004},{"type":413,"tag":623,"props":18528,"children":18529},{"style":671},[18530],{"type":418,"value":638},{"type":413,"tag":623,"props":18532,"children":18533},{"style":630},[18534],{"type":418,"value":18535}," Database",{"type":413,"tag":623,"props":18537,"children":18538},{"style":671},[18539],{"type":418,"value":1018},{"type":413,"tag":623,"props":18541,"children":18542},{"style":671},[18543],{"type":418,"value":1023},{"type":413,"tag":623,"props":18545,"children":18546},{"style":635},[18547],{"type":418,"value":18548},"sqldb-sqlDbWithAzureAd-Main",{"type":413,"tag":623,"props":18550,"children":18551},{"style":671},[18552],{"type":418,"value":1023},{"type":413,"tag":623,"props":18554,"children":18555},{"style":671},[18556],{"type":418,"value":1037},{"type":413,"tag":623,"props":18558,"children":18559},{"style":671},[18560],{"type":418,"value":638},{"type":413,"tag":623,"props":18562,"children":18563},{"style":630},[18564],{"type":418,"value":18565}," DatabaseArgs\n",{"type":413,"tag":623,"props":18567,"children":18568},{"class":625,"line":1045},[18569],{"type":413,"tag":623,"props":18570,"children":18571},{"style":671},[18572],{"type":418,"value":1051},{"type":413,"tag":623,"props":18574,"children":18575},{"class":625,"line":1054},[18576,18580,18584,18588,18592,18596],{"type":413,"tag":623,"props":18577,"children":18578},{"style":1058},[18579],{"type":418,"value":14231},{"type":413,"tag":623,"props":18581,"children":18582},{"style":671},[18583],{"type":418,"value":1066},{"type":413,"tag":623,"props":18585,"children":18586},{"style":1058},[18587],{"type":418,"value":13992},{"type":413,"tag":623,"props":18589,"children":18590},{"style":671},[18591],{"type":418,"value":1404},{"type":413,"tag":623,"props":18593,"children":18594},{"style":1058},[18595],{"type":418,"value":3350},{"type":413,"tag":623,"props":18597,"children":18598},{"style":671},[18599],{"type":418,"value":1084},{"type":413,"tag":623,"props":18601,"children":18602},{"class":625,"line":1087},[18603,18608,18612,18616,18620,18624],{"type":413,"tag":623,"props":18604,"children":18605},{"style":1058},[18606],{"type":418,"value":18607},"    ServerName ",{"type":413,"tag":623,"props":18609,"children":18610},{"style":671},[18611],{"type":418,"value":1066},{"type":413,"tag":623,"props":18613,"children":18614},{"style":1058},[18615],{"type":418,"value":18212},{"type":413,"tag":623,"props":18617,"children":18618},{"style":671},[18619],{"type":418,"value":1404},{"type":413,"tag":623,"props":18621,"children":18622},{"style":1058},[18623],{"type":418,"value":3350},{"type":413,"tag":623,"props":18625,"children":18626},{"style":671},[18627],{"type":418,"value":1084},{"type":413,"tag":623,"props":18629,"children":18630},{"class":625,"line":1104},[18631,18635,18639,18643],{"type":413,"tag":623,"props":18632,"children":18633},{"style":1058},[18634],{"type":418,"value":14296},{"type":413,"tag":623,"props":18636,"children":18637},{"style":671},[18638],{"type":418,"value":1066},{"type":413,"tag":623,"props":18640,"children":18641},{"style":671},[18642],{"type":418,"value":638},{"type":413,"tag":623,"props":18644,"children":18645},{"style":630},[18646],{"type":418,"value":18647}," SkuArgs\n",{"type":413,"tag":623,"props":18649,"children":18650},{"class":625,"line":1113},[18651],{"type":413,"tag":623,"props":18652,"children":18653},{"style":671},[18654],{"type":418,"value":1110},{"type":413,"tag":623,"props":18656,"children":18657},{"class":625,"line":1161},[18658,18662,18666,18670,18674],{"type":413,"tag":623,"props":18659,"children":18660},{"style":1058},[18661],{"type":418,"value":14365},{"type":413,"tag":623,"props":18663,"children":18664},{"style":671},[18665],{"type":418,"value":1066},{"type":413,"tag":623,"props":18667,"children":18668},{"style":671},[18669],{"type":418,"value":674},{"type":413,"tag":623,"props":18671,"children":18672},{"style":635},[18673],{"type":418,"value":14345},{"type":413,"tag":623,"props":18675,"children":18676},{"style":671},[18677],{"type":418,"value":684},{"type":413,"tag":623,"props":18679,"children":18680},{"class":625,"line":1207},[18681],{"type":413,"tag":623,"props":18682,"children":18683},{"style":671},[18684],{"type":418,"value":2097},{"type":413,"tag":623,"props":18686,"children":18687},{"class":625,"line":1251},[18688],{"type":413,"tag":623,"props":18689,"children":18690},{"style":671},[18691],{"type":418,"value":1352},{"type":413,"tag":420,"props":18693,"children":18695},{"id":18694},"grant-sql-database-access-permissions-to-azure-ad-entities",[18696],{"type":418,"value":18697},"Grant SQL Database access permissions to Azure AD entities",{"type":413,"tag":414,"props":18699,"children":18700},{},[18701],{"type":418,"value":18702},"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":413,"tag":414,"props":18704,"children":18705},{},[18706,18708,18715],{"type":418,"value":18707},"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":413,"tag":432,"props":18709,"children":18712},{"href":18710,"rel":18711},"https://docs.microsoft.com/en-us/azure/app-service/tutorial-connect-msi-sql-database?tabs=windowsclient%2Cef%2Cdotnet#grant-permissions-to-managed-identity",[436],[18713],{"type":418,"value":18714},"in the documentation",{"type":418,"value":1404},{"type":413,"tag":612,"props":18717,"children":18721},{"className":18718,"code":18719,"language":18720,"meta":401,"style":401},"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",[18722],{"type":413,"tag":619,"props":18723,"children":18724},{"__ignoreMap":401},[18725,18757,18785,18809],{"type":413,"tag":623,"props":18726,"children":18727},{"class":625,"line":626},[18728,18733,18738,18743,18748,18753],{"type":413,"tag":623,"props":18729,"children":18730},{"style":3551},[18731],{"type":418,"value":18732},"CREATE",{"type":413,"tag":623,"props":18734,"children":18735},{"style":1058},[18736],{"type":418,"value":18737}," USER [\u003Cidentity-name>] ",{"type":413,"tag":623,"props":18739,"children":18740},{"style":3551},[18741],{"type":418,"value":18742},"FROM",{"type":413,"tag":623,"props":18744,"children":18745},{"style":3551},[18746],{"type":418,"value":18747}," EXTERNAL",{"type":413,"tag":623,"props":18749,"children":18750},{"style":3551},[18751],{"type":418,"value":18752}," PROVIDER",{"type":413,"tag":623,"props":18754,"children":18755},{"style":1058},[18756],{"type":418,"value":2524},{"type":413,"tag":623,"props":18758,"children":18759},{"class":625,"line":1045},[18760,18765,18770,18775,18780],{"type":413,"tag":623,"props":18761,"children":18762},{"style":3551},[18763],{"type":418,"value":18764},"ALTER",{"type":413,"tag":623,"props":18766,"children":18767},{"style":3551},[18768],{"type":418,"value":18769}," ROLE",{"type":413,"tag":623,"props":18771,"children":18772},{"style":1058},[18773],{"type":418,"value":18774}," db_datareader ",{"type":413,"tag":623,"props":18776,"children":18777},{"style":3551},[18778],{"type":418,"value":18779},"ADD",{"type":413,"tag":623,"props":18781,"children":18782},{"style":1058},[18783],{"type":418,"value":18784}," MEMBER [\u003Cidentity-name>];\n",{"type":413,"tag":623,"props":18786,"children":18787},{"class":625,"line":1054},[18788,18792,18796,18801,18805],{"type":413,"tag":623,"props":18789,"children":18790},{"style":3551},[18791],{"type":418,"value":18764},{"type":413,"tag":623,"props":18793,"children":18794},{"style":3551},[18795],{"type":418,"value":18769},{"type":413,"tag":623,"props":18797,"children":18798},{"style":1058},[18799],{"type":418,"value":18800}," db_datawriter ",{"type":413,"tag":623,"props":18802,"children":18803},{"style":3551},[18804],{"type":418,"value":18779},{"type":413,"tag":623,"props":18806,"children":18807},{"style":1058},[18808],{"type":418,"value":18784},{"type":413,"tag":623,"props":18810,"children":18811},{"class":625,"line":1087},[18812],{"type":413,"tag":623,"props":18813,"children":18814},{"style":3551},[18815],{"type":418,"value":18816},"GO\n",{"type":413,"tag":414,"props":18818,"children":18819},{},[18820,18822,18828,18829,18835],{"type":418,"value":18821},"With this command, we are creating a user and giving ",{"type":413,"tag":619,"props":18823,"children":18825},{"className":18824},[],[18826],{"type":418,"value":18827},"db_datareader",{"type":418,"value":1778},{"type":413,"tag":619,"props":18830,"children":18832},{"className":18831},[],[18833],{"type":418,"value":18834},"db_datawriter",{"type":418,"value":18836}," 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":413,"tag":414,"props":18838,"children":18839},{},[18840],{"type":418,"value":18841},"So it's not just about setting a property to properly configure an Azure resource, it's a bit more complicated.",{"type":413,"tag":414,"props":18843,"children":18844},{},[18845],{"type":418,"value":18846},"I see multiple ways of doing that:",{"type":413,"tag":443,"props":18848,"children":18849},{},[18850,18855,18860],{"type":413,"tag":447,"props":18851,"children":18852},{},[18853],{"type":418,"value":18854},"Create a new Pulumi provider \"SQL Server provider\" that is to able manage users in an SQL Server database",{"type":413,"tag":447,"props":18856,"children":18857},{},[18858],{"type":418,"value":18859},"Write custom C# code that executes the SQL command once the database is created",{"type":413,"tag":447,"props":18861,"children":18862},{},[18863,18865,18871],{"type":418,"value":18864},"Use the Pulumi Command provider to execute the SQL command using the ",{"type":413,"tag":619,"props":18866,"children":18868},{"className":18867},[],[18869],{"type":418,"value":18870},"sqlcmd",{"type":418,"value":18872}," utility",{"type":413,"tag":414,"props":18874,"children":18875},{},[18876],{"type":418,"value":18877},"Let's review these solutions.",{"type":413,"tag":600,"props":18879,"children":18881},{"id":18880},"new-sql-server-provider",[18882],{"type":418,"value":18883},"New \"SQL Server Provider\"",{"type":413,"tag":414,"props":18885,"children":18886},{},[18887,18889,18896],{"type":418,"value":18888},"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":413,"tag":432,"props":18890,"children":18893},{"href":18891,"rel":18892},"https://github.com/pulumi/pulumi-provider-boilerplate",[436],[18894],{"type":418,"value":18895},"Pulumi GitHub repository",{"type":418,"value":18897}," 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":413,"tag":414,"props":18899,"children":18900},{},[18901],{"type":413,"tag":487,"props":18902,"children":18906},{"alt":18903,"className":18904,"src":18905},"xyz Pulumi Provider boilerplate code repository on GitHub.",[491,492],"/posts/images/sqldatabase_ad_provider_1.png",[],{"type":413,"tag":414,"props":18908,"children":18909},{},[18910,18912,18919,18921,18926,18928,18935],{"type":418,"value":18911},"Another way would be to adapt the existing ",{"type":413,"tag":432,"props":18913,"children":18916},{"href":18914,"rel":18915},"https://registry.terraform.io/providers/betr-io/mssql/latest/docs",[436],[18917],{"type":418,"value":18918},"Microsoft SQL Server Provider",{"type":418,"value":18920}," 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":413,"tag":432,"props":18922,"children":18924},{"href":17187,"rel":18923},[436],[18925],{"type":418,"value":17191},{"type":418,"value":18927},". 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":413,"tag":432,"props":18929,"children":18932},{"href":18930,"rel":18931},"https://github.com/pulumi/pulumi-tf-provider-boilerplate",[436],[18933],{"type":418,"value":18934},"GitHub repository",{"type":418,"value":18936}," contains boilerplate code to do exactly that.",{"type":413,"tag":414,"props":18938,"children":18939},{},[18940],{"type":413,"tag":487,"props":18941,"children":18944},{"alt":17155,"className":18942,"src":18943},[491,492],"/posts/images/sqldatabase_ad_provider_2.png",[],{"type":413,"tag":496,"props":18946,"children":18947},{"icon":557},[18948],{"type":413,"tag":414,"props":18949,"children":18950},{},[18951,18953,18959],{"type":418,"value":18952},"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":413,"tag":619,"props":18954,"children":18956},{"className":18955},[],[18957],{"type":418,"value":18958},"mssql",{"type":418,"value":18960}," one.",{"type":413,"tag":414,"props":18962,"children":18963},{},[18964,18966,18971],{"type":418,"value":18965},"This first solution of creating a new \"SQL Server Provider\" (whether it be from scratch, from boilerplate code, or from the ",{"type":413,"tag":619,"props":18967,"children":18969},{"className":18968},[],[18970],{"type":418,"value":18958},{"type":418,"value":18972}," 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":413,"tag":600,"props":18974,"children":18976},{"id":18975},"custom-c-code",[18977],{"type":418,"value":18978},"Custom C# code",{"type":413,"tag":414,"props":18980,"children":18981},{},[18982,18984,18996,18998,19005],{"type":418,"value":18983},"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":413,"tag":432,"props":18985,"children":18988},{"href":18986,"rel":18987},"https://docs.microsoft.com/en-us/sql/connect/ado-net/overview-sqlclient-driver",[436],[18989,18994],{"type":413,"tag":619,"props":18990,"children":18992},{"className":18991},[],[18993],{"type":418,"value":17838},{"type":418,"value":18995}," library",{"type":418,"value":18997}," (which is a data provider for Azure SQL Database) to connect and send commands to the database. And if I want to use ",{"type":413,"tag":432,"props":18999,"children":19002},{"href":19000,"rel":19001},"https://github.com/DapperLib/Dapper",[436],[19003],{"type":418,"value":19004},"Dapper",{"type":418,"value":19006}," 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":413,"tag":414,"props":19008,"children":19009},{},[19010],{"type":418,"value":19011},"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":413,"tag":414,"props":19013,"children":19014},{},[19015,19017,19022],{"type":418,"value":19016},"The easiest way is to use an ",{"type":413,"tag":619,"props":19018,"children":19020},{"className":19019},[],[19021],{"type":418,"value":2187},{"type":418,"value":19023}," method on an output of the database like this:",{"type":413,"tag":612,"props":19025,"children":19027},{"className":981,"code":19026,"language":326,"meta":401,"style":401},"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",[19028],{"type":413,"tag":619,"props":19029,"children":19030},{"__ignoreMap":401},[19031,19067,19074,19082,19090,19098,19106,19118,19133],{"type":413,"tag":623,"props":19032,"children":19033},{"class":625,"line":626},[19034,19039,19043,19047,19051,19055,19059,19063],{"type":413,"tag":623,"props":19035,"children":19036},{"style":1058},[19037],{"type":418,"value":19038},"database",{"type":413,"tag":623,"props":19040,"children":19041},{"style":671},[19042],{"type":418,"value":1404},{"type":413,"tag":623,"props":19044,"children":19045},{"style":1058},[19046],{"type":418,"value":3350},{"type":413,"tag":623,"props":19048,"children":19049},{"style":671},[19050],{"type":418,"value":1404},{"type":413,"tag":623,"props":19052,"children":19053},{"style":1407},[19054],{"type":418,"value":2187},{"type":413,"tag":623,"props":19056,"children":19057},{"style":671},[19058],{"type":418,"value":1018},{"type":413,"tag":623,"props":19060,"children":19061},{"style":630},[19062],{"type":418,"value":5665},{"type":413,"tag":623,"props":19064,"children":19065},{"style":671},[19066],{"type":418,"value":2351},{"type":413,"tag":623,"props":19068,"children":19069},{"class":625,"line":1045},[19070],{"type":413,"tag":623,"props":19071,"children":19072},{"style":671},[19073],{"type":418,"value":1051},{"type":413,"tag":623,"props":19075,"children":19076},{"class":625,"line":1054},[19077],{"type":413,"tag":623,"props":19078,"children":19079},{"style":8144},[19080],{"type":418,"value":19081},"    /*** \n",{"type":413,"tag":623,"props":19083,"children":19084},{"class":625,"line":1087},[19085],{"type":413,"tag":623,"props":19086,"children":19087},{"style":8144},[19088],{"type":418,"value":19089},"     * Indempotent code using Microsoft.Data.SqlClient library\n",{"type":413,"tag":623,"props":19091,"children":19092},{"class":625,"line":1104},[19093],{"type":413,"tag":623,"props":19094,"children":19095},{"style":8144},[19096],{"type":418,"value":19097},"     * to execute the SQL command that assigns the correct roles\n",{"type":413,"tag":623,"props":19099,"children":19100},{"class":625,"line":1113},[19101],{"type":413,"tag":623,"props":19102,"children":19103},{"style":8144},[19104],{"type":418,"value":19105},"     * to the Azure AD group we want to have access to the database.\n",{"type":413,"tag":623,"props":19107,"children":19108},{"class":625,"line":1161},[19109,19114],{"type":413,"tag":623,"props":19110,"children":19111},{"style":8144},[19112],{"type":418,"value":19113},"    ***/",{"type":413,"tag":623,"props":19115,"children":19116},{"style":1058},[19117],{"type":418,"value":3120},{"type":413,"tag":623,"props":19119,"children":19120},{"class":625,"line":1207},[19121,19125,19129],{"type":413,"tag":623,"props":19122,"children":19123},{"style":2485},[19124],{"type":418,"value":2488},{"type":413,"tag":623,"props":19126,"children":19127},{"style":5580},[19128],{"type":418,"value":9472},{"type":413,"tag":623,"props":19130,"children":19131},{"style":671},[19132],{"type":418,"value":2524},{"type":413,"tag":623,"props":19134,"children":19135},{"class":625,"line":1251},[19136],{"type":413,"tag":623,"props":19137,"children":19138},{"style":671},[19139],{"type":418,"value":1352},{"type":413,"tag":414,"props":19141,"children":19142},{},[19143,19145,19150],{"type":418,"value":19144},"The code in the ",{"type":413,"tag":619,"props":19146,"children":19148},{"className":19147},[],[19149],{"type":418,"value":2187},{"type":418,"value":19151}," 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":413,"tag":414,"props":19153,"children":19154},{},[19155,19157,19162,19164,19170,19172,19179],{"type":418,"value":19156},"Another way would be to use ",{"type":413,"tag":432,"props":19158,"children":19160},{"href":17397,"rel":19159},[436],[19161],{"type":418,"value":17401},{"type":418,"value":19163}," 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":413,"tag":432,"props":19165,"children":19167},{"href":17406,"rel":19166},[436],[19168],{"type":418,"value":19169},"this Pulumi article",{"type":418,"value":19171},". 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":413,"tag":432,"props":19173,"children":19176},{"href":19174,"rel":19175},"https://github.com/pulumi/pulumi/issues/3638",[436],[19177],{"type":418,"value":19178},"this GitHub issue",{"type":418,"value":19180},", .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":413,"tag":600,"props":19182,"children":19184},{"id":19183},"command-provider-with-the-sqlcmd-utility",[19185,19187,19192],{"type":418,"value":19186},"Command provider with the ",{"type":413,"tag":619,"props":19188,"children":19190},{"className":19189},[],[19191],{"type":418,"value":18870},{"type":418,"value":18872},{"type":413,"tag":414,"props":19194,"children":19195},{},[19196,19202,19204,19215,19217,19222,19224,19231],{"type":413,"tag":432,"props":19197,"children":19199},{"href":18710,"rel":19198},[436],[19200],{"type":418,"value":19201},"The Microsoft tutorial",{"type":418,"value":19203},", that shows how to grant database permissions to an Azure AD entity, explains how the necessary SQL commands can be run using the ",{"type":413,"tag":432,"props":19205,"children":19208},{"href":19206,"rel":19207},"https://docs.microsoft.com/en-us/sql/tools/sqlcmd-utility",[436],[19209,19214],{"type":413,"tag":619,"props":19210,"children":19212},{"className":19211},[],[19213],{"type":418,"value":18870},{"type":418,"value":18872},{"type":418,"value":19216},". So instead of writing some C# code to do the same, an interesting idea would be to directly run the ",{"type":413,"tag":619,"props":19218,"children":19220},{"className":19219},[],[19221],{"type":418,"value":18870},{"type":418,"value":19223}," utility. And you know what? There is a Pulumi provider for executing commands and scripts: ",{"type":413,"tag":432,"props":19225,"children":19228},{"href":19226,"rel":19227},"https://www.pulumi.com/registry/packages/command/api-docs/",[436],[19229],{"type":418,"value":19230},"the Command Provider",{"type":418,"value":1404},{"type":413,"tag":414,"props":19233,"children":19234},{},[19235],{"type":413,"tag":487,"props":19236,"children":19240},{"alt":19237,"className":19238,"src":19239},"Pulumi Command Provider on GitHub.",[491,492],"/posts/images/sqldatabase_ad_pulumi_1.png",[],{"type":413,"tag":414,"props":19242,"children":19243},{},[19244,19246,19251,19253,19259,19261,19266],{"type":418,"value":19245},"Because it's a Pulumi provider, the ",{"type":413,"tag":619,"props":19247,"children":19249},{"className":19248},[],[19250],{"type":418,"value":18870},{"type":418,"value":19252}," 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":413,"tag":619,"props":19254,"children":19256},{"className":19255},[],[19257],{"type":418,"value":19258},"create",{"type":418,"value":19260}," script when the resource is created and so on). So it's very nice and not the same as executing the ",{"type":413,"tag":619,"props":19262,"children":19264},{"className":19263},[],[19265],{"type":418,"value":18870},{"type":418,"value":19267}," 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":413,"tag":496,"props":19269,"children":19270},{"icon":4578},[19271],{"type":413,"tag":414,"props":19272,"children":19273},{},[19274,19276,19281,19282,19288,19290,19296,19297,19303,19304,19310,19312,19319],{"type":418,"value":19275},"Pulumi Command Provider is currently in preview and only supports running scripts on ",{"type":413,"tag":619,"props":19277,"children":19279},{"className":19278},[],[19280],{"type":418,"value":19258},{"type":418,"value":1778},{"type":413,"tag":619,"props":19283,"children":19285},{"className":19284},[],[19286],{"type":418,"value":19287},"destroy",{"type":418,"value":19289}," operations (support for ",{"type":413,"tag":619,"props":19291,"children":19293},{"className":19292},[],[19294],{"type":418,"value":19295},"diff",{"type":418,"value":1770},{"type":413,"tag":619,"props":19298,"children":19300},{"className":19299},[],[19301],{"type":418,"value":19302},"update",{"type":418,"value":1778},{"type":413,"tag":619,"props":19305,"children":19307},{"className":19306},[],[19308],{"type":418,"value":19309},"read",{"type":418,"value":19311}," operations ",{"type":413,"tag":432,"props":19313,"children":19316},{"href":19314,"rel":19315},"https://github.com/pulumi/pulumi-command/issues/20",[436],[19317],{"type":418,"value":19318},"will probably be added later",{"type":418,"value":19320},"). 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":413,"tag":420,"props":19322,"children":19324},{"id":19323},"implement-the-database-permissions-for-an-azure-ad-group",[19325],{"type":418,"value":19326},"Implement the database permissions for an Azure AD Group",{"type":413,"tag":414,"props":19328,"children":19329},{},[19330,19332,19337,19339,19344],{"type":418,"value":19331},"Of the 3 possible solutions let's take the 3rd one with the Command provider and the ",{"type":413,"tag":619,"props":19333,"children":19335},{"className":19334},[],[19336],{"type":418,"value":18870},{"type":418,"value":19338}," utility. It is probably not the \"best\" solution but I thought it would be simpler to use the ",{"type":413,"tag":619,"props":19340,"children":19342},{"className":19341},[],[19343],{"type":418,"value":18870},{"type":418,"value":19345}," 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":413,"tag":600,"props":19347,"children":19349},{"id":19348},"allow-the-machine-running-the-pulumi-program-to-connect-to-the-sql-server",[19350],{"type":418,"value":19351},"Allow the machine running the Pulumi program to connect to the SQL Server",{"type":413,"tag":414,"props":19353,"children":19354},{},[19355,19357,19363],{"type":418,"value":19356},"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":413,"tag":619,"props":19358,"children":19360},{"className":19359},[],[19361],{"type":418,"value":19362},"ipify API",{"type":418,"value":19364},". It's a simple open source HTTP API that returns the public IP address of the caller.",{"type":413,"tag":612,"props":19366,"children":19368},{"className":981,"code":19367,"language":326,"meta":401,"style":401},"var publicIp = Output.Create(new HttpClient().GetStringAsync(\"https://api.ipify.org\"));\n",[19369],{"type":413,"tag":619,"props":19370,"children":19371},{"__ignoreMap":401},[19372],{"type":413,"tag":623,"props":19373,"children":19374},{"class":625,"line":626},[19375,19379,19384,19388,19392,19396,19401,19405,19409,19414,19419,19423,19427,19432,19436],{"type":413,"tag":623,"props":19376,"children":19377},{"style":630},[19378],{"type":418,"value":994},{"type":413,"tag":623,"props":19380,"children":19381},{"style":630},[19382],{"type":418,"value":19383}," publicIp",{"type":413,"tag":623,"props":19385,"children":19386},{"style":671},[19387],{"type":418,"value":1004},{"type":413,"tag":623,"props":19389,"children":19390},{"style":1058},[19391],{"type":418,"value":3003},{"type":413,"tag":623,"props":19393,"children":19394},{"style":671},[19395],{"type":418,"value":1404},{"type":413,"tag":623,"props":19397,"children":19398},{"style":1407},[19399],{"type":418,"value":19400},"Create",{"type":413,"tag":623,"props":19402,"children":19403},{"style":671},[19404],{"type":418,"value":2389},{"type":413,"tag":623,"props":19406,"children":19407},{"style":630},[19408],{"type":418,"value":16076},{"type":413,"tag":623,"props":19410,"children":19411},{"style":671},[19412],{"type":418,"value":19413},"().",{"type":413,"tag":623,"props":19415,"children":19416},{"style":1407},[19417],{"type":418,"value":19418},"GetStringAsync",{"type":413,"tag":623,"props":19420,"children":19421},{"style":671},[19422],{"type":418,"value":1018},{"type":413,"tag":623,"props":19424,"children":19425},{"style":671},[19426],{"type":418,"value":1023},{"type":413,"tag":623,"props":19428,"children":19429},{"style":635},[19430],{"type":418,"value":19431},"https://api.ipify.org",{"type":413,"tag":623,"props":19433,"children":19434},{"style":671},[19435],{"type":418,"value":1023},{"type":413,"tag":623,"props":19437,"children":19438},{"style":671},[19439],{"type":418,"value":19440},"));\n",{"type":413,"tag":496,"props":19442,"children":19443},{"icon":557},[19444],{"type":413,"tag":414,"props":19445,"children":19446},{},[19447,19449,19455,19457,19463,19465,19472],{"type":418,"value":19448},"You can note here that we are just using standard C# code with an ",{"type":413,"tag":619,"props":19450,"children":19452},{"className":19451},[],[19453],{"type":418,"value":19454},"HttpClient",{"type":418,"value":19456}," that makes a ",{"type":413,"tag":619,"props":19458,"children":19460},{"className":19459},[],[19461],{"type":418,"value":19462},"GET",{"type":418,"value":19464}," 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":413,"tag":432,"props":19466,"children":19469},{"href":19467,"rel":19468},"https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http",[436],[19470],{"type":418,"value":19471},"http data source",{"type":418,"value":19473}," 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":413,"tag":414,"props":19475,"children":19476},{},[19477],{"type":418,"value":19478},"Now we can enable this public IP by creating a firewall rule in the SQL Server.",{"type":413,"tag":612,"props":19480,"children":19482},{"className":981,"code":19481,"language":326,"meta":401,"style":401},"var enableLocalMachine = new FirewallRule(\"AllowLocalMachine\", new FirewallRuleArgs\n{\n    ResourceGroupName = resourceGroup.Name,\n    ServerName = sqlServer.Name,\n    StartIpAddress = publicIp,\n    EndIpAddress = publicIp\n});\n",[19483],{"type":413,"tag":619,"props":19484,"children":19485},{"__ignoreMap":401},[19486,19541,19548,19575,19602,19622,19639],{"type":413,"tag":623,"props":19487,"children":19488},{"class":625,"line":626},[19489,19493,19498,19502,19506,19511,19515,19519,19524,19528,19532,19536],{"type":413,"tag":623,"props":19490,"children":19491},{"style":630},[19492],{"type":418,"value":994},{"type":413,"tag":623,"props":19494,"children":19495},{"style":630},[19496],{"type":418,"value":19497}," enableLocalMachine",{"type":413,"tag":623,"props":19499,"children":19500},{"style":671},[19501],{"type":418,"value":1004},{"type":413,"tag":623,"props":19503,"children":19504},{"style":671},[19505],{"type":418,"value":638},{"type":413,"tag":623,"props":19507,"children":19508},{"style":630},[19509],{"type":418,"value":19510}," FirewallRule",{"type":413,"tag":623,"props":19512,"children":19513},{"style":671},[19514],{"type":418,"value":1018},{"type":413,"tag":623,"props":19516,"children":19517},{"style":671},[19518],{"type":418,"value":1023},{"type":413,"tag":623,"props":19520,"children":19521},{"style":635},[19522],{"type":418,"value":19523},"AllowLocalMachine",{"type":413,"tag":623,"props":19525,"children":19526},{"style":671},[19527],{"type":418,"value":1023},{"type":413,"tag":623,"props":19529,"children":19530},{"style":671},[19531],{"type":418,"value":1037},{"type":413,"tag":623,"props":19533,"children":19534},{"style":671},[19535],{"type":418,"value":638},{"type":413,"tag":623,"props":19537,"children":19538},{"style":630},[19539],{"type":418,"value":19540}," FirewallRuleArgs\n",{"type":413,"tag":623,"props":19542,"children":19543},{"class":625,"line":1045},[19544],{"type":413,"tag":623,"props":19545,"children":19546},{"style":671},[19547],{"type":418,"value":1051},{"type":413,"tag":623,"props":19549,"children":19550},{"class":625,"line":1054},[19551,19555,19559,19563,19567,19571],{"type":413,"tag":623,"props":19552,"children":19553},{"style":1058},[19554],{"type":418,"value":14231},{"type":413,"tag":623,"props":19556,"children":19557},{"style":671},[19558],{"type":418,"value":1066},{"type":413,"tag":623,"props":19560,"children":19561},{"style":1058},[19562],{"type":418,"value":13992},{"type":413,"tag":623,"props":19564,"children":19565},{"style":671},[19566],{"type":418,"value":1404},{"type":413,"tag":623,"props":19568,"children":19569},{"style":1058},[19570],{"type":418,"value":3350},{"type":413,"tag":623,"props":19572,"children":19573},{"style":671},[19574],{"type":418,"value":1084},{"type":413,"tag":623,"props":19576,"children":19577},{"class":625,"line":1087},[19578,19582,19586,19590,19594,19598],{"type":413,"tag":623,"props":19579,"children":19580},{"style":1058},[19581],{"type":418,"value":18607},{"type":413,"tag":623,"props":19583,"children":19584},{"style":671},[19585],{"type":418,"value":1066},{"type":413,"tag":623,"props":19587,"children":19588},{"style":1058},[19589],{"type":418,"value":18212},{"type":413,"tag":623,"props":19591,"children":19592},{"style":671},[19593],{"type":418,"value":1404},{"type":413,"tag":623,"props":19595,"children":19596},{"style":1058},[19597],{"type":418,"value":3350},{"type":413,"tag":623,"props":19599,"children":19600},{"style":671},[19601],{"type":418,"value":1084},{"type":413,"tag":623,"props":19603,"children":19604},{"class":625,"line":1104},[19605,19610,19614,19618],{"type":413,"tag":623,"props":19606,"children":19607},{"style":1058},[19608],{"type":418,"value":19609},"    StartIpAddress ",{"type":413,"tag":623,"props":19611,"children":19612},{"style":671},[19613],{"type":418,"value":1066},{"type":413,"tag":623,"props":19615,"children":19616},{"style":1058},[19617],{"type":418,"value":19383},{"type":413,"tag":623,"props":19619,"children":19620},{"style":671},[19621],{"type":418,"value":1084},{"type":413,"tag":623,"props":19623,"children":19624},{"class":625,"line":1113},[19625,19630,19634],{"type":413,"tag":623,"props":19626,"children":19627},{"style":1058},[19628],{"type":418,"value":19629},"    EndIpAddress ",{"type":413,"tag":623,"props":19631,"children":19632},{"style":671},[19633],{"type":418,"value":1066},{"type":413,"tag":623,"props":19635,"children":19636},{"style":1058},[19637],{"type":418,"value":19638}," publicIp\n",{"type":413,"tag":623,"props":19640,"children":19641},{"class":625,"line":1161},[19642],{"type":413,"tag":623,"props":19643,"children":19644},{"style":671},[19645],{"type":418,"value":1352},{"type":413,"tag":600,"props":19647,"children":19649},{"id":19648},"create-the-azure-ad-group-that-will-be-given-access-to-the-database",[19650],{"type":418,"value":19651},"Create the Azure AD group that will be given access to the database",{"type":413,"tag":414,"props":19653,"children":19654},{},[19655],{"type":418,"value":19656},"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":413,"tag":612,"props":19658,"children":19660},{"className":981,"code":19659,"language":326,"meta":401,"style":401},"var sqlDatabaseAuthorizedGroup = new Group(\"SqlDbUsersGroup\", new GroupArgs\n{\n    DisplayName = \"SqlDbUsersGroup\",\n    SecurityEnabled = true,\n    Owners = new InputList\u003Cstring> { sqlAdAdmin.Id }\n});\n",[19661],{"type":413,"tag":619,"props":19662,"children":19663},{"__ignoreMap":401},[19664,19719,19726,19753,19773,19828],{"type":413,"tag":623,"props":19665,"children":19666},{"class":625,"line":626},[19667,19671,19676,19680,19684,19689,19693,19697,19702,19706,19710,19714],{"type":413,"tag":623,"props":19668,"children":19669},{"style":630},[19670],{"type":418,"value":994},{"type":413,"tag":623,"props":19672,"children":19673},{"style":630},[19674],{"type":418,"value":19675}," sqlDatabaseAuthorizedGroup",{"type":413,"tag":623,"props":19677,"children":19678},{"style":671},[19679],{"type":418,"value":1004},{"type":413,"tag":623,"props":19681,"children":19682},{"style":671},[19683],{"type":418,"value":638},{"type":413,"tag":623,"props":19685,"children":19686},{"style":630},[19687],{"type":418,"value":19688}," Group",{"type":413,"tag":623,"props":19690,"children":19691},{"style":671},[19692],{"type":418,"value":1018},{"type":413,"tag":623,"props":19694,"children":19695},{"style":671},[19696],{"type":418,"value":1023},{"type":413,"tag":623,"props":19698,"children":19699},{"style":635},[19700],{"type":418,"value":19701},"SqlDbUsersGroup",{"type":413,"tag":623,"props":19703,"children":19704},{"style":671},[19705],{"type":418,"value":1023},{"type":413,"tag":623,"props":19707,"children":19708},{"style":671},[19709],{"type":418,"value":1037},{"type":413,"tag":623,"props":19711,"children":19712},{"style":671},[19713],{"type":418,"value":638},{"type":413,"tag":623,"props":19715,"children":19716},{"style":630},[19717],{"type":418,"value":19718}," GroupArgs\n",{"type":413,"tag":623,"props":19720,"children":19721},{"class":625,"line":1045},[19722],{"type":413,"tag":623,"props":19723,"children":19724},{"style":671},[19725],{"type":418,"value":1051},{"type":413,"tag":623,"props":19727,"children":19728},{"class":625,"line":1054},[19729,19733,19737,19741,19745,19749],{"type":413,"tag":623,"props":19730,"children":19731},{"style":1058},[19732],{"type":418,"value":2668},{"type":413,"tag":623,"props":19734,"children":19735},{"style":671},[19736],{"type":418,"value":1066},{"type":413,"tag":623,"props":19738,"children":19739},{"style":671},[19740],{"type":418,"value":674},{"type":413,"tag":623,"props":19742,"children":19743},{"style":635},[19744],{"type":418,"value":19701},{"type":413,"tag":623,"props":19746,"children":19747},{"style":671},[19748],{"type":418,"value":1023},{"type":413,"tag":623,"props":19750,"children":19751},{"style":671},[19752],{"type":418,"value":1084},{"type":413,"tag":623,"props":19754,"children":19755},{"class":625,"line":1087},[19756,19761,19765,19769],{"type":413,"tag":623,"props":19757,"children":19758},{"style":1058},[19759],{"type":418,"value":19760},"    SecurityEnabled ",{"type":413,"tag":623,"props":19762,"children":19763},{"style":671},[19764],{"type":418,"value":1066},{"type":413,"tag":623,"props":19766,"children":19767},{"style":5580},[19768],{"type":418,"value":9472},{"type":413,"tag":623,"props":19770,"children":19771},{"style":671},[19772],{"type":418,"value":1084},{"type":413,"tag":623,"props":19774,"children":19775},{"class":625,"line":1104},[19776,19781,19785,19789,19794,19798,19802,19807,19811,19815,19819,19824],{"type":413,"tag":623,"props":19777,"children":19778},{"style":1058},[19779],{"type":418,"value":19780},"    Owners ",{"type":413,"tag":623,"props":19782,"children":19783},{"style":671},[19784],{"type":418,"value":1066},{"type":413,"tag":623,"props":19786,"children":19787},{"style":671},[19788],{"type":418,"value":638},{"type":413,"tag":623,"props":19790,"children":19791},{"style":630},[19792],{"type":418,"value":19793}," InputList",{"type":413,"tag":623,"props":19795,"children":19796},{"style":671},[19797],{"type":418,"value":4427},{"type":413,"tag":623,"props":19799,"children":19800},{"style":671},[19801],{"type":418,"value":4432},{"type":413,"tag":623,"props":19803,"children":19804},{"style":671},[19805],{"type":418,"value":19806},">",{"type":413,"tag":623,"props":19808,"children":19809},{"style":671},[19810],{"type":418,"value":5852},{"type":413,"tag":623,"props":19812,"children":19813},{"style":1058},[19814],{"type":418,"value":18031},{"type":413,"tag":623,"props":19816,"children":19817},{"style":671},[19818],{"type":418,"value":1404},{"type":413,"tag":623,"props":19820,"children":19821},{"style":1058},[19822],{"type":418,"value":19823},"Id ",{"type":413,"tag":623,"props":19825,"children":19826},{"style":671},[19827],{"type":418,"value":15641},{"type":413,"tag":623,"props":19829,"children":19830},{"class":625,"line":1113},[19831],{"type":413,"tag":623,"props":19832,"children":19833},{"style":671},[19834],{"type":418,"value":1352},{"type":413,"tag":414,"props":19836,"children":19837},{},[19838],{"type":418,"value":19839},"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":413,"tag":443,"props":19841,"children":19842},{},[19843,19848,19853],{"type":413,"tag":447,"props":19844,"children":19845},{},[19846],{"type":418,"value":19847},"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":413,"tag":447,"props":19849,"children":19850},{},[19851],{"type":418,"value":19852},"you don't lose granularity of access control (you can always create several groups with different permissions if you need to)",{"type":413,"tag":447,"props":19854,"children":19855},{},[19856],{"type":418,"value":19857},"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":413,"tag":600,"props":19859,"children":19861},{"id":19860},"assign-the-roles-to-the-azure-ad-group-using-the-command-provider",[19862],{"type":418,"value":19863},"Assign the roles to the Azure AD group using the Command provider",{"type":413,"tag":414,"props":19865,"children":19866},{},[19867,19869,19874,19876,19881],{"type":418,"value":19868},"As we already talked about, we can specify a script to run on the ",{"type":413,"tag":619,"props":19870,"children":19872},{"className":19871},[],[19873],{"type":418,"value":19258},{"type":418,"value":19875}," operation and another on the ",{"type":413,"tag":619,"props":19877,"children":19879},{"className":19878},[],[19880],{"type":418,"value":19287},{"type":418,"value":19882}," 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":413,"tag":612,"props":19884,"children":19886},{"className":18718,"code":19885,"language":18720,"meta":401,"style":401},"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",[19887],{"type":413,"tag":619,"props":19888,"children":19889},{"__ignoreMap":401},[19890,19918,19942,19965],{"type":413,"tag":623,"props":19891,"children":19892},{"class":625,"line":626},[19893,19897,19902,19906,19910,19914],{"type":413,"tag":623,"props":19894,"children":19895},{"style":3551},[19896],{"type":418,"value":18732},{"type":413,"tag":623,"props":19898,"children":19899},{"style":1058},[19900],{"type":418,"value":19901}," USER {sqlDatabaseAuthorizedGroup.DisplayName} ",{"type":413,"tag":623,"props":19903,"children":19904},{"style":3551},[19905],{"type":418,"value":18742},{"type":413,"tag":623,"props":19907,"children":19908},{"style":3551},[19909],{"type":418,"value":18747},{"type":413,"tag":623,"props":19911,"children":19912},{"style":3551},[19913],{"type":418,"value":18752},{"type":413,"tag":623,"props":19915,"children":19916},{"style":1058},[19917],{"type":418,"value":2524},{"type":413,"tag":623,"props":19919,"children":19920},{"class":625,"line":1045},[19921,19925,19929,19933,19937],{"type":413,"tag":623,"props":19922,"children":19923},{"style":3551},[19924],{"type":418,"value":18764},{"type":413,"tag":623,"props":19926,"children":19927},{"style":3551},[19928],{"type":418,"value":18769},{"type":413,"tag":623,"props":19930,"children":19931},{"style":1058},[19932],{"type":418,"value":18774},{"type":413,"tag":623,"props":19934,"children":19935},{"style":3551},[19936],{"type":418,"value":18779},{"type":413,"tag":623,"props":19938,"children":19939},{"style":1058},[19940],{"type":418,"value":19941}," MEMBER {sqlDatabaseAuthorizedGroup.DisplayName};\n",{"type":413,"tag":623,"props":19943,"children":19944},{"class":625,"line":1054},[19945,19949,19953,19957,19961],{"type":413,"tag":623,"props":19946,"children":19947},{"style":3551},[19948],{"type":418,"value":18764},{"type":413,"tag":623,"props":19950,"children":19951},{"style":3551},[19952],{"type":418,"value":18769},{"type":413,"tag":623,"props":19954,"children":19955},{"style":1058},[19956],{"type":418,"value":18800},{"type":413,"tag":623,"props":19958,"children":19959},{"style":3551},[19960],{"type":418,"value":18779},{"type":413,"tag":623,"props":19962,"children":19963},{"style":1058},[19964],{"type":418,"value":19941},{"type":413,"tag":623,"props":19966,"children":19967},{"class":625,"line":1087},[19968],{"type":413,"tag":623,"props":19969,"children":19970},{"style":3551},[19971],{"type":418,"value":18816},{"type":413,"tag":414,"props":19973,"children":19974},{},[19975,19976,19981],{"type":418,"value":6304},{"type":413,"tag":619,"props":19977,"children":19979},{"className":19978},[],[19980],{"type":418,"value":18870},{"type":418,"value":19982}," utility can be used like this to send a command on the database:",{"type":413,"tag":612,"props":19984,"children":19986},{"className":5099,"code":19985,"language":248,"meta":401,"style":401},"sqlcmd -S {sqlServer.Name}.database.windows.net -d {database.Name} -U {sqlAdAdmin.UserPrincipalName} -P {sqlAdAdmin.Password} -G -l 30 -Q '___SQL Command___'\n",[19987],{"type":413,"tag":619,"props":19988,"children":19989},{"__ignoreMap":401},[19990],{"type":413,"tag":623,"props":19991,"children":19992},{"class":625,"line":626},[19993,19998,20002,20007,20011,20016,20020,20025,20029,20033,20037,20042,20046,20051,20056,20060,20065,20069,20073,20078,20082,20087,20091,20095,20100,20104,20109,20114,20118,20123,20128,20133],{"type":413,"tag":623,"props":19994,"children":19995},{"style":1058},[19996],{"type":418,"value":19997},"sqlcmd ",{"type":413,"tag":623,"props":19999,"children":20000},{"style":671},[20001],{"type":418,"value":3503},{"type":413,"tag":623,"props":20003,"children":20004},{"style":1058},[20005],{"type":418,"value":20006},"S ",{"type":413,"tag":623,"props":20008,"children":20009},{"style":671},[20010],{"type":418,"value":2456},{"type":413,"tag":623,"props":20012,"children":20013},{"style":1058},[20014],{"type":418,"value":20015},"sqlServer.Name",{"type":413,"tag":623,"props":20017,"children":20018},{"style":671},[20019],{"type":418,"value":3327},{"type":413,"tag":623,"props":20021,"children":20022},{"style":1058},[20023],{"type":418,"value":20024},".database.windows.net ",{"type":413,"tag":623,"props":20026,"children":20027},{"style":671},[20028],{"type":418,"value":3503},{"type":413,"tag":623,"props":20030,"children":20031},{"style":1058},[20032],{"type":418,"value":5139},{"type":413,"tag":623,"props":20034,"children":20035},{"style":671},[20036],{"type":418,"value":2456},{"type":413,"tag":623,"props":20038,"children":20039},{"style":1058},[20040],{"type":418,"value":20041},"database.Name",{"type":413,"tag":623,"props":20043,"children":20044},{"style":671},[20045],{"type":418,"value":3327},{"type":413,"tag":623,"props":20047,"children":20048},{"style":671},[20049],{"type":418,"value":20050}," -",{"type":413,"tag":623,"props":20052,"children":20053},{"style":1058},[20054],{"type":418,"value":20055},"U ",{"type":413,"tag":623,"props":20057,"children":20058},{"style":671},[20059],{"type":418,"value":2456},{"type":413,"tag":623,"props":20061,"children":20062},{"style":1058},[20063],{"type":418,"value":20064},"sqlAdAdmin.UserPrincipalName",{"type":413,"tag":623,"props":20066,"children":20067},{"style":671},[20068],{"type":418,"value":3327},{"type":413,"tag":623,"props":20070,"children":20071},{"style":671},[20072],{"type":418,"value":20050},{"type":413,"tag":623,"props":20074,"children":20075},{"style":1058},[20076],{"type":418,"value":20077},"P ",{"type":413,"tag":623,"props":20079,"children":20080},{"style":671},[20081],{"type":418,"value":2456},{"type":413,"tag":623,"props":20083,"children":20084},{"style":1058},[20085],{"type":418,"value":20086},"sqlAdAdmin.Password",{"type":413,"tag":623,"props":20088,"children":20089},{"style":671},[20090],{"type":418,"value":3327},{"type":413,"tag":623,"props":20092,"children":20093},{"style":671},[20094],{"type":418,"value":20050},{"type":413,"tag":623,"props":20096,"children":20097},{"style":1058},[20098],{"type":418,"value":20099},"G ",{"type":413,"tag":623,"props":20101,"children":20102},{"style":671},[20103],{"type":418,"value":3503},{"type":413,"tag":623,"props":20105,"children":20106},{"style":1058},[20107],{"type":418,"value":20108},"l ",{"type":413,"tag":623,"props":20110,"children":20111},{"style":3551},[20112],{"type":418,"value":20113},"30",{"type":413,"tag":623,"props":20115,"children":20116},{"style":671},[20117],{"type":418,"value":20050},{"type":413,"tag":623,"props":20119,"children":20120},{"style":1058},[20121],{"type":418,"value":20122},"Q ",{"type":413,"tag":623,"props":20124,"children":20125},{"style":671},[20126],{"type":418,"value":20127},"'",{"type":413,"tag":623,"props":20129,"children":20130},{"style":635},[20131],{"type":418,"value":20132},"___SQL Command___",{"type":413,"tag":623,"props":20134,"children":20135},{"style":671},[20136],{"type":418,"value":3592},{"type":413,"tag":414,"props":20138,"children":20139},{},[20140,20142,20148,20150,20155],{"type":418,"value":20141},"You can check the ",{"type":413,"tag":432,"props":20143,"children":20146},{"href":20144,"rel":20145},"https://docs.microsoft.com/en-us/sql/tools/sqlcmd-utility?view=sql-server-ver15#sqlcmd-commands",[436],[20147],{"type":418,"value":4994},{"type":418,"value":20149}," to learn more about how to use ",{"type":413,"tag":619,"props":20151,"children":20153},{"className":20152},[],[20154],{"type":418,"value":18870},{"type":418,"value":20156}," 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":413,"tag":414,"props":20158,"children":20159},{},[20160],{"type":418,"value":20161},"If we use all that with our Command provider, we get the following C# code.",{"type":413,"tag":612,"props":20163,"children":20165},{"className":981,"code":20164,"language":326,"meta":401,"style":401},"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",[20166],{"type":413,"tag":619,"props":20167,"children":20168},{"__ignoreMap":401},[20169,20224,20231,20454,20487,20494,20515,20531,20538],{"type":413,"tag":623,"props":20170,"children":20171},{"class":625,"line":626},[20172,20176,20181,20185,20189,20194,20198,20202,20207,20211,20215,20219],{"type":413,"tag":623,"props":20173,"children":20174},{"style":630},[20175],{"type":418,"value":994},{"type":413,"tag":623,"props":20177,"children":20178},{"style":630},[20179],{"type":418,"value":20180}," authorizeAdGroup",{"type":413,"tag":623,"props":20182,"children":20183},{"style":671},[20184],{"type":418,"value":1004},{"type":413,"tag":623,"props":20186,"children":20187},{"style":671},[20188],{"type":418,"value":638},{"type":413,"tag":623,"props":20190,"children":20191},{"style":630},[20192],{"type":418,"value":20193}," Command",{"type":413,"tag":623,"props":20195,"children":20196},{"style":671},[20197],{"type":418,"value":1018},{"type":413,"tag":623,"props":20199,"children":20200},{"style":671},[20201],{"type":418,"value":1023},{"type":413,"tag":623,"props":20203,"children":20204},{"style":635},[20205],{"type":418,"value":20206},"AuthorizeAdGroup",{"type":413,"tag":623,"props":20208,"children":20209},{"style":671},[20210],{"type":418,"value":1023},{"type":413,"tag":623,"props":20212,"children":20213},{"style":671},[20214],{"type":418,"value":1037},{"type":413,"tag":623,"props":20216,"children":20217},{"style":671},[20218],{"type":418,"value":638},{"type":413,"tag":623,"props":20220,"children":20221},{"style":630},[20222],{"type":418,"value":20223}," CommandArgs\n",{"type":413,"tag":623,"props":20225,"children":20226},{"class":625,"line":1045},[20227],{"type":413,"tag":623,"props":20228,"children":20229},{"style":671},[20230],{"type":418,"value":1051},{"type":413,"tag":623,"props":20232,"children":20233},{"class":625,"line":1054},[20234,20239,20243,20247,20251,20255,20259,20263,20268,20272,20277,20281,20285,20289,20294,20298,20302,20306,20310,20314,20319,20323,20327,20331,20335,20339,20344,20348,20352,20356,20361,20365,20370,20374,20379,20383,20387,20391,20396,20400,20404,20408,20412,20416,20421,20425,20429,20433,20437,20441,20446,20450],{"type":413,"tag":623,"props":20235,"children":20236},{"style":1058},[20237],{"type":418,"value":20238},"    Create ",{"type":413,"tag":623,"props":20240,"children":20241},{"style":671},[20242],{"type":418,"value":1066},{"type":413,"tag":623,"props":20244,"children":20245},{"style":1058},[20246],{"type":418,"value":3003},{"type":413,"tag":623,"props":20248,"children":20249},{"style":671},[20250],{"type":418,"value":1404},{"type":413,"tag":623,"props":20252,"children":20253},{"style":1407},[20254],{"type":418,"value":3012},{"type":413,"tag":623,"props":20256,"children":20257},{"style":671},[20258],{"type":418,"value":1018},{"type":413,"tag":623,"props":20260,"children":20261},{"style":671},[20262],{"type":418,"value":2446},{"type":413,"tag":623,"props":20264,"children":20265},{"style":635},[20266],{"type":418,"value":20267},"sqlcmd -S ",{"type":413,"tag":623,"props":20269,"children":20270},{"style":671},[20271],{"type":418,"value":2456},{"type":413,"tag":623,"props":20273,"children":20274},{"style":1058},[20275],{"type":418,"value":20276},"sqlServer",{"type":413,"tag":623,"props":20278,"children":20279},{"style":671},[20280],{"type":418,"value":1404},{"type":413,"tag":623,"props":20282,"children":20283},{"style":1058},[20284],{"type":418,"value":3350},{"type":413,"tag":623,"props":20286,"children":20287},{"style":671},[20288],{"type":418,"value":3327},{"type":413,"tag":623,"props":20290,"children":20291},{"style":635},[20292],{"type":418,"value":20293},".database.windows.net -d ",{"type":413,"tag":623,"props":20295,"children":20296},{"style":671},[20297],{"type":418,"value":2456},{"type":413,"tag":623,"props":20299,"children":20300},{"style":1058},[20301],{"type":418,"value":19038},{"type":413,"tag":623,"props":20303,"children":20304},{"style":671},[20305],{"type":418,"value":1404},{"type":413,"tag":623,"props":20307,"children":20308},{"style":1058},[20309],{"type":418,"value":3350},{"type":413,"tag":623,"props":20311,"children":20312},{"style":671},[20313],{"type":418,"value":3327},{"type":413,"tag":623,"props":20315,"children":20316},{"style":635},[20317],{"type":418,"value":20318}," -U ",{"type":413,"tag":623,"props":20320,"children":20321},{"style":671},[20322],{"type":418,"value":2456},{"type":413,"tag":623,"props":20324,"children":20325},{"style":1058},[20326],{"type":418,"value":17954},{"type":413,"tag":623,"props":20328,"children":20329},{"style":671},[20330],{"type":418,"value":1404},{"type":413,"tag":623,"props":20332,"children":20333},{"style":1058},[20334],{"type":418,"value":18366},{"type":413,"tag":623,"props":20336,"children":20337},{"style":671},[20338],{"type":418,"value":3327},{"type":413,"tag":623,"props":20340,"children":20341},{"style":635},[20342],{"type":418,"value":20343}," -P ",{"type":413,"tag":623,"props":20345,"children":20346},{"style":671},[20347],{"type":418,"value":2456},{"type":413,"tag":623,"props":20349,"children":20350},{"style":1058},[20351],{"type":418,"value":17954},{"type":413,"tag":623,"props":20353,"children":20354},{"style":671},[20355],{"type":418,"value":1404},{"type":413,"tag":623,"props":20357,"children":20358},{"style":1058},[20359],{"type":418,"value":20360},"Password",{"type":413,"tag":623,"props":20362,"children":20363},{"style":671},[20364],{"type":418,"value":3327},{"type":413,"tag":623,"props":20366,"children":20367},{"style":635},[20368],{"type":418,"value":20369}," -G -l 30 -Q 'CREATE USER ",{"type":413,"tag":623,"props":20371,"children":20372},{"style":671},[20373],{"type":418,"value":2456},{"type":413,"tag":623,"props":20375,"children":20376},{"style":1058},[20377],{"type":418,"value":20378},"sqlDatabaseAuthorizedGroup",{"type":413,"tag":623,"props":20380,"children":20381},{"style":671},[20382],{"type":418,"value":1404},{"type":413,"tag":623,"props":20384,"children":20385},{"style":1058},[20386],{"type":418,"value":2519},{"type":413,"tag":623,"props":20388,"children":20389},{"style":671},[20390],{"type":418,"value":3327},{"type":413,"tag":623,"props":20392,"children":20393},{"style":635},[20394],{"type":418,"value":20395}," FROM EXTERNAL PROVIDER; ALTER ROLE db_datareader ADD MEMBER ",{"type":413,"tag":623,"props":20397,"children":20398},{"style":671},[20399],{"type":418,"value":2456},{"type":413,"tag":623,"props":20401,"children":20402},{"style":1058},[20403],{"type":418,"value":20378},{"type":413,"tag":623,"props":20405,"children":20406},{"style":671},[20407],{"type":418,"value":1404},{"type":413,"tag":623,"props":20409,"children":20410},{"style":1058},[20411],{"type":418,"value":2519},{"type":413,"tag":623,"props":20413,"children":20414},{"style":671},[20415],{"type":418,"value":3327},{"type":413,"tag":623,"props":20417,"children":20418},{"style":635},[20419],{"type":418,"value":20420},"; ALTER ROLE db_datawriter ADD MEMBER ",{"type":413,"tag":623,"props":20422,"children":20423},{"style":671},[20424],{"type":418,"value":2456},{"type":413,"tag":623,"props":20426,"children":20427},{"style":1058},[20428],{"type":418,"value":20378},{"type":413,"tag":623,"props":20430,"children":20431},{"style":671},[20432],{"type":418,"value":1404},{"type":413,"tag":623,"props":20434,"children":20435},{"style":1058},[20436],{"type":418,"value":2519},{"type":413,"tag":623,"props":20438,"children":20439},{"style":671},[20440],{"type":418,"value":3327},{"type":413,"tag":623,"props":20442,"children":20443},{"style":635},[20444],{"type":418,"value":20445},";'",{"type":413,"tag":623,"props":20447,"children":20448},{"style":671},[20449],{"type":418,"value":1023},{"type":413,"tag":623,"props":20451,"children":20452},{"style":671},[20453],{"type":418,"value":3820},{"type":413,"tag":623,"props":20455,"children":20456},{"class":625,"line":1087},[20457,20462,20466,20470,20474,20478,20482],{"type":413,"tag":623,"props":20458,"children":20459},{"style":1058},[20460],{"type":418,"value":20461},"    Interpreter ",{"type":413,"tag":623,"props":20463,"children":20464},{"style":671},[20465],{"type":418,"value":1066},{"type":413,"tag":623,"props":20467,"children":20468},{"style":671},[20469],{"type":418,"value":638},{"type":413,"tag":623,"props":20471,"children":20472},{"style":630},[20473],{"type":418,"value":19793},{"type":413,"tag":623,"props":20475,"children":20476},{"style":671},[20477],{"type":418,"value":4427},{"type":413,"tag":623,"props":20479,"children":20480},{"style":671},[20481],{"type":418,"value":4432},{"type":413,"tag":623,"props":20483,"children":20484},{"style":671},[20485],{"type":418,"value":20486},">\n",{"type":413,"tag":623,"props":20488,"children":20489},{"class":625,"line":1104},[20490],{"type":413,"tag":623,"props":20491,"children":20492},{"style":671},[20493],{"type":418,"value":1110},{"type":413,"tag":623,"props":20495,"children":20496},{"class":625,"line":1113},[20497,20502,20507,20511],{"type":413,"tag":623,"props":20498,"children":20499},{"style":671},[20500],{"type":418,"value":20501},"        \"",{"type":413,"tag":623,"props":20503,"children":20504},{"style":635},[20505],{"type":418,"value":20506},"pwsh",{"type":413,"tag":623,"props":20508,"children":20509},{"style":671},[20510],{"type":418,"value":1023},{"type":413,"tag":623,"props":20512,"children":20513},{"style":671},[20514],{"type":418,"value":1084},{"type":413,"tag":623,"props":20516,"children":20517},{"class":625,"line":1161},[20518,20522,20527],{"type":413,"tag":623,"props":20519,"children":20520},{"style":671},[20521],{"type":418,"value":20501},{"type":413,"tag":623,"props":20523,"children":20524},{"style":635},[20525],{"type":418,"value":20526},"-c",{"type":413,"tag":623,"props":20528,"children":20529},{"style":671},[20530],{"type":418,"value":684},{"type":413,"tag":623,"props":20532,"children":20533},{"class":625,"line":1207},[20534],{"type":413,"tag":623,"props":20535,"children":20536},{"style":671},[20537],{"type":418,"value":2097},{"type":413,"tag":623,"props":20539,"children":20540},{"class":625,"line":1251},[20541],{"type":413,"tag":623,"props":20542,"children":20543},{"style":671},[20544],{"type":418,"value":1352},{"type":413,"tag":414,"props":20546,"children":20547},{},[20548],{"type":418,"value":20549},"As you can see, we can specify a specific interpreter to use (PowerShell here).",{"type":413,"tag":496,"props":20551,"children":20552},{"icon":557},[20553],{"type":413,"tag":414,"props":20554,"children":20555},{},[20556,20558,20565,20567,20573,20575,20581],{"type":418,"value":20557},"Don't do like me and forget that our variables are ",{"type":413,"tag":432,"props":20559,"children":20562},{"href":20560,"rel":20561},"https://www.pulumi.com/docs/intro/concepts/inputs-outputs/#inputs-and-outputs",[436],[20563],{"type":418,"value":20564},"outputs",{"type":418,"value":20566}," (only fully known when the infrastructure resource is completely provisioned). Because of that it is necessary to use the ",{"type":413,"tag":619,"props":20568,"children":20570},{"className":20569},[],[20571],{"type":418,"value":20572},"Output.Format",{"type":418,"value":20574}," method for string interpolation instead of using the C# operator ",{"type":413,"tag":619,"props":20576,"children":20578},{"className":20577},[],[20579],{"type":418,"value":20580},"$",{"type":418,"value":20582},". 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":413,"tag":600,"props":20584,"children":20586},{"id":20585},"results",[20587],{"type":418,"value":20588},"Results",{"type":413,"tag":414,"props":20590,"children":20591},{},[20592,20594,20599,20600,20605],{"type":418,"value":20593},"And that's it! We now have created the Azure AD group as an external user in the database and assigned it the ",{"type":413,"tag":619,"props":20595,"children":20597},{"className":20596},[],[20598],{"type":418,"value":18827},{"type":418,"value":1778},{"type":413,"tag":619,"props":20601,"children":20603},{"className":20602},[],[20604],{"type":418,"value":18834},{"type":418,"value":20606}," roles.\nHere is what it looks like in Azure Data Studio:",{"type":413,"tag":414,"props":20608,"children":20609},{},[20610],{"type":413,"tag":487,"props":20611,"children":20615},{"alt":20612,"className":20613,"src":20614},"SQL query listing database members and roles in Azure Data Studio.",[491,492],"/posts/images/sqldatabase_ad_azuredatastudio.png",[],{"type":413,"tag":420,"props":20617,"children":20618},{"id":16373},[20619],{"type":418,"value":16376},{"type":413,"tag":414,"props":20621,"children":20622},{},[20623,20625,20631],{"type":418,"value":20624},"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":413,"tag":432,"props":20626,"children":20629},{"href":20627,"rel":20628},"https://github.com/TechWatching/SqlDatabaseWithAzureAd",[436],[20630],{"type":418,"value":18934},{"type":418,"value":1404},{"type":413,"tag":414,"props":20633,"children":20634},{},[20635],{"type":418,"value":20636},"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":413,"tag":414,"props":20638,"children":20639},{},[20640],{"type":418,"value":20641},"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":413,"tag":414,"props":20643,"children":20644},{},[20645],{"type":418,"value":20646},"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":413,"tag":4673,"props":20648,"children":20649},{},[20650],{"type":418,"value":4677},{"title":401,"searchDepth":1045,"depth":1045,"links":20652},[20653,20654,20655,20656,20662,20668],{"id":17815,"depth":1045,"text":17818},{"id":17869,"depth":1045,"text":17872},{"id":18185,"depth":1045,"text":18188},{"id":18694,"depth":1045,"text":18697,"children":20657},[20658,20659,20660],{"id":18880,"depth":1054,"text":18883},{"id":18975,"depth":1054,"text":18978},{"id":19183,"depth":1054,"text":20661},"Command provider with the sqlcmd utility",{"id":19323,"depth":1045,"text":19326,"children":20663},[20664,20665,20666,20667],{"id":19348,"depth":1054,"text":19351},{"id":19648,"depth":1054,"text":19651},{"id":19860,"depth":1054,"text":19863},{"id":20585,"depth":1054,"text":20588},{"id":16373,"depth":1045,"text":16376},"content:1.posts:35.sqldatabase-active-directory-authent.md","1.posts/35.sqldatabase-active-directory-authent.md",{"_path":100,"_dir":399,"_draft":400,"_partial":400,"_locale":401,"title":99,"description":20672,"lead":20673,"date":20674,"badge":20675,"image":20676,"tags":20678,"body":20679,"_type":4691,"_id":20955,"_source":4693,"_file":20956,"_extension":4695},"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":266},{"src":20677},"/images/cloud-lighthouse_1.jpg",[312,315],{"type":410,"children":20680,"toc":20950},[20681,20685,20691,20696,20714,20744,20778,20816,20829,20835,20847,20870,20883,20889,20904,20916,20925,20937],{"type":413,"tag":414,"props":20682,"children":20683},{},[20684],{"type":418,"value":20672},{"type":413,"tag":420,"props":20686,"children":20688},{"id":20687},"developing-and-deploying-cloud-infrastructure",[20689],{"type":418,"value":20690},"Developing and deploying Cloud Infrastructure",{"type":413,"tag":414,"props":20692,"children":20693},{},[20694],{"type":418,"value":20695},"When doing Infrastructure as Code for a cloud application we usually do the following steps:",{"type":413,"tag":6988,"props":20697,"children":20698},{},[20699,20704,20709],{"type":413,"tag":447,"props":20700,"children":20701},{},[20702],{"type":418,"value":20703},"we write the code describing the desired state of the infrastructure",{"type":413,"tag":447,"props":20705,"children":20706},{},[20707],{"type":418,"value":20708},"we build this infrastructure code and compare the resulting desired state against the current state of the infrastructure",{"type":413,"tag":447,"props":20710,"children":20711},{},[20712],{"type":418,"value":20713},"we deploy to the provisioned infrastructure the changes needed to achieve the desired state",{"type":413,"tag":414,"props":20715,"children":20716},{},[20717,20719,20730,20732,20742],{"type":418,"value":20718},"When using Pulumi, you can run the ",{"type":413,"tag":432,"props":20720,"children":20723},{"href":20721,"rel":20722},"https://www.pulumi.com/docs/reference/cli/pulumi_preview/",[436],[20724],{"type":413,"tag":619,"props":20725,"children":20727},{"className":20726},[],[20728],{"type":418,"value":20729},"pulumi preview",{"type":418,"value":20731}," command for step 2 and the ",{"type":413,"tag":432,"props":20733,"children":20736},{"href":20734,"rel":20735},"https://www.pulumi.com/docs/reference/cli/pulumi_up/",[436],[20737],{"type":413,"tag":619,"props":20738,"children":20740},{"className":20739},[],[20741],{"type":418,"value":4573},{"type":418,"value":20743}," command for step 3.",{"type":413,"tag":414,"props":20745,"children":20746},{},[20747,20749,20754,20756,20762,20764,20769,20771,20776],{"type":418,"value":20748},"As its name suggests, the ",{"type":413,"tag":619,"props":20750,"children":20752},{"className":20751},[],[20753],{"type":418,"value":20729},{"type":418,"value":20755}," 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":413,"tag":619,"props":20757,"children":20759},{"className":20758},[],[20760],{"type":418,"value":20761},"pulumi update",{"type":418,"value":20763}," 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":413,"tag":619,"props":20765,"children":20767},{"className":20766},[],[20768],{"type":418,"value":20729},{"type":418,"value":20770},": most of the time I only use the ",{"type":413,"tag":619,"props":20772,"children":20774},{"className":20773},[],[20775],{"type":418,"value":4573},{"type":418,"value":20777}," command (which means I do steps 2 and 3 in one row).",{"type":413,"tag":496,"props":20779,"children":20780},{"icon":557},[20781],{"type":413,"tag":414,"props":20782,"children":20783},{},[20784,20786,20791,20793,20798,20800,20806,20808,20814],{"type":418,"value":20785},"In case you wonder, if you already have run ",{"type":413,"tag":619,"props":20787,"children":20789},{"className":20788},[],[20790],{"type":418,"value":20729},{"type":418,"value":20792}," before running ",{"type":413,"tag":619,"props":20794,"children":20796},{"className":20795},[],[20797],{"type":418,"value":4573},{"type":418,"value":20799}," you can skip the preview in the ",{"type":413,"tag":619,"props":20801,"children":20803},{"className":20802},[],[20804],{"type":418,"value":20805},"up",{"type":418,"value":20807}," command by using the ",{"type":413,"tag":619,"props":20809,"children":20811},{"className":20810},[],[20812],{"type":418,"value":20813},"--skip-preview",{"type":418,"value":20815}," option.",{"type":413,"tag":414,"props":20817,"children":20818},{},[20819,20821,20828],{"type":418,"value":20820},"Of course, you can use these commands to automate the deployment of your cloud infrastructure using your favorite ",{"type":413,"tag":432,"props":20822,"children":20825},{"href":20823,"rel":20824},"https://www.pulumi.com/docs/guides/continuous-delivery/",[436],[20826],{"type":418,"value":20827},"CI/CD system",{"type":418,"value":1404},{"type":413,"tag":420,"props":20830,"children":20832},{"id":20831},"the-need-for-a-hot-reload-like-experience-when-doing-iac",[20833],{"type":418,"value":20834},"The need for a hot reload-like experience when doing IaC",{"type":413,"tag":414,"props":20836,"children":20837},{},[20838,20840,20845],{"type":418,"value":20839},"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":413,"tag":619,"props":20841,"children":20843},{"className":20842},[],[20844],{"type":418,"value":4573},{"type":418,"value":20846}," command, and approve the deployment.",{"type":413,"tag":496,"props":20848,"children":20849},{"icon":557},[20850],{"type":413,"tag":414,"props":20851,"children":20852},{},[20853,20855,20861,20863,20868],{"type":418,"value":20854},"You can use the ",{"type":413,"tag":619,"props":20856,"children":20858},{"className":20857},[],[20859],{"type":418,"value":20860},"--yes",{"type":418,"value":20862}," option to automatically approve the changes and directly perform the update when running the ",{"type":413,"tag":619,"props":20864,"children":20866},{"className":20865},[],[20867],{"type":418,"value":4573},{"type":418,"value":20869}," command.",{"type":413,"tag":414,"props":20871,"children":20872},{},[20873,20875,20881],{"type":418,"value":20874},"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":413,"tag":619,"props":20876,"children":20878},{"className":20877},[],[20879],{"type":418,"value":20880},"pulumi watch",{"type":418,"value":20882}," command is here for.",{"type":413,"tag":420,"props":20884,"children":20886},{"id":20885},"using-pulumi-watch",[20887],{"type":418,"value":20888},"Using Pulumi Watch",{"type":413,"tag":414,"props":20890,"children":20891},{},[20892,20902],{"type":413,"tag":432,"props":20893,"children":20896},{"href":20894,"rel":20895},"https://www.pulumi.com/docs/reference/cli/pulumi_watch/",[436],[20897],{"type":413,"tag":619,"props":20898,"children":20900},{"className":20899},[],[20901],{"type":418,"value":20880},{"type":418,"value":20903}," is a command currently in preview that watches for changes in the infrastructure code directory and continuously updates the cloud resources.",{"type":413,"tag":414,"props":20905,"children":20906},{},[20907,20909,20914],{"type":418,"value":20908},"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":413,"tag":619,"props":20910,"children":20912},{"className":20911},[],[20913],{"type":418,"value":20880},{"type":418,"value":20915}," 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":413,"tag":414,"props":20917,"children":20918},{},[20919],{"type":413,"tag":487,"props":20920,"children":20924},{"alt":20921,"className":20922,"src":20923},"Pulumi watch in terminal.",[491,492],"/posts/images/pulumiwatch_terminal_1.gif",[],{"type":413,"tag":414,"props":20926,"children":20927},{},[20928,20930,20935],{"type":418,"value":20929},"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":413,"tag":619,"props":20931,"children":20933},{"className":20932},[],[20934],{"type":418,"value":20880},{"type":418,"value":20936}," all the time, but for quickly writing and testing your infrastructure code it can be very helpful.",{"type":413,"tag":414,"props":20938,"children":20939},{},[20940,20942,20948],{"type":418,"value":20941},"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":413,"tag":619,"props":20943,"children":20945},{"className":20944},[],[20946],{"type":418,"value":20947},"watch",{"type":418,"value":20949}," command you don't want to do without it.",{"title":401,"searchDepth":1045,"depth":1045,"links":20951},[20952,20953,20954],{"id":20687,"depth":1045,"text":20690},{"id":20831,"depth":1045,"text":20834},{"id":20885,"depth":1045,"text":20888},"content:1.posts:31.pulumi-watch.md","1.posts/31.pulumi-watch.md",{"_path":97,"_dir":399,"_draft":400,"_partial":400,"_locale":401,"title":96,"description":20958,"lead":20959,"date":20960,"badge":20961,"image":20962,"tags":20964,"body":20965,"_type":4691,"_id":23512,"_source":4693,"_file":23513,"_extension":4695},"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":266},{"src":20963},"/images/cloud-crave_1.jpg",[312,315,225,318,260,228],{"type":410,"children":20966,"toc":23494},[20967,20971,20977,20983,20988,21006,21014,21019,21024,21047,21055,21061,21066,21071,21094,21099,21105,21111,21122,21156,21161,21166,21172,21194,21203,21217,21222,21367,21372,21474,21479,21556,21561,21682,21687,21723,21729,21734,21759,21779,21866,21871,21891,21900,21906,21912,21917,21922,21928,21933,21938,21965,21970,21976,21981,22117,22130,22135,22210,22223,22296,22301,22352,22357,22392,22397,22492,22498,22503,22508,22522,22544,22553,22558,22562,22567,23472,23477,23490],{"type":413,"tag":414,"props":20968,"children":20969},{},[20970],{"type":418,"value":20958},{"type":413,"tag":420,"props":20972,"children":20974},{"id":20973},"a-quick-reminder-about-states-and-backends",[20975],{"type":418,"value":20976},"A quick reminder about states and backends",{"type":413,"tag":600,"props":20978,"children":20980},{"id":20979},"what-is-this-state-we-need-to-store",[20981],{"type":418,"value":20982},"What is this state we need to store?",{"type":413,"tag":414,"props":20984,"children":20985},{},[20986],{"type":418,"value":20987},"Like other Infrastructure as Code platforms, Pulumi uses a declarative approach:",{"type":413,"tag":443,"props":20989,"children":20990},{},[20991,20996,21001],{"type":413,"tag":447,"props":20992,"children":20993},{},[20994],{"type":418,"value":20995},"we write code to describe the desired state of our infrastructure",{"type":413,"tag":447,"props":20997,"children":20998},{},[20999],{"type":418,"value":21000},"Pulumi engine compares this desired state with the current state of the infrastructure and determines what changes need to be made",{"type":413,"tag":447,"props":21002,"children":21003},{},[21004],{"type":418,"value":21005},"Pulumi deploys these changes and updates the current state of the provisioned infrastructure",{"type":413,"tag":496,"props":21007,"children":21008},{"icon":557},[21009],{"type":413,"tag":414,"props":21010,"children":21011},{},[21012],{"type":418,"value":21013},"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":413,"tag":414,"props":21015,"children":21016},{},[21017],{"type":418,"value":21018},"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":413,"tag":414,"props":21020,"children":21021},{},[21022],{"type":418,"value":21023},"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":413,"tag":414,"props":21025,"children":21026},{},[21027,21029,21036,21038,21045],{"type":418,"value":21028},"If you want more information about states and backend, Pulumi has a ",{"type":413,"tag":432,"props":21030,"children":21033},{"href":21031,"rel":21032},"https://www.pulumi.com/docs/intro/concepts/state/",[436],[21034],{"type":418,"value":21035},"documentation page",{"type":418,"value":21037}," about that and there is also ",{"type":413,"tag":432,"props":21039,"children":21042},{"href":21040,"rel":21041},"https://www.pulumi.com/docs/intro/concepts/how-pulumi-works/",[436],[21043],{"type":418,"value":21044},"a page",{"type":418,"value":21046}," about how Pulumi works.",{"type":413,"tag":414,"props":21048,"children":21049},{},[21050],{"type":413,"tag":487,"props":21051,"children":21054},{"alt":17130,"className":21052,"src":21053},[491,492],"/posts/images/pulumiazurebackend_schema_1.png",[],{"type":413,"tag":600,"props":21056,"children":21058},{"id":21057},"what-backends-can-we-use-to-manage-the-infrastructure",[21059],{"type":418,"value":21060},"What \"backends\" can we use to manage the infrastructure?",{"type":413,"tag":414,"props":21062,"children":21063},{},[21064],{"type":418,"value":21065},"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":413,"tag":414,"props":21067,"children":21068},{},[21069],{"type":418,"value":21070},"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":413,"tag":443,"props":21072,"children":21073},{},[21074,21079,21084,21089],{"type":413,"tag":447,"props":21075,"children":21076},{},[21077],{"type":418,"value":21078},"Local Filesystem",{"type":413,"tag":447,"props":21080,"children":21081},{},[21082],{"type":418,"value":21083},"AWS S3 (or compatible server)",{"type":413,"tag":447,"props":21085,"children":21086},{},[21087],{"type":418,"value":21088},"Google Cloud Storage",{"type":413,"tag":447,"props":21090,"children":21091},{},[21092],{"type":418,"value":21093},"Azure Blob Storage",{"type":413,"tag":414,"props":21095,"children":21096},{},[21097],{"type":418,"value":21098},"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":413,"tag":420,"props":21100,"children":21102},{"id":21101},"using-pulumi-with-the-azure-blob-storage-backend",[21103],{"type":418,"value":21104},"Using Pulumi with the Azure Blob Storage backend",{"type":413,"tag":600,"props":21106,"children":21108},{"id":21107},"what-do-we-need",[21109],{"type":418,"value":21110},"What do we need?",{"type":413,"tag":414,"props":21112,"children":21113},{},[21114,21120],{"type":413,"tag":432,"props":21115,"children":21118},{"href":21116,"rel":21117},"https://www.pulumi.com/docs/intro/concepts/state/#logging-into-the-azure-blob-storage-backend",[436],[21119],{"type":418,"value":17204},{"type":418,"value":21121}," on using Azure Blob Storage backend is short. It only says that we need to:",{"type":413,"tag":443,"props":21123,"children":21124},{},[21125,21130,21135],{"type":413,"tag":447,"props":21126,"children":21127},{},[21128],{"type":418,"value":21129},"set the AZURE_STORAGE_ACCOUNT environment variable to specify the Azure storage account to use",{"type":413,"tag":447,"props":21131,"children":21132},{},[21133],{"type":418,"value":21134},"set the AZURE_STORAGE_KEY or the AZURE_STORAGE_SAS_TOKEN environment variables to let Pulumi access the storage",{"type":413,"tag":447,"props":21136,"children":21137},{},[21138,21140,21146,21148,21154],{"type":418,"value":21139},"execute the following command ",{"type":413,"tag":619,"props":21141,"children":21143},{"className":21142},[],[21144],{"type":418,"value":21145},"pulumi login azblob://\u003Ccontainer-path>",{"type":418,"value":21147}," where ",{"type":413,"tag":619,"props":21149,"children":21151},{"className":21150},[],[21152],{"type":418,"value":21153},"container-path",{"type":418,"value":21155}," is the path to a blob container in the storage account",{"type":413,"tag":414,"props":21157,"children":21158},{},[21159],{"type":418,"value":21160},"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":413,"tag":414,"props":21162,"children":21163},{},[21164],{"type":418,"value":21165},"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":413,"tag":600,"props":21167,"children":21169},{"id":21168},"how-to-create-and-configure-the-azure-blob-storage-backend",[21170],{"type":418,"value":21171},"How to create and configure the Azure Blob Storage backend?",{"type":413,"tag":414,"props":21173,"children":21174},{},[21175,21177,21184,21186,21192],{"type":418,"value":21176},"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":413,"tag":432,"props":21178,"children":21181},{"href":21179,"rel":21180},"https://marketplace.visualstudio.com/items?itemName=ms-vscode.azurecli",[436],[21182],{"type":418,"value":21183},"Azure CLI Tools extension",{"type":418,"value":21185},": you can create ",{"type":413,"tag":619,"props":21187,"children":21189},{"className":21188},[],[21190],{"type":418,"value":21191},".azcli",{"type":418,"value":21193}," files with IntelliSense on them and run the commands you are writing in the integrated terminal (see screenshot below).",{"type":413,"tag":414,"props":21195,"children":21196},{},[21197],{"type":413,"tag":487,"props":21198,"children":21202},{"alt":21199,"className":21200,"src":21201},"Azure CLI script in vscode.",[491,492],"/posts/images/pulumiazurebackend_vscode_1.png",[],{"type":413,"tag":496,"props":21204,"children":21205},{"icon":557},[21206],{"type":413,"tag":414,"props":21207,"children":21208},{},[21209,21211,21216],{"type":418,"value":21210},"If you are not familiar with Azure CLI, you can check my article \"",{"type":413,"tag":432,"props":21212,"children":21214},{"href":5041,"rel":21213},[436],[21215],{"type":418,"value":15},{"type":418,"value":4726},{"type":413,"tag":414,"props":21218,"children":21219},{},[21220],{"type":418,"value":21221},"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":413,"tag":612,"props":21223,"children":21225},{"className":5099,"code":21224,"language":248,"meta":401,"style":401},"$random=Get-Random -Maximum 1000\n$location=\"West Europe\"\n$rgName=\"rg-iacstate-westeu-$random\"\n$saName=\"stiacstate$random\"\n",[21226],{"type":413,"tag":619,"props":21227,"children":21228},{"__ignoreMap":401},[21229,21264,21293,21330],{"type":413,"tag":623,"props":21230,"children":21231},{"class":625,"line":626},[21232,21236,21241,21245,21250,21254,21259],{"type":413,"tag":623,"props":21233,"children":21234},{"style":671},[21235],{"type":418,"value":20580},{"type":413,"tag":623,"props":21237,"children":21238},{"style":1058},[21239],{"type":418,"value":21240},"random",{"type":413,"tag":623,"props":21242,"children":21243},{"style":671},[21244],{"type":418,"value":1066},{"type":413,"tag":623,"props":21246,"children":21247},{"style":1407},[21248],{"type":418,"value":21249},"Get-Random",{"type":413,"tag":623,"props":21251,"children":21252},{"style":671},[21253],{"type":418,"value":20050},{"type":413,"tag":623,"props":21255,"children":21256},{"style":1058},[21257],{"type":418,"value":21258},"Maximum ",{"type":413,"tag":623,"props":21260,"children":21261},{"style":3551},[21262],{"type":418,"value":21263},"1000\n",{"type":413,"tag":623,"props":21265,"children":21266},{"class":625,"line":1045},[21267,21271,21276,21280,21284,21289],{"type":413,"tag":623,"props":21268,"children":21269},{"style":671},[21270],{"type":418,"value":20580},{"type":413,"tag":623,"props":21272,"children":21273},{"style":1058},[21274],{"type":418,"value":21275},"location",{"type":413,"tag":623,"props":21277,"children":21278},{"style":671},[21279],{"type":418,"value":1066},{"type":413,"tag":623,"props":21281,"children":21282},{"style":671},[21283],{"type":418,"value":1023},{"type":413,"tag":623,"props":21285,"children":21286},{"style":635},[21287],{"type":418,"value":21288},"West Europe",{"type":413,"tag":623,"props":21290,"children":21291},{"style":671},[21292],{"type":418,"value":684},{"type":413,"tag":623,"props":21294,"children":21295},{"class":625,"line":1054},[21296,21300,21305,21309,21313,21318,21322,21326],{"type":413,"tag":623,"props":21297,"children":21298},{"style":671},[21299],{"type":418,"value":20580},{"type":413,"tag":623,"props":21301,"children":21302},{"style":1058},[21303],{"type":418,"value":21304},"rgName",{"type":413,"tag":623,"props":21306,"children":21307},{"style":671},[21308],{"type":418,"value":1066},{"type":413,"tag":623,"props":21310,"children":21311},{"style":671},[21312],{"type":418,"value":1023},{"type":413,"tag":623,"props":21314,"children":21315},{"style":635},[21316],{"type":418,"value":21317},"rg-iacstate-westeu-",{"type":413,"tag":623,"props":21319,"children":21320},{"style":671},[21321],{"type":418,"value":20580},{"type":413,"tag":623,"props":21323,"children":21324},{"style":1058},[21325],{"type":418,"value":21240},{"type":413,"tag":623,"props":21327,"children":21328},{"style":671},[21329],{"type":418,"value":684},{"type":413,"tag":623,"props":21331,"children":21332},{"class":625,"line":1087},[21333,21337,21342,21346,21350,21355,21359,21363],{"type":413,"tag":623,"props":21334,"children":21335},{"style":671},[21336],{"type":418,"value":20580},{"type":413,"tag":623,"props":21338,"children":21339},{"style":1058},[21340],{"type":418,"value":21341},"saName",{"type":413,"tag":623,"props":21343,"children":21344},{"style":671},[21345],{"type":418,"value":1066},{"type":413,"tag":623,"props":21347,"children":21348},{"style":671},[21349],{"type":418,"value":1023},{"type":413,"tag":623,"props":21351,"children":21352},{"style":635},[21353],{"type":418,"value":21354},"stiacstate",{"type":413,"tag":623,"props":21356,"children":21357},{"style":671},[21358],{"type":418,"value":20580},{"type":413,"tag":623,"props":21360,"children":21361},{"style":1058},[21362],{"type":418,"value":21240},{"type":413,"tag":623,"props":21364,"children":21365},{"style":671},[21366],{"type":418,"value":684},{"type":413,"tag":414,"props":21368,"children":21369},{},[21370],{"type":418,"value":21371},"Then let's create our resource group and our storage account:",{"type":413,"tag":612,"props":21373,"children":21375},{"className":614,"code":21374,"language":616,"meta":401,"style":401},"az group create -n $rgName -l $location\naz storage account create -g $rgName -n $saName -l $location --sku Standard_LRS\n",[21376],{"type":413,"tag":619,"props":21377,"children":21378},{"__ignoreMap":401},[21379,21416],{"type":413,"tag":623,"props":21380,"children":21381},{"class":625,"line":626},[21382,21387,21392,21397,21401,21406,21411],{"type":413,"tag":623,"props":21383,"children":21384},{"style":630},[21385],{"type":418,"value":21386},"az",{"type":413,"tag":623,"props":21388,"children":21389},{"style":635},[21390],{"type":418,"value":21391}," group",{"type":413,"tag":623,"props":21393,"children":21394},{"style":635},[21395],{"type":418,"value":21396}," create",{"type":413,"tag":623,"props":21398,"children":21399},{"style":635},[21400],{"type":418,"value":648},{"type":413,"tag":623,"props":21402,"children":21403},{"style":1058},[21404],{"type":418,"value":21405}," $rgName ",{"type":413,"tag":623,"props":21407,"children":21408},{"style":635},[21409],{"type":418,"value":21410},"-l",{"type":413,"tag":623,"props":21412,"children":21413},{"style":1058},[21414],{"type":418,"value":21415}," $location\n",{"type":413,"tag":623,"props":21417,"children":21418},{"class":625,"line":1045},[21419,21423,21428,21433,21437,21442,21446,21450,21455,21459,21464,21469],{"type":413,"tag":623,"props":21420,"children":21421},{"style":630},[21422],{"type":418,"value":21386},{"type":413,"tag":623,"props":21424,"children":21425},{"style":635},[21426],{"type":418,"value":21427}," storage",{"type":413,"tag":623,"props":21429,"children":21430},{"style":635},[21431],{"type":418,"value":21432}," account",{"type":413,"tag":623,"props":21434,"children":21435},{"style":635},[21436],{"type":418,"value":21396},{"type":413,"tag":623,"props":21438,"children":21439},{"style":635},[21440],{"type":418,"value":21441}," -g",{"type":413,"tag":623,"props":21443,"children":21444},{"style":1058},[21445],{"type":418,"value":21405},{"type":413,"tag":623,"props":21447,"children":21448},{"style":635},[21449],{"type":418,"value":718},{"type":413,"tag":623,"props":21451,"children":21452},{"style":1058},[21453],{"type":418,"value":21454}," $saName ",{"type":413,"tag":623,"props":21456,"children":21457},{"style":635},[21458],{"type":418,"value":21410},{"type":413,"tag":623,"props":21460,"children":21461},{"style":1058},[21462],{"type":418,"value":21463}," $location ",{"type":413,"tag":623,"props":21465,"children":21466},{"style":635},[21467],{"type":418,"value":21468},"--sku",{"type":413,"tag":623,"props":21470,"children":21471},{"style":635},[21472],{"type":418,"value":21473}," Standard_LRS\n",{"type":413,"tag":414,"props":21475,"children":21476},{},[21477],{"type":418,"value":21478},"The key to access the storage account can be retrieved with the following command:",{"type":413,"tag":612,"props":21480,"children":21482},{"className":614,"code":21481,"language":616,"meta":401,"style":401},"az storage account keys list --account-name $saName -g $rgName -o tsv --query '[0].value'\n",[21483],{"type":413,"tag":619,"props":21484,"children":21485},{"__ignoreMap":401},[21486],{"type":413,"tag":623,"props":21487,"children":21488},{"class":625,"line":626},[21489,21493,21497,21501,21506,21511,21516,21520,21524,21528,21533,21538,21543,21547,21552],{"type":413,"tag":623,"props":21490,"children":21491},{"style":630},[21492],{"type":418,"value":21386},{"type":413,"tag":623,"props":21494,"children":21495},{"style":635},[21496],{"type":418,"value":21427},{"type":413,"tag":623,"props":21498,"children":21499},{"style":635},[21500],{"type":418,"value":21432},{"type":413,"tag":623,"props":21502,"children":21503},{"style":635},[21504],{"type":418,"value":21505}," keys",{"type":413,"tag":623,"props":21507,"children":21508},{"style":635},[21509],{"type":418,"value":21510}," list",{"type":413,"tag":623,"props":21512,"children":21513},{"style":635},[21514],{"type":418,"value":21515}," --account-name",{"type":413,"tag":623,"props":21517,"children":21518},{"style":1058},[21519],{"type":418,"value":21454},{"type":413,"tag":623,"props":21521,"children":21522},{"style":635},[21523],{"type":418,"value":5229},{"type":413,"tag":623,"props":21525,"children":21526},{"style":1058},[21527],{"type":418,"value":21405},{"type":413,"tag":623,"props":21529,"children":21530},{"style":635},[21531],{"type":418,"value":21532},"-o",{"type":413,"tag":623,"props":21534,"children":21535},{"style":635},[21536],{"type":418,"value":21537}," tsv",{"type":413,"tag":623,"props":21539,"children":21540},{"style":635},[21541],{"type":418,"value":21542}," --query",{"type":413,"tag":623,"props":21544,"children":21545},{"style":671},[21546],{"type":418,"value":3583},{"type":413,"tag":623,"props":21548,"children":21549},{"style":635},[21550],{"type":418,"value":21551},"[0].value",{"type":413,"tag":623,"props":21553,"children":21554},{"style":671},[21555],{"type":418,"value":3592},{"type":413,"tag":414,"props":21557,"children":21558},{},[21559],{"type":418,"value":21560},"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":413,"tag":612,"props":21562,"children":21564},{"className":5099,"code":21563,"language":248,"meta":401,"style":401},"$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",[21565],{"type":413,"tag":619,"props":21566,"children":21567},{"__ignoreMap":401},[21568,21660],{"type":413,"tag":623,"props":21569,"children":21570},{"class":625,"line":626},[21571,21575,21580,21585,21590,21594,21599,21603,21608,21612,21617,21621,21626,21630,21635,21639,21644,21648,21652,21656],{"type":413,"tag":623,"props":21572,"children":21573},{"style":671},[21574],{"type":418,"value":20580},{"type":413,"tag":623,"props":21576,"children":21577},{"style":1058},[21578],{"type":418,"value":21579},"env:AZURE_STORAGE_KEY",{"type":413,"tag":623,"props":21581,"children":21582},{"style":671},[21583],{"type":418,"value":21584},"=$(",{"type":413,"tag":623,"props":21586,"children":21587},{"style":1058},[21588],{"type":418,"value":21589},"az storage account keys list ",{"type":413,"tag":623,"props":21591,"children":21592},{"style":671},[21593],{"type":418,"value":3503},{"type":413,"tag":623,"props":21595,"children":21596},{"style":1058},[21597],{"type":418,"value":21598},"n ",{"type":413,"tag":623,"props":21600,"children":21601},{"style":671},[21602],{"type":418,"value":20580},{"type":413,"tag":623,"props":21604,"children":21605},{"style":1058},[21606],{"type":418,"value":21607},"saName ",{"type":413,"tag":623,"props":21609,"children":21610},{"style":671},[21611],{"type":418,"value":3503},{"type":413,"tag":623,"props":21613,"children":21614},{"style":1058},[21615],{"type":418,"value":21616},"g ",{"type":413,"tag":623,"props":21618,"children":21619},{"style":671},[21620],{"type":418,"value":20580},{"type":413,"tag":623,"props":21622,"children":21623},{"style":1058},[21624],{"type":418,"value":21625},"rgName ",{"type":413,"tag":623,"props":21627,"children":21628},{"style":671},[21629],{"type":418,"value":3503},{"type":413,"tag":623,"props":21631,"children":21632},{"style":1058},[21633],{"type":418,"value":21634},"o tsv ",{"type":413,"tag":623,"props":21636,"children":21637},{"style":671},[21638],{"type":418,"value":5362},{"type":413,"tag":623,"props":21640,"children":21641},{"style":1058},[21642],{"type":418,"value":21643},"query ",{"type":413,"tag":623,"props":21645,"children":21646},{"style":671},[21647],{"type":418,"value":20127},{"type":413,"tag":623,"props":21649,"children":21650},{"style":635},[21651],{"type":418,"value":21551},{"type":413,"tag":623,"props":21653,"children":21654},{"style":671},[21655],{"type":418,"value":20127},{"type":413,"tag":623,"props":21657,"children":21658},{"style":671},[21659],{"type":418,"value":3042},{"type":413,"tag":623,"props":21661,"children":21662},{"class":625,"line":1045},[21663,21667,21672,21677],{"type":413,"tag":623,"props":21664,"children":21665},{"style":671},[21666],{"type":418,"value":20580},{"type":413,"tag":623,"props":21668,"children":21669},{"style":1058},[21670],{"type":418,"value":21671},"env:AZURE_STORAGE_ACCOUNT",{"type":413,"tag":623,"props":21673,"children":21674},{"style":671},[21675],{"type":418,"value":21676},"=$",{"type":413,"tag":623,"props":21678,"children":21679},{"style":1058},[21680],{"type":418,"value":21681},"saName\n",{"type":413,"tag":414,"props":21683,"children":21684},{},[21685],{"type":418,"value":21686},"And finally, we can create the blob container that will contain the infrastructure state:",{"type":413,"tag":612,"props":21688,"children":21690},{"className":614,"code":21689,"language":616,"meta":401,"style":401},"az storage container create -n iacstate\n",[21691],{"type":413,"tag":619,"props":21692,"children":21693},{"__ignoreMap":401},[21694],{"type":413,"tag":623,"props":21695,"children":21696},{"class":625,"line":626},[21697,21701,21705,21710,21714,21718],{"type":413,"tag":623,"props":21698,"children":21699},{"style":630},[21700],{"type":418,"value":21386},{"type":413,"tag":623,"props":21702,"children":21703},{"style":635},[21704],{"type":418,"value":21427},{"type":413,"tag":623,"props":21706,"children":21707},{"style":635},[21708],{"type":418,"value":21709}," container",{"type":413,"tag":623,"props":21711,"children":21712},{"style":635},[21713],{"type":418,"value":21396},{"type":413,"tag":623,"props":21715,"children":21716},{"style":635},[21717],{"type":418,"value":648},{"type":413,"tag":623,"props":21719,"children":21720},{"style":635},[21721],{"type":418,"value":21722}," iacstate\n",{"type":413,"tag":600,"props":21724,"children":21726},{"id":21725},"how-to-provision-your-project-infrastructure-using-the-azure-blob-storage-backend",[21727],{"type":418,"value":21728},"How to provision your project infrastructure using the Azure Blob Storage backend?",{"type":413,"tag":414,"props":21730,"children":21731},{},[21732],{"type":418,"value":21733},"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":413,"tag":612,"props":21735,"children":21737},{"className":5099,"code":21736,"language":248,"meta":401,"style":401},"pulumi login azblob://iacstate\n",[21738],{"type":413,"tag":619,"props":21739,"children":21740},{"__ignoreMap":401},[21741],{"type":413,"tag":623,"props":21742,"children":21743},{"class":625,"line":626},[21744,21749,21754],{"type":413,"tag":623,"props":21745,"children":21746},{"style":1058},[21747],{"type":418,"value":21748},"pulumi login azblob:",{"type":413,"tag":623,"props":21750,"children":21751},{"style":671},[21752],{"type":418,"value":21753},"//",{"type":413,"tag":623,"props":21755,"children":21756},{"style":1058},[21757],{"type":418,"value":21758},"iacstate\n",{"type":413,"tag":414,"props":21760,"children":21761},{},[21762,21764,21770,21772,21777],{"type":418,"value":21763},"To verify Pulumi can correctly provision cloud resources using our Azure Blob Storage backend, we can create a new Pulumi project using the ",{"type":413,"tag":619,"props":21765,"children":21767},{"className":21766},[],[21768],{"type":418,"value":21769},"azure-csharp",{"type":418,"value":21771}," template and deploy the infrastructure with the ",{"type":413,"tag":619,"props":21773,"children":21775},{"className":21774},[],[21776],{"type":418,"value":4573},{"type":418,"value":21778}," command:",{"type":413,"tag":612,"props":21780,"children":21782},{"className":5099,"code":21781,"language":248,"meta":401,"style":401},"mkdir infra;cd infra;\npulumi new azure-csharp -n AzureStorageBackend -s dev -y\npulumi up -y\n",[21783],{"type":413,"tag":619,"props":21784,"children":21785},{"__ignoreMap":401},[21786,21807,21850],{"type":413,"tag":623,"props":21787,"children":21788},{"class":625,"line":626},[21789,21794,21798,21803],{"type":413,"tag":623,"props":21790,"children":21791},{"style":1058},[21792],{"type":418,"value":21793},"mkdir infra",{"type":413,"tag":623,"props":21795,"children":21796},{"style":671},[21797],{"type":418,"value":9626},{"type":413,"tag":623,"props":21799,"children":21800},{"style":1058},[21801],{"type":418,"value":21802},"cd infra",{"type":413,"tag":623,"props":21804,"children":21805},{"style":671},[21806],{"type":418,"value":2524},{"type":413,"tag":623,"props":21808,"children":21809},{"class":625,"line":1045},[21810,21815,21819,21824,21828,21833,21837,21841,21845],{"type":413,"tag":623,"props":21811,"children":21812},{"style":1058},[21813],{"type":418,"value":21814},"pulumi new azure",{"type":413,"tag":623,"props":21816,"children":21817},{"style":671},[21818],{"type":418,"value":3503},{"type":413,"tag":623,"props":21820,"children":21821},{"style":1058},[21822],{"type":418,"value":21823},"csharp ",{"type":413,"tag":623,"props":21825,"children":21826},{"style":671},[21827],{"type":418,"value":3503},{"type":413,"tag":623,"props":21829,"children":21830},{"style":1058},[21831],{"type":418,"value":21832},"n AzureStorageBackend ",{"type":413,"tag":623,"props":21834,"children":21835},{"style":671},[21836],{"type":418,"value":3503},{"type":413,"tag":623,"props":21838,"children":21839},{"style":1058},[21840],{"type":418,"value":5130},{"type":413,"tag":623,"props":21842,"children":21843},{"style":671},[21844],{"type":418,"value":3503},{"type":413,"tag":623,"props":21846,"children":21847},{"style":1058},[21848],{"type":418,"value":21849},"y\n",{"type":413,"tag":623,"props":21851,"children":21852},{"class":625,"line":1054},[21853,21858,21862],{"type":413,"tag":623,"props":21854,"children":21855},{"style":1058},[21856],{"type":418,"value":21857},"pulumi up ",{"type":413,"tag":623,"props":21859,"children":21860},{"style":671},[21861],{"type":418,"value":3503},{"type":413,"tag":623,"props":21863,"children":21864},{"style":1058},[21865],{"type":418,"value":21849},{"type":413,"tag":414,"props":21867,"children":21868},{},[21869],{"type":418,"value":21870},"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":413,"tag":414,"props":21872,"children":21873},{},[21874,21876,21881,21883,21889],{"type":418,"value":21875},"Once the ",{"type":413,"tag":619,"props":21877,"children":21879},{"className":21878},[],[21880],{"type":418,"value":4573},{"type":418,"value":21882}," command is finished, the infrastructure requested is provisioned, and we can see a new state file has been created in the ",{"type":413,"tag":619,"props":21884,"children":21886},{"className":21885},[],[21887],{"type":418,"value":21888},"iacstate",{"type":418,"value":21890}," blob container.",{"type":413,"tag":414,"props":21892,"children":21893},{},[21894],{"type":413,"tag":487,"props":21895,"children":21899},{"alt":21896,"className":21897,"src":21898},"Azure Blob container explorer showing the Pulumi state file.",[491,492],"/posts/images/pulumiazurebackend_azure_1.png",[],{"type":413,"tag":420,"props":21901,"children":21903},{"id":21902},"managing-state-sensitive-data",[21904],{"type":418,"value":21905},"Managing state sensitive data",{"type":413,"tag":600,"props":21907,"children":21909},{"id":21908},"why-is-it-needed-to-protect-sensitive-data-in-the-state",[21910],{"type":418,"value":21911},"Why is it needed to protect sensitive data in the state?",{"type":413,"tag":414,"props":21913,"children":21914},{},[21915],{"type":418,"value":21916},"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":413,"tag":414,"props":21918,"children":21919},{},[21920],{"type":418,"value":21921},"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":413,"tag":600,"props":21923,"children":21925},{"id":21924},"what-are-the-available-encryption-providers",[21926],{"type":418,"value":21927},"What are the available encryption providers?",{"type":413,"tag":414,"props":21929,"children":21930},{},[21931],{"type":418,"value":21932},"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":413,"tag":414,"props":21934,"children":21935},{},[21936],{"type":418,"value":21937},"The passphrase is just one of the supported encryption/secrets providers but there are others:",{"type":413,"tag":443,"props":21939,"children":21940},{},[21941,21946,21950,21955,21960],{"type":413,"tag":447,"props":21942,"children":21943},{},[21944],{"type":418,"value":21945},"AWS Key Management Service",{"type":413,"tag":447,"props":21947,"children":21948},{},[21949],{"type":418,"value":260},{"type":413,"tag":447,"props":21951,"children":21952},{},[21953],{"type":418,"value":21954},"Google Cloud Key Management Service",{"type":413,"tag":447,"props":21956,"children":21957},{},[21958],{"type":418,"value":21959},"HashiCorp Vault Transit Secrets Engine",{"type":413,"tag":447,"props":21961,"children":21962},{},[21963],{"type":418,"value":21964},"Pulumi Service (used by default when using Pulumi Service as the backend)",{"type":413,"tag":414,"props":21966,"children":21967},{},[21968],{"type":418,"value":21969},"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":413,"tag":600,"props":21971,"children":21973},{"id":21972},"how-to-use-azure-key-vault-as-the-encryption-provider",[21974],{"type":418,"value":21975},"How to use Azure Key Vault as the encryption provider?",{"type":413,"tag":414,"props":21977,"children":21978},{},[21979],{"type":418,"value":21980},"Let's first create a Key Vault:",{"type":413,"tag":612,"props":21982,"children":21984},{"className":5099,"code":21983,"language":248,"meta":401,"style":401},"$kvName=\"kv-iacstate-westeu-$random\"\n$vaultId=az keyvault create -g $rgName -n $kvName --enable-rbac-authorization true --query \"id\"\n",[21985],{"type":413,"tag":619,"props":21986,"children":21987},{"__ignoreMap":401},[21988,22025],{"type":413,"tag":623,"props":21989,"children":21990},{"class":625,"line":626},[21991,21995,22000,22004,22008,22013,22017,22021],{"type":413,"tag":623,"props":21992,"children":21993},{"style":671},[21994],{"type":418,"value":20580},{"type":413,"tag":623,"props":21996,"children":21997},{"style":1058},[21998],{"type":418,"value":21999},"kvName",{"type":413,"tag":623,"props":22001,"children":22002},{"style":671},[22003],{"type":418,"value":1066},{"type":413,"tag":623,"props":22005,"children":22006},{"style":671},[22007],{"type":418,"value":1023},{"type":413,"tag":623,"props":22009,"children":22010},{"style":635},[22011],{"type":418,"value":22012},"kv-iacstate-westeu-",{"type":413,"tag":623,"props":22014,"children":22015},{"style":671},[22016],{"type":418,"value":20580},{"type":413,"tag":623,"props":22018,"children":22019},{"style":1058},[22020],{"type":418,"value":21240},{"type":413,"tag":623,"props":22022,"children":22023},{"style":671},[22024],{"type":418,"value":684},{"type":413,"tag":623,"props":22026,"children":22027},{"class":625,"line":1045},[22028,22032,22037,22041,22046,22050,22054,22058,22062,22066,22070,22074,22079,22083,22088,22092,22097,22101,22105,22109,22113],{"type":413,"tag":623,"props":22029,"children":22030},{"style":671},[22031],{"type":418,"value":20580},{"type":413,"tag":623,"props":22033,"children":22034},{"style":1058},[22035],{"type":418,"value":22036},"vaultId",{"type":413,"tag":623,"props":22038,"children":22039},{"style":671},[22040],{"type":418,"value":1066},{"type":413,"tag":623,"props":22042,"children":22043},{"style":1058},[22044],{"type":418,"value":22045},"az keyvault create ",{"type":413,"tag":623,"props":22047,"children":22048},{"style":671},[22049],{"type":418,"value":3503},{"type":413,"tag":623,"props":22051,"children":22052},{"style":1058},[22053],{"type":418,"value":21616},{"type":413,"tag":623,"props":22055,"children":22056},{"style":671},[22057],{"type":418,"value":20580},{"type":413,"tag":623,"props":22059,"children":22060},{"style":1058},[22061],{"type":418,"value":21625},{"type":413,"tag":623,"props":22063,"children":22064},{"style":671},[22065],{"type":418,"value":3503},{"type":413,"tag":623,"props":22067,"children":22068},{"style":1058},[22069],{"type":418,"value":21598},{"type":413,"tag":623,"props":22071,"children":22072},{"style":671},[22073],{"type":418,"value":20580},{"type":413,"tag":623,"props":22075,"children":22076},{"style":1058},[22077],{"type":418,"value":22078},"kvName ",{"type":413,"tag":623,"props":22080,"children":22081},{"style":671},[22082],{"type":418,"value":5362},{"type":413,"tag":623,"props":22084,"children":22085},{"style":1407},[22086],{"type":418,"value":22087},"enable-rbac",{"type":413,"tag":623,"props":22089,"children":22090},{"style":671},[22091],{"type":418,"value":3503},{"type":413,"tag":623,"props":22093,"children":22094},{"style":1058},[22095],{"type":418,"value":22096},"authorization true ",{"type":413,"tag":623,"props":22098,"children":22099},{"style":671},[22100],{"type":418,"value":5362},{"type":413,"tag":623,"props":22102,"children":22103},{"style":1058},[22104],{"type":418,"value":21643},{"type":413,"tag":623,"props":22106,"children":22107},{"style":671},[22108],{"type":418,"value":1023},{"type":413,"tag":623,"props":22110,"children":22111},{"style":635},[22112],{"type":418,"value":6537},{"type":413,"tag":623,"props":22114,"children":22115},{"style":671},[22116],{"type":418,"value":684},{"type":413,"tag":414,"props":22118,"children":22119},{},[22120,22122,22128],{"type":418,"value":22121},"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":413,"tag":619,"props":22123,"children":22125},{"className":22124},[],[22126],{"type":418,"value":22127},"--enable-rbac-authorization",{"type":418,"value":22129}," 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":413,"tag":414,"props":22131,"children":22132},{},[22133],{"type":418,"value":22134},"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":413,"tag":612,"props":22136,"children":22138},{"className":5099,"code":22137,"language":248,"meta":401,"style":401},"$myUserId=az ad signed-in-user show --query \"objectId\" -o tsv \n",[22139],{"type":413,"tag":619,"props":22140,"children":22141},{"__ignoreMap":401},[22142],{"type":413,"tag":623,"props":22143,"children":22144},{"class":625,"line":626},[22145,22149,22154,22158,22163,22167,22172,22176,22181,22185,22189,22193,22197,22201,22205],{"type":413,"tag":623,"props":22146,"children":22147},{"style":671},[22148],{"type":418,"value":20580},{"type":413,"tag":623,"props":22150,"children":22151},{"style":1058},[22152],{"type":418,"value":22153},"myUserId",{"type":413,"tag":623,"props":22155,"children":22156},{"style":671},[22157],{"type":418,"value":1066},{"type":413,"tag":623,"props":22159,"children":22160},{"style":1058},[22161],{"type":418,"value":22162},"az ad signed",{"type":413,"tag":623,"props":22164,"children":22165},{"style":671},[22166],{"type":418,"value":3503},{"type":413,"tag":623,"props":22168,"children":22169},{"style":1058},[22170],{"type":418,"value":22171},"in",{"type":413,"tag":623,"props":22173,"children":22174},{"style":671},[22175],{"type":418,"value":3503},{"type":413,"tag":623,"props":22177,"children":22178},{"style":1058},[22179],{"type":418,"value":22180},"user show ",{"type":413,"tag":623,"props":22182,"children":22183},{"style":671},[22184],{"type":418,"value":5362},{"type":413,"tag":623,"props":22186,"children":22187},{"style":1058},[22188],{"type":418,"value":21643},{"type":413,"tag":623,"props":22190,"children":22191},{"style":671},[22192],{"type":418,"value":1023},{"type":413,"tag":623,"props":22194,"children":22195},{"style":635},[22196],{"type":418,"value":6086},{"type":413,"tag":623,"props":22198,"children":22199},{"style":671},[22200],{"type":418,"value":1023},{"type":413,"tag":623,"props":22202,"children":22203},{"style":671},[22204],{"type":418,"value":20050},{"type":413,"tag":623,"props":22206,"children":22207},{"style":1058},[22208],{"type":418,"value":22209},"o tsv\n",{"type":413,"tag":414,"props":22211,"children":22212},{},[22213,22215,22221],{"type":418,"value":22214},"We can then assign to this user the ",{"type":413,"tag":619,"props":22216,"children":22218},{"className":22217},[],[22219],{"type":418,"value":22220},"Key Vault Crypto Officer",{"type":418,"value":22222}," role that will allow us to create a key and encrypt/decrypt data.",{"type":413,"tag":612,"props":22224,"children":22226},{"className":5099,"code":22225,"language":248,"meta":401,"style":401},"az role assignment create --scope $vaultId --role \"Key Vault Crypto Officer\" --assignee $myUserId \n",[22227],{"type":413,"tag":619,"props":22228,"children":22229},{"__ignoreMap":401},[22230],{"type":413,"tag":623,"props":22231,"children":22232},{"class":625,"line":626},[22233,22238,22242,22247,22251,22256,22260,22265,22269,22273,22277,22282,22287,22291],{"type":413,"tag":623,"props":22234,"children":22235},{"style":1058},[22236],{"type":418,"value":22237},"az role assignment create ",{"type":413,"tag":623,"props":22239,"children":22240},{"style":671},[22241],{"type":418,"value":5362},{"type":413,"tag":623,"props":22243,"children":22244},{"style":1058},[22245],{"type":418,"value":22246},"scope ",{"type":413,"tag":623,"props":22248,"children":22249},{"style":671},[22250],{"type":418,"value":20580},{"type":413,"tag":623,"props":22252,"children":22253},{"style":1058},[22254],{"type":418,"value":22255},"vaultId ",{"type":413,"tag":623,"props":22257,"children":22258},{"style":671},[22259],{"type":418,"value":5362},{"type":413,"tag":623,"props":22261,"children":22262},{"style":1058},[22263],{"type":418,"value":22264},"role ",{"type":413,"tag":623,"props":22266,"children":22267},{"style":671},[22268],{"type":418,"value":1023},{"type":413,"tag":623,"props":22270,"children":22271},{"style":635},[22272],{"type":418,"value":22220},{"type":413,"tag":623,"props":22274,"children":22275},{"style":671},[22276],{"type":418,"value":1023},{"type":413,"tag":623,"props":22278,"children":22279},{"style":671},[22280],{"type":418,"value":22281}," --",{"type":413,"tag":623,"props":22283,"children":22284},{"style":1058},[22285],{"type":418,"value":22286},"assignee ",{"type":413,"tag":623,"props":22288,"children":22289},{"style":671},[22290],{"type":418,"value":20580},{"type":413,"tag":623,"props":22292,"children":22293},{"style":1058},[22294],{"type":418,"value":22295},"myUserId\n",{"type":413,"tag":414,"props":22297,"children":22298},{},[22299],{"type":418,"value":22300},"The key to encrypt/decrypt data can be created with the following command:",{"type":413,"tag":612,"props":22302,"children":22304},{"className":5099,"code":22303,"language":248,"meta":401,"style":401},"az keyvault key create -n encryptionState --vault-name $kvName\n",[22305],{"type":413,"tag":619,"props":22306,"children":22307},{"__ignoreMap":401},[22308],{"type":413,"tag":623,"props":22309,"children":22310},{"class":625,"line":626},[22311,22316,22320,22325,22329,22334,22338,22343,22347],{"type":413,"tag":623,"props":22312,"children":22313},{"style":1058},[22314],{"type":418,"value":22315},"az keyvault key create ",{"type":413,"tag":623,"props":22317,"children":22318},{"style":671},[22319],{"type":418,"value":3503},{"type":413,"tag":623,"props":22321,"children":22322},{"style":1058},[22323],{"type":418,"value":22324},"n encryptionState ",{"type":413,"tag":623,"props":22326,"children":22327},{"style":671},[22328],{"type":418,"value":5362},{"type":413,"tag":623,"props":22330,"children":22331},{"style":1058},[22332],{"type":418,"value":22333},"vault",{"type":413,"tag":623,"props":22335,"children":22336},{"style":671},[22337],{"type":418,"value":3503},{"type":413,"tag":623,"props":22339,"children":22340},{"style":1058},[22341],{"type":418,"value":22342},"name ",{"type":413,"tag":623,"props":22344,"children":22345},{"style":671},[22346],{"type":418,"value":20580},{"type":413,"tag":623,"props":22348,"children":22349},{"style":1058},[22350],{"type":418,"value":22351},"kvName\n",{"type":413,"tag":414,"props":22353,"children":22354},{},[22355],{"type":418,"value":22356},"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":413,"tag":612,"props":22358,"children":22360},{"className":5099,"code":22359,"language":248,"meta":401,"style":401},"$env:AZURE_KEYVAULT_AUTH_VIA_CLI=\"true\"\n",[22361],{"type":413,"tag":619,"props":22362,"children":22363},{"__ignoreMap":401},[22364],{"type":413,"tag":623,"props":22365,"children":22366},{"class":625,"line":626},[22367,22371,22376,22380,22384,22388],{"type":413,"tag":623,"props":22368,"children":22369},{"style":671},[22370],{"type":418,"value":20580},{"type":413,"tag":623,"props":22372,"children":22373},{"style":1058},[22374],{"type":418,"value":22375},"env:AZURE_KEYVAULT_AUTH_VIA_CLI",{"type":413,"tag":623,"props":22377,"children":22378},{"style":671},[22379],{"type":418,"value":1066},{"type":413,"tag":623,"props":22381,"children":22382},{"style":671},[22383],{"type":418,"value":1023},{"type":413,"tag":623,"props":22385,"children":22386},{"style":635},[22387],{"type":418,"value":5711},{"type":413,"tag":623,"props":22389,"children":22390},{"style":671},[22391],{"type":418,"value":684},{"type":413,"tag":414,"props":22393,"children":22394},{},[22395],{"type":418,"value":22396},"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":413,"tag":612,"props":22398,"children":22400},{"className":5099,"code":22399,"language":248,"meta":401,"style":401},"pulumi new azure-csharp -n AzureStorageBackend -s dev -y --secrets-provider=\"azurekeyvault://$kvName.vault.azure.net/keys/encryptionState\"\n",[22401],{"type":413,"tag":619,"props":22402,"children":22403},{"__ignoreMap":401},[22404],{"type":413,"tag":623,"props":22405,"children":22406},{"class":625,"line":626},[22407,22411,22415,22419,22423,22427,22431,22435,22439,22444,22448,22453,22457,22462,22466,22470,22475,22479,22483,22488],{"type":413,"tag":623,"props":22408,"children":22409},{"style":1058},[22410],{"type":418,"value":21814},{"type":413,"tag":623,"props":22412,"children":22413},{"style":671},[22414],{"type":418,"value":3503},{"type":413,"tag":623,"props":22416,"children":22417},{"style":1058},[22418],{"type":418,"value":21823},{"type":413,"tag":623,"props":22420,"children":22421},{"style":671},[22422],{"type":418,"value":3503},{"type":413,"tag":623,"props":22424,"children":22425},{"style":1058},[22426],{"type":418,"value":21832},{"type":413,"tag":623,"props":22428,"children":22429},{"style":671},[22430],{"type":418,"value":3503},{"type":413,"tag":623,"props":22432,"children":22433},{"style":1058},[22434],{"type":418,"value":5130},{"type":413,"tag":623,"props":22436,"children":22437},{"style":671},[22438],{"type":418,"value":3503},{"type":413,"tag":623,"props":22440,"children":22441},{"style":1058},[22442],{"type":418,"value":22443},"y ",{"type":413,"tag":623,"props":22445,"children":22446},{"style":671},[22447],{"type":418,"value":5362},{"type":413,"tag":623,"props":22449,"children":22450},{"style":1058},[22451],{"type":418,"value":22452},"secrets",{"type":413,"tag":623,"props":22454,"children":22455},{"style":671},[22456],{"type":418,"value":3503},{"type":413,"tag":623,"props":22458,"children":22459},{"style":1058},[22460],{"type":418,"value":22461},"provider",{"type":413,"tag":623,"props":22463,"children":22464},{"style":671},[22465],{"type":418,"value":1066},{"type":413,"tag":623,"props":22467,"children":22468},{"style":671},[22469],{"type":418,"value":1023},{"type":413,"tag":623,"props":22471,"children":22472},{"style":635},[22473],{"type":418,"value":22474},"azurekeyvault://",{"type":413,"tag":623,"props":22476,"children":22477},{"style":671},[22478],{"type":418,"value":20580},{"type":413,"tag":623,"props":22480,"children":22481},{"style":1058},[22482],{"type":418,"value":21999},{"type":413,"tag":623,"props":22484,"children":22485},{"style":635},[22486],{"type":418,"value":22487},".vault.azure.net/keys/encryptionState",{"type":413,"tag":623,"props":22489,"children":22490},{"style":671},[22491],{"type":418,"value":684},{"type":413,"tag":420,"props":22493,"children":22495},{"id":22494},"comparing-with-how-terraform-handle-state",[22496],{"type":418,"value":22497},"Comparing with how Terraform handle state",{"type":413,"tag":414,"props":22499,"children":22500},{},[22501],{"type":418,"value":22502},"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":413,"tag":414,"props":22504,"children":22505},{},[22506],{"type":418,"value":22507},"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":413,"tag":414,"props":22509,"children":22510},{},[22511,22513,22520],{"type":418,"value":22512},"On Microsoft documentation, there is a tutorial ",{"type":413,"tag":432,"props":22514,"children":22517},{"href":22515,"rel":22516},"https://docs.microsoft.com/en-us/azure/developer/terraform/store-state-in-azure-storage",[436],[22518],{"type":418,"value":22519},"\"Store Terraform state in Azure Storage\"",{"type":418,"value":22521}," 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":413,"tag":414,"props":22523,"children":22524},{},[22525,22527,22534,22536,22542],{"type":418,"value":22526},"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":413,"tag":432,"props":22528,"children":22531},{"href":22529,"rel":22530},"https://www.terraform.io/docs/language/state/sensitive-data.html",[436],[22532],{"type":418,"value":22533},"Terraform documentation",{"type":418,"value":22535}," just says to ",{"type":413,"tag":619,"props":22537,"children":22539},{"className":22538},[],[22540],{"type":418,"value":22541},"treat the state itself as sensitive data",{"type":418,"value":22543},". 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":413,"tag":414,"props":22545,"children":22546},{},[22547],{"type":413,"tag":487,"props":22548,"children":22552},{"alt":22549,"className":22550,"src":22551},"Terraform state file containing clear text sensitive information in vscode.",[491,492],"/posts/images/pulumiazurebackend_tf_1.png",[],{"type":413,"tag":414,"props":22554,"children":22555},{},[22556],{"type":418,"value":22557},"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":413,"tag":420,"props":22559,"children":22560},{"id":4624},[22561],{"type":418,"value":4627},{"type":413,"tag":414,"props":22563,"children":22564},{},[22565],{"type":418,"value":22566},"You can find below the complete Azure CLI script used in this article:",{"type":413,"tag":612,"props":22568,"children":22570},{"className":5099,"code":22569,"language":248,"meta":401,"style":401},"# 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",[22571],{"type":413,"tag":619,"props":22572,"children":22573},{"__ignoreMap":401},[22574,22582,22613,22640,22675,22710,22745,22752,22793,22800,22808,22874,22882,22965,22984,23001,23008,23016,23103,23167,23227,23266,23274,23301,23308,23316,23331,23339,23358,23366,23449,23457],{"type":413,"tag":623,"props":22575,"children":22576},{"class":625,"line":626},[22577],{"type":413,"tag":623,"props":22578,"children":22579},{"style":8144},[22580],{"type":418,"value":22581},"# PowerShell variables used in the script \n",{"type":413,"tag":623,"props":22583,"children":22584},{"class":625,"line":1045},[22585,22589,22593,22597,22601,22605,22609],{"type":413,"tag":623,"props":22586,"children":22587},{"style":671},[22588],{"type":418,"value":20580},{"type":413,"tag":623,"props":22590,"children":22591},{"style":1058},[22592],{"type":418,"value":21240},{"type":413,"tag":623,"props":22594,"children":22595},{"style":671},[22596],{"type":418,"value":1066},{"type":413,"tag":623,"props":22598,"children":22599},{"style":1407},[22600],{"type":418,"value":21249},{"type":413,"tag":623,"props":22602,"children":22603},{"style":671},[22604],{"type":418,"value":20050},{"type":413,"tag":623,"props":22606,"children":22607},{"style":1058},[22608],{"type":418,"value":21258},{"type":413,"tag":623,"props":22610,"children":22611},{"style":3551},[22612],{"type":418,"value":21263},{"type":413,"tag":623,"props":22614,"children":22615},{"class":625,"line":1054},[22616,22620,22624,22628,22632,22636],{"type":413,"tag":623,"props":22617,"children":22618},{"style":671},[22619],{"type":418,"value":20580},{"type":413,"tag":623,"props":22621,"children":22622},{"style":1058},[22623],{"type":418,"value":21275},{"type":413,"tag":623,"props":22625,"children":22626},{"style":671},[22627],{"type":418,"value":1066},{"type":413,"tag":623,"props":22629,"children":22630},{"style":671},[22631],{"type":418,"value":1023},{"type":413,"tag":623,"props":22633,"children":22634},{"style":635},[22635],{"type":418,"value":21288},{"type":413,"tag":623,"props":22637,"children":22638},{"style":671},[22639],{"type":418,"value":684},{"type":413,"tag":623,"props":22641,"children":22642},{"class":625,"line":1087},[22643,22647,22651,22655,22659,22663,22667,22671],{"type":413,"tag":623,"props":22644,"children":22645},{"style":671},[22646],{"type":418,"value":20580},{"type":413,"tag":623,"props":22648,"children":22649},{"style":1058},[22650],{"type":418,"value":21304},{"type":413,"tag":623,"props":22652,"children":22653},{"style":671},[22654],{"type":418,"value":1066},{"type":413,"tag":623,"props":22656,"children":22657},{"style":671},[22658],{"type":418,"value":1023},{"type":413,"tag":623,"props":22660,"children":22661},{"style":635},[22662],{"type":418,"value":21317},{"type":413,"tag":623,"props":22664,"children":22665},{"style":671},[22666],{"type":418,"value":20580},{"type":413,"tag":623,"props":22668,"children":22669},{"style":1058},[22670],{"type":418,"value":21240},{"type":413,"tag":623,"props":22672,"children":22673},{"style":671},[22674],{"type":418,"value":684},{"type":413,"tag":623,"props":22676,"children":22677},{"class":625,"line":1104},[22678,22682,22686,22690,22694,22698,22702,22706],{"type":413,"tag":623,"props":22679,"children":22680},{"style":671},[22681],{"type":418,"value":20580},{"type":413,"tag":623,"props":22683,"children":22684},{"style":1058},[22685],{"type":418,"value":21341},{"type":413,"tag":623,"props":22687,"children":22688},{"style":671},[22689],{"type":418,"value":1066},{"type":413,"tag":623,"props":22691,"children":22692},{"style":671},[22693],{"type":418,"value":1023},{"type":413,"tag":623,"props":22695,"children":22696},{"style":635},[22697],{"type":418,"value":21354},{"type":413,"tag":623,"props":22699,"children":22700},{"style":671},[22701],{"type":418,"value":20580},{"type":413,"tag":623,"props":22703,"children":22704},{"style":1058},[22705],{"type":418,"value":21240},{"type":413,"tag":623,"props":22707,"children":22708},{"style":671},[22709],{"type":418,"value":684},{"type":413,"tag":623,"props":22711,"children":22712},{"class":625,"line":1113},[22713,22717,22721,22725,22729,22733,22737,22741],{"type":413,"tag":623,"props":22714,"children":22715},{"style":671},[22716],{"type":418,"value":20580},{"type":413,"tag":623,"props":22718,"children":22719},{"style":1058},[22720],{"type":418,"value":21999},{"type":413,"tag":623,"props":22722,"children":22723},{"style":671},[22724],{"type":418,"value":1066},{"type":413,"tag":623,"props":22726,"children":22727},{"style":671},[22728],{"type":418,"value":1023},{"type":413,"tag":623,"props":22730,"children":22731},{"style":635},[22732],{"type":418,"value":22012},{"type":413,"tag":623,"props":22734,"children":22735},{"style":671},[22736],{"type":418,"value":20580},{"type":413,"tag":623,"props":22738,"children":22739},{"style":1058},[22740],{"type":418,"value":21240},{"type":413,"tag":623,"props":22742,"children":22743},{"style":671},[22744],{"type":418,"value":684},{"type":413,"tag":623,"props":22746,"children":22747},{"class":625,"line":1161},[22748],{"type":413,"tag":623,"props":22749,"children":22750},{"emptyLinePlaceholder":2790},[22751],{"type":418,"value":2793},{"type":413,"tag":623,"props":22753,"children":22754},{"class":625,"line":1207},[22755,22760,22764,22768,22772,22776,22780,22784,22788],{"type":413,"tag":623,"props":22756,"children":22757},{"style":1058},[22758],{"type":418,"value":22759},"az group create ",{"type":413,"tag":623,"props":22761,"children":22762},{"style":671},[22763],{"type":418,"value":3503},{"type":413,"tag":623,"props":22765,"children":22766},{"style":1058},[22767],{"type":418,"value":21598},{"type":413,"tag":623,"props":22769,"children":22770},{"style":671},[22771],{"type":418,"value":20580},{"type":413,"tag":623,"props":22773,"children":22774},{"style":1058},[22775],{"type":418,"value":21625},{"type":413,"tag":623,"props":22777,"children":22778},{"style":671},[22779],{"type":418,"value":3503},{"type":413,"tag":623,"props":22781,"children":22782},{"style":1058},[22783],{"type":418,"value":20108},{"type":413,"tag":623,"props":22785,"children":22786},{"style":671},[22787],{"type":418,"value":20580},{"type":413,"tag":623,"props":22789,"children":22790},{"style":1058},[22791],{"type":418,"value":22792},"location\n",{"type":413,"tag":623,"props":22794,"children":22795},{"class":625,"line":1251},[22796],{"type":413,"tag":623,"props":22797,"children":22798},{"emptyLinePlaceholder":2790},[22799],{"type":418,"value":2793},{"type":413,"tag":623,"props":22801,"children":22802},{"class":625,"line":1296},[22803],{"type":413,"tag":623,"props":22804,"children":22805},{"style":8144},[22806],{"type":418,"value":22807},"# Configure the Azure Blob Storage that will contain the state \n",{"type":413,"tag":623,"props":22809,"children":22810},{"class":625,"line":1337},[22811,22816,22820,22824,22828,22832,22836,22840,22844,22848,22852,22856,22860,22865,22869],{"type":413,"tag":623,"props":22812,"children":22813},{"style":1058},[22814],{"type":418,"value":22815},"az storage account create ",{"type":413,"tag":623,"props":22817,"children":22818},{"style":671},[22819],{"type":418,"value":3503},{"type":413,"tag":623,"props":22821,"children":22822},{"style":1058},[22823],{"type":418,"value":21616},{"type":413,"tag":623,"props":22825,"children":22826},{"style":671},[22827],{"type":418,"value":20580},{"type":413,"tag":623,"props":22829,"children":22830},{"style":1058},[22831],{"type":418,"value":21625},{"type":413,"tag":623,"props":22833,"children":22834},{"style":671},[22835],{"type":418,"value":3503},{"type":413,"tag":623,"props":22837,"children":22838},{"style":1058},[22839],{"type":418,"value":21598},{"type":413,"tag":623,"props":22841,"children":22842},{"style":671},[22843],{"type":418,"value":20580},{"type":413,"tag":623,"props":22845,"children":22846},{"style":1058},[22847],{"type":418,"value":21607},{"type":413,"tag":623,"props":22849,"children":22850},{"style":671},[22851],{"type":418,"value":3503},{"type":413,"tag":623,"props":22853,"children":22854},{"style":1058},[22855],{"type":418,"value":20108},{"type":413,"tag":623,"props":22857,"children":22858},{"style":671},[22859],{"type":418,"value":20580},{"type":413,"tag":623,"props":22861,"children":22862},{"style":1058},[22863],{"type":418,"value":22864},"location ",{"type":413,"tag":623,"props":22866,"children":22867},{"style":671},[22868],{"type":418,"value":5362},{"type":413,"tag":623,"props":22870,"children":22871},{"style":1058},[22872],{"type":418,"value":22873},"sku Standard_LRS\n",{"type":413,"tag":623,"props":22875,"children":22876},{"class":625,"line":1346},[22877],{"type":413,"tag":623,"props":22878,"children":22879},{"style":8144},[22880],{"type":418,"value":22881},"# Set environment variables needed to write on the storage account\n",{"type":413,"tag":623,"props":22883,"children":22884},{"class":625,"line":2100},[22885,22889,22893,22897,22901,22905,22909,22913,22917,22921,22925,22929,22933,22937,22941,22945,22949,22953,22957,22961],{"type":413,"tag":623,"props":22886,"children":22887},{"style":671},[22888],{"type":418,"value":20580},{"type":413,"tag":623,"props":22890,"children":22891},{"style":1058},[22892],{"type":418,"value":21579},{"type":413,"tag":623,"props":22894,"children":22895},{"style":671},[22896],{"type":418,"value":21584},{"type":413,"tag":623,"props":22898,"children":22899},{"style":1058},[22900],{"type":418,"value":21589},{"type":413,"tag":623,"props":22902,"children":22903},{"style":671},[22904],{"type":418,"value":3503},{"type":413,"tag":623,"props":22906,"children":22907},{"style":1058},[22908],{"type":418,"value":21598},{"type":413,"tag":623,"props":22910,"children":22911},{"style":671},[22912],{"type":418,"value":20580},{"type":413,"tag":623,"props":22914,"children":22915},{"style":1058},[22916],{"type":418,"value":21607},{"type":413,"tag":623,"props":22918,"children":22919},{"style":671},[22920],{"type":418,"value":3503},{"type":413,"tag":623,"props":22922,"children":22923},{"style":1058},[22924],{"type":418,"value":21616},{"type":413,"tag":623,"props":22926,"children":22927},{"style":671},[22928],{"type":418,"value":20580},{"type":413,"tag":623,"props":22930,"children":22931},{"style":1058},[22932],{"type":418,"value":21625},{"type":413,"tag":623,"props":22934,"children":22935},{"style":671},[22936],{"type":418,"value":3503},{"type":413,"tag":623,"props":22938,"children":22939},{"style":1058},[22940],{"type":418,"value":21634},{"type":413,"tag":623,"props":22942,"children":22943},{"style":671},[22944],{"type":418,"value":5362},{"type":413,"tag":623,"props":22946,"children":22947},{"style":1058},[22948],{"type":418,"value":21643},{"type":413,"tag":623,"props":22950,"children":22951},{"style":671},[22952],{"type":418,"value":20127},{"type":413,"tag":623,"props":22954,"children":22955},{"style":635},[22956],{"type":418,"value":21551},{"type":413,"tag":623,"props":22958,"children":22959},{"style":671},[22960],{"type":418,"value":20127},{"type":413,"tag":623,"props":22962,"children":22963},{"style":671},[22964],{"type":418,"value":3042},{"type":413,"tag":623,"props":22966,"children":22967},{"class":625,"line":2897},[22968,22972,22976,22980],{"type":413,"tag":623,"props":22969,"children":22970},{"style":671},[22971],{"type":418,"value":20580},{"type":413,"tag":623,"props":22973,"children":22974},{"style":1058},[22975],{"type":418,"value":21671},{"type":413,"tag":623,"props":22977,"children":22978},{"style":671},[22979],{"type":418,"value":21676},{"type":413,"tag":623,"props":22981,"children":22982},{"style":1058},[22983],{"type":418,"value":21681},{"type":413,"tag":623,"props":22985,"children":22986},{"class":625,"line":2926},[22987,22992,22996],{"type":413,"tag":623,"props":22988,"children":22989},{"style":1058},[22990],{"type":418,"value":22991},"az storage container create ",{"type":413,"tag":623,"props":22993,"children":22994},{"style":671},[22995],{"type":418,"value":3503},{"type":413,"tag":623,"props":22997,"children":22998},{"style":1058},[22999],{"type":418,"value":23000},"n iacstate\n",{"type":413,"tag":623,"props":23002,"children":23003},{"class":625,"line":2957},[23004],{"type":413,"tag":623,"props":23005,"children":23006},{"emptyLinePlaceholder":2790},[23007],{"type":418,"value":2793},{"type":413,"tag":623,"props":23009,"children":23010},{"class":625,"line":2988},[23011],{"type":413,"tag":623,"props":23012,"children":23013},{"style":8144},[23014],{"type":418,"value":23015},"# Configure the Key Vault that will be used to encrypt the sensitive data\n",{"type":413,"tag":623,"props":23017,"children":23018},{"class":625,"line":3045},[23019,23023,23027,23031,23035,23039,23043,23047,23051,23055,23059,23063,23067,23071,23075,23079,23083,23087,23091,23095,23099],{"type":413,"tag":623,"props":23020,"children":23021},{"style":671},[23022],{"type":418,"value":20580},{"type":413,"tag":623,"props":23024,"children":23025},{"style":1058},[23026],{"type":418,"value":22036},{"type":413,"tag":623,"props":23028,"children":23029},{"style":671},[23030],{"type":418,"value":1066},{"type":413,"tag":623,"props":23032,"children":23033},{"style":1058},[23034],{"type":418,"value":22045},{"type":413,"tag":623,"props":23036,"children":23037},{"style":671},[23038],{"type":418,"value":3503},{"type":413,"tag":623,"props":23040,"children":23041},{"style":1058},[23042],{"type":418,"value":21616},{"type":413,"tag":623,"props":23044,"children":23045},{"style":671},[23046],{"type":418,"value":20580},{"type":413,"tag":623,"props":23048,"children":23049},{"style":1058},[23050],{"type":418,"value":21625},{"type":413,"tag":623,"props":23052,"children":23053},{"style":671},[23054],{"type":418,"value":3503},{"type":413,"tag":623,"props":23056,"children":23057},{"style":1058},[23058],{"type":418,"value":21598},{"type":413,"tag":623,"props":23060,"children":23061},{"style":671},[23062],{"type":418,"value":20580},{"type":413,"tag":623,"props":23064,"children":23065},{"style":1058},[23066],{"type":418,"value":22078},{"type":413,"tag":623,"props":23068,"children":23069},{"style":671},[23070],{"type":418,"value":5362},{"type":413,"tag":623,"props":23072,"children":23073},{"style":1407},[23074],{"type":418,"value":22087},{"type":413,"tag":623,"props":23076,"children":23077},{"style":671},[23078],{"type":418,"value":3503},{"type":413,"tag":623,"props":23080,"children":23081},{"style":1058},[23082],{"type":418,"value":22096},{"type":413,"tag":623,"props":23084,"children":23085},{"style":671},[23086],{"type":418,"value":5362},{"type":413,"tag":623,"props":23088,"children":23089},{"style":1058},[23090],{"type":418,"value":21643},{"type":413,"tag":623,"props":23092,"children":23093},{"style":671},[23094],{"type":418,"value":1023},{"type":413,"tag":623,"props":23096,"children":23097},{"style":635},[23098],{"type":418,"value":6537},{"type":413,"tag":623,"props":23100,"children":23101},{"style":671},[23102],{"type":418,"value":684},{"type":413,"tag":623,"props":23104,"children":23105},{"class":625,"line":7652},[23106,23110,23114,23118,23122,23126,23130,23134,23138,23142,23146,23150,23154,23158,23162],{"type":413,"tag":623,"props":23107,"children":23108},{"style":671},[23109],{"type":418,"value":20580},{"type":413,"tag":623,"props":23111,"children":23112},{"style":1058},[23113],{"type":418,"value":22153},{"type":413,"tag":623,"props":23115,"children":23116},{"style":671},[23117],{"type":418,"value":1066},{"type":413,"tag":623,"props":23119,"children":23120},{"style":1058},[23121],{"type":418,"value":22162},{"type":413,"tag":623,"props":23123,"children":23124},{"style":671},[23125],{"type":418,"value":3503},{"type":413,"tag":623,"props":23127,"children":23128},{"style":1058},[23129],{"type":418,"value":22171},{"type":413,"tag":623,"props":23131,"children":23132},{"style":671},[23133],{"type":418,"value":3503},{"type":413,"tag":623,"props":23135,"children":23136},{"style":1058},[23137],{"type":418,"value":22180},{"type":413,"tag":623,"props":23139,"children":23140},{"style":671},[23141],{"type":418,"value":5362},{"type":413,"tag":623,"props":23143,"children":23144},{"style":1058},[23145],{"type":418,"value":21643},{"type":413,"tag":623,"props":23147,"children":23148},{"style":671},[23149],{"type":418,"value":1023},{"type":413,"tag":623,"props":23151,"children":23152},{"style":635},[23153],{"type":418,"value":6086},{"type":413,"tag":623,"props":23155,"children":23156},{"style":671},[23157],{"type":418,"value":1023},{"type":413,"tag":623,"props":23159,"children":23160},{"style":671},[23161],{"type":418,"value":20050},{"type":413,"tag":623,"props":23163,"children":23164},{"style":1058},[23165],{"type":418,"value":23166},"o tsv \n",{"type":413,"tag":623,"props":23168,"children":23169},{"class":625,"line":7980},[23170,23174,23178,23182,23186,23190,23194,23198,23202,23206,23210,23214,23218,23222],{"type":413,"tag":623,"props":23171,"children":23172},{"style":1058},[23173],{"type":418,"value":22237},{"type":413,"tag":623,"props":23175,"children":23176},{"style":671},[23177],{"type":418,"value":5362},{"type":413,"tag":623,"props":23179,"children":23180},{"style":1058},[23181],{"type":418,"value":22246},{"type":413,"tag":623,"props":23183,"children":23184},{"style":671},[23185],{"type":418,"value":20580},{"type":413,"tag":623,"props":23187,"children":23188},{"style":1058},[23189],{"type":418,"value":22255},{"type":413,"tag":623,"props":23191,"children":23192},{"style":671},[23193],{"type":418,"value":5362},{"type":413,"tag":623,"props":23195,"children":23196},{"style":1058},[23197],{"type":418,"value":22264},{"type":413,"tag":623,"props":23199,"children":23200},{"style":671},[23201],{"type":418,"value":1023},{"type":413,"tag":623,"props":23203,"children":23204},{"style":635},[23205],{"type":418,"value":22220},{"type":413,"tag":623,"props":23207,"children":23208},{"style":671},[23209],{"type":418,"value":1023},{"type":413,"tag":623,"props":23211,"children":23212},{"style":671},[23213],{"type":418,"value":22281},{"type":413,"tag":623,"props":23215,"children":23216},{"style":1058},[23217],{"type":418,"value":22286},{"type":413,"tag":623,"props":23219,"children":23220},{"style":671},[23221],{"type":418,"value":20580},{"type":413,"tag":623,"props":23223,"children":23224},{"style":1058},[23225],{"type":418,"value":23226},"myUserId \n",{"type":413,"tag":623,"props":23228,"children":23229},{"class":625,"line":8009},[23230,23234,23238,23242,23246,23250,23254,23258,23262],{"type":413,"tag":623,"props":23231,"children":23232},{"style":1058},[23233],{"type":418,"value":22315},{"type":413,"tag":623,"props":23235,"children":23236},{"style":671},[23237],{"type":418,"value":3503},{"type":413,"tag":623,"props":23239,"children":23240},{"style":1058},[23241],{"type":418,"value":22324},{"type":413,"tag":623,"props":23243,"children":23244},{"style":671},[23245],{"type":418,"value":5362},{"type":413,"tag":623,"props":23247,"children":23248},{"style":1058},[23249],{"type":418,"value":22333},{"type":413,"tag":623,"props":23251,"children":23252},{"style":671},[23253],{"type":418,"value":3503},{"type":413,"tag":623,"props":23255,"children":23256},{"style":1058},[23257],{"type":418,"value":22342},{"type":413,"tag":623,"props":23259,"children":23260},{"style":671},[23261],{"type":418,"value":20580},{"type":413,"tag":623,"props":23263,"children":23264},{"style":1058},[23265],{"type":418,"value":22351},{"type":413,"tag":623,"props":23267,"children":23268},{"class":625,"line":8027},[23269],{"type":413,"tag":623,"props":23270,"children":23271},{"style":8144},[23272],{"type":418,"value":23273},"# Use az cli to authenticate to key vault instead of using environment variables \n",{"type":413,"tag":623,"props":23275,"children":23276},{"class":625,"line":8036},[23277,23281,23285,23289,23293,23297],{"type":413,"tag":623,"props":23278,"children":23279},{"style":671},[23280],{"type":418,"value":20580},{"type":413,"tag":623,"props":23282,"children":23283},{"style":1058},[23284],{"type":418,"value":22375},{"type":413,"tag":623,"props":23286,"children":23287},{"style":671},[23288],{"type":418,"value":1066},{"type":413,"tag":623,"props":23290,"children":23291},{"style":671},[23292],{"type":418,"value":1023},{"type":413,"tag":623,"props":23294,"children":23295},{"style":635},[23296],{"type":418,"value":5711},{"type":413,"tag":623,"props":23298,"children":23299},{"style":671},[23300],{"type":418,"value":684},{"type":413,"tag":623,"props":23302,"children":23303},{"class":625,"line":8373},[23304],{"type":413,"tag":623,"props":23305,"children":23306},{"emptyLinePlaceholder":2790},[23307],{"type":418,"value":2793},{"type":413,"tag":623,"props":23309,"children":23310},{"class":625,"line":8391},[23311],{"type":413,"tag":623,"props":23312,"children":23313},{"style":8144},[23314],{"type":418,"value":23315},"# Indicate pulumi to use the newly created azure blob storage as a backend\n",{"type":413,"tag":623,"props":23317,"children":23318},{"class":625,"line":8416},[23319,23323,23327],{"type":413,"tag":623,"props":23320,"children":23321},{"style":1058},[23322],{"type":418,"value":21748},{"type":413,"tag":623,"props":23324,"children":23325},{"style":671},[23326],{"type":418,"value":21753},{"type":413,"tag":623,"props":23328,"children":23329},{"style":1058},[23330],{"type":418,"value":21758},{"type":413,"tag":623,"props":23332,"children":23333},{"class":625,"line":8425},[23334],{"type":413,"tag":623,"props":23335,"children":23336},{"style":8144},[23337],{"type":418,"value":23338},"# Create and use a folder to store the infrastructure code\n",{"type":413,"tag":623,"props":23340,"children":23341},{"class":625,"line":8446},[23342,23346,23350,23354],{"type":413,"tag":623,"props":23343,"children":23344},{"style":1058},[23345],{"type":418,"value":21793},{"type":413,"tag":623,"props":23347,"children":23348},{"style":671},[23349],{"type":418,"value":9626},{"type":413,"tag":623,"props":23351,"children":23352},{"style":1058},[23353],{"type":418,"value":21802},{"type":413,"tag":623,"props":23355,"children":23356},{"style":671},[23357],{"type":418,"value":2524},{"type":413,"tag":623,"props":23359,"children":23360},{"class":625,"line":8463},[23361],{"type":413,"tag":623,"props":23362,"children":23363},{"style":8144},[23364],{"type":418,"value":23365},"# Create a new Pulumi project using the azure blob storage as the backend and the keyvault as the encryption provider \n",{"type":413,"tag":623,"props":23367,"children":23368},{"class":625,"line":8471},[23369,23373,23377,23381,23385,23389,23393,23397,23401,23405,23409,23413,23417,23421,23425,23429,23433,23437,23441,23445],{"type":413,"tag":623,"props":23370,"children":23371},{"style":1058},[23372],{"type":418,"value":21814},{"type":413,"tag":623,"props":23374,"children":23375},{"style":671},[23376],{"type":418,"value":3503},{"type":413,"tag":623,"props":23378,"children":23379},{"style":1058},[23380],{"type":418,"value":21823},{"type":413,"tag":623,"props":23382,"children":23383},{"style":671},[23384],{"type":418,"value":3503},{"type":413,"tag":623,"props":23386,"children":23387},{"style":1058},[23388],{"type":418,"value":21832},{"type":413,"tag":623,"props":23390,"children":23391},{"style":671},[23392],{"type":418,"value":3503},{"type":413,"tag":623,"props":23394,"children":23395},{"style":1058},[23396],{"type":418,"value":5130},{"type":413,"tag":623,"props":23398,"children":23399},{"style":671},[23400],{"type":418,"value":3503},{"type":413,"tag":623,"props":23402,"children":23403},{"style":1058},[23404],{"type":418,"value":22443},{"type":413,"tag":623,"props":23406,"children":23407},{"style":671},[23408],{"type":418,"value":5362},{"type":413,"tag":623,"props":23410,"children":23411},{"style":1058},[23412],{"type":418,"value":22452},{"type":413,"tag":623,"props":23414,"children":23415},{"style":671},[23416],{"type":418,"value":3503},{"type":413,"tag":623,"props":23418,"children":23419},{"style":1058},[23420],{"type":418,"value":22461},{"type":413,"tag":623,"props":23422,"children":23423},{"style":671},[23424],{"type":418,"value":1066},{"type":413,"tag":623,"props":23426,"children":23427},{"style":671},[23428],{"type":418,"value":1023},{"type":413,"tag":623,"props":23430,"children":23431},{"style":635},[23432],{"type":418,"value":22474},{"type":413,"tag":623,"props":23434,"children":23435},{"style":671},[23436],{"type":418,"value":20580},{"type":413,"tag":623,"props":23438,"children":23439},{"style":1058},[23440],{"type":418,"value":21999},{"type":413,"tag":623,"props":23442,"children":23443},{"style":635},[23444],{"type":418,"value":22487},{"type":413,"tag":623,"props":23446,"children":23447},{"style":671},[23448],{"type":418,"value":684},{"type":413,"tag":623,"props":23450,"children":23451},{"class":625,"line":8492},[23452],{"type":413,"tag":623,"props":23453,"children":23454},{"style":8144},[23455],{"type":418,"value":23456},"# Deploy the infrastructure\n",{"type":413,"tag":623,"props":23458,"children":23459},{"class":625,"line":8509},[23460,23464,23468],{"type":413,"tag":623,"props":23461,"children":23462},{"style":1058},[23463],{"type":418,"value":21857},{"type":413,"tag":623,"props":23465,"children":23466},{"style":671},[23467],{"type":418,"value":3503},{"type":413,"tag":623,"props":23469,"children":23470},{"style":1058},[23471],{"type":418,"value":21849},{"type":413,"tag":414,"props":23473,"children":23474},{},[23475],{"type":418,"value":23476},"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":413,"tag":414,"props":23478,"children":23479},{},[23480,23482,23488],{"type":418,"value":23481},"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":413,"tag":432,"props":23483,"children":23486},{"href":23484,"rel":23485},"https://www.pulumi.com/pricing/",[436],[23487],{"type":418,"value":542},{"type":418,"value":23489}," 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":413,"tag":4673,"props":23491,"children":23492},{},[23493],{"type":418,"value":4677},{"title":401,"searchDepth":1045,"depth":1045,"links":23495},[23496,23500,23505,23510,23511],{"id":20973,"depth":1045,"text":20976,"children":23497},[23498,23499],{"id":20979,"depth":1054,"text":20982},{"id":21057,"depth":1054,"text":21060},{"id":21101,"depth":1045,"text":21104,"children":23501},[23502,23503,23504],{"id":21107,"depth":1054,"text":21110},{"id":21168,"depth":1054,"text":21171},{"id":21725,"depth":1054,"text":21728},{"id":21902,"depth":1045,"text":21905,"children":23506},[23507,23508,23509],{"id":21908,"depth":1054,"text":21911},{"id":21924,"depth":1054,"text":21927},{"id":21972,"depth":1054,"text":21975},{"id":22494,"depth":1045,"text":22497},{"id":4624,"depth":1045,"text":4627},"content:1.posts:30.pulumi-azure-backend.md","1.posts/30.pulumi-azure-backend.md",{"_path":76,"_dir":399,"_draft":400,"_partial":400,"_locale":401,"title":75,"description":23515,"date":23516,"image":23517,"badge":23519,"tags":23520,"body":23521,"_type":4691,"_id":25450,"_source":4693,"_file":25451,"_extension":4695},"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":23518},"/images/lightning_2.jpg",{"label":266},[257,312,315,252,228],{"type":410,"children":23522,"toc":25439},[23523,23536,23541,23547,23560,23572,23581,23587,23592,23601,23606,23612,23626,23635,23648,23661,23676,23682,23695,23701,23706,23724,23729,23735,23748,23753,23859,23872,24082,24087,24357,24369,25025,25047,25265,25273,25292,25306,25312,25317,25352,25361,25374,25379,25408,25413,25421,25425,25430,25435],{"type":413,"tag":414,"props":23524,"children":23525},{},[23526,23528,23534],{"type":418,"value":23527},"If you are using Azure Functions chances are you are using the setting ",{"type":413,"tag":619,"props":23529,"children":23531},{"className":23530},[],[23532],{"type":418,"value":23533},"AzureWebJobsStorage",{"type":418,"value":23535}," 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":413,"tag":414,"props":23537,"children":23538},{},[23539],{"type":418,"value":23540},"But first, let's start at the beginning.",{"type":413,"tag":420,"props":23542,"children":23544},{"id":23543},"what-is-this-azurewebjobsstorage-setting",[23545],{"type":418,"value":23546},"What is this AzureWebJobsStorage setting?",{"type":413,"tag":414,"props":23548,"children":23549},{},[23550,23552,23558],{"type":418,"value":23551},"As explained in the ",{"type":413,"tag":432,"props":23553,"children":23556},{"href":23554,"rel":23555},"https://docs.microsoft.com/en-us/azure/azure-functions/storage-considerations#storage-account-requirements",[436],[23557],{"type":418,"value":4994},{"type":418,"value":23559},", 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":413,"tag":414,"props":23561,"children":23562},{},[23563,23565,23570],{"type":418,"value":23564},"By default when you create a Function App with its storage account from Azure Portal, the setting ",{"type":413,"tag":619,"props":23566,"children":23568},{"className":23567},[],[23569],{"type":418,"value":23533},{"type":418,"value":23571}," 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":413,"tag":414,"props":23573,"children":23574},{},[23575],{"type":413,"tag":487,"props":23576,"children":23580},{"alt":23577,"className":23578,"src":23579},"AzureWebJobsStorage setting with a secret value in Function App settings.",[491,492],"/posts/images/functionsidentity_portal_1.png",[],{"type":413,"tag":420,"props":23582,"children":23584},{"id":23583},"why-azurewebjobsstorage-poses-a-security-risk",[23585],{"type":418,"value":23586},"Why AzureWebJobsStorage poses a security risk?",{"type":413,"tag":414,"props":23588,"children":23589},{},[23590],{"type":418,"value":23591},"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":413,"tag":414,"props":23593,"children":23594},{},[23595],{"type":413,"tag":487,"props":23596,"children":23600},{"alt":23597,"className":23598,"src":23599},"Padlock on a keyboard.",[491,492],"/posts/images/functionsidentity_padlock_1.jpgpng",[],{"type":413,"tag":414,"props":23602,"children":23603},{},[23604],{"type":418,"value":23605},"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":413,"tag":420,"props":23607,"children":23609},{"id":23608},"what-can-we-do-about-it",[23610],{"type":418,"value":23611},"What can we do about it?",{"type":413,"tag":414,"props":23613,"children":23614},{},[23615,23617,23624],{"type":418,"value":23616},"A solution could be to store the AzureWebJobsStorage secret value in an Azure Key Vault and use a ",{"type":413,"tag":432,"props":23618,"children":23621},{"href":23619,"rel":23620},"https://docs.microsoft.com/en-us/azure/app-service/app-service-key-vault-references",[436],[23622],{"type":418,"value":23623},"Key Vault reference",{"type":418,"value":23625}," to link the secret in Key Vault to the AzureWebJobsStorage setting like on the example below.",{"type":413,"tag":414,"props":23627,"children":23628},{},[23629],{"type":413,"tag":487,"props":23630,"children":23634},{"alt":23631,"className":23632,"src":23633},"AzureWebJobsStorage setting as a keyvault reference in Function App settings.",[491,492],"/posts/images/functionsidentity_portal_2.png",[],{"type":413,"tag":414,"props":23636,"children":23637},{},[23638,23640,23646],{"type":418,"value":23639},"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":413,"tag":619,"props":23641,"children":23643},{"className":23642},[],[23644],{"type":418,"value":23645},"AzureWebJobsStorage__accountName",{"type":418,"value":23647}," that only contains the name of the storage account and no secret value at all.",{"type":413,"tag":414,"props":23649,"children":23650},{},[23651,23653,23659],{"type":418,"value":23652},"If you want more details about connecting to the storage with the Function App identity you can find it ",{"type":413,"tag":432,"props":23654,"children":23657},{"href":23655,"rel":23656},"https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference#connecting-to-host-storage-with-an-identity",[436],[23658],{"type":418,"value":542},{"type":418,"value":23660},". 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":413,"tag":496,"props":23662,"children":23663},{"icon":557},[23664],{"type":413,"tag":414,"props":23665,"children":23666},{},[23667,23669,23674],{"type":418,"value":23668},"If you have read ",{"type":413,"tag":432,"props":23670,"children":23672},{"href":17826,"rel":23671},[436],[23673],{"type":418,"value":17535},{"type":418,"value":23675}," 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":413,"tag":420,"props":23677,"children":23679},{"id":23678},"how-to-configure-a-function-app-to-work-with-its-storage-account-without-a-secret-connection-string",[23680],{"type":418,"value":23681},"How to configure a Function App to work with its storage account without a secret connection string?",{"type":413,"tag":414,"props":23683,"children":23684},{},[23685,23687,23693],{"type":418,"value":23686},"To do that, I will use ",{"type":413,"tag":432,"props":23688,"children":23691},{"href":23689,"rel":23690},"https://www.pulumi.com/",[436],[23692],{"type":418,"value":312},{"type":418,"value":23694}," 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":413,"tag":600,"props":23696,"children":23698},{"id":23697},"what-resources-do-we-need-to-create",[23699],{"type":418,"value":23700},"What resources do we need to create?",{"type":413,"tag":414,"props":23702,"children":23703},{},[23704],{"type":418,"value":23705},"We need to create 3 different Azure resources:",{"type":413,"tag":443,"props":23707,"children":23708},{},[23709,23714,23719],{"type":413,"tag":447,"props":23710,"children":23711},{},[23712],{"type":418,"value":23713},"a consumption App Service Plan",{"type":413,"tag":447,"props":23715,"children":23716},{},[23717],{"type":418,"value":23718},"a Function App",{"type":413,"tag":447,"props":23720,"children":23721},{},[23722],{"type":418,"value":23723},"a Storage Account",{"type":413,"tag":414,"props":23725,"children":23726},{},[23727],{"type":418,"value":23728},"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":413,"tag":600,"props":23730,"children":23732},{"id":23731},"what-the-infrastructure-code-looks-like",[23733],{"type":418,"value":23734},"What the infrastructure code looks like?",{"type":413,"tag":414,"props":23736,"children":23737},{},[23738,23740,23746],{"type":418,"value":23739},"The infrastructure code looks like standard C# code, but it describes the Azure resources we need using the ",{"type":413,"tag":432,"props":23741,"children":23744},{"href":23742,"rel":23743},"https://www.pulumi.com/blog/full-coverage-of-azure-resources-with-azure-native/",[436],[23745],{"type":418,"value":775},{"type":418,"value":23747}," for Pulumi.",{"type":413,"tag":414,"props":23749,"children":23750},{},[23751],{"type":418,"value":23752},"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":413,"tag":612,"props":23754,"children":23756},{"className":981,"code":23755,"language":326,"meta":401,"style":401},"var resourceGroup = new ResourceGroup($\"rg-{Deployment.Instance.ProjectName}-{Deployment.Instance.StackName}\");\n",[23757],{"type":413,"tag":619,"props":23758,"children":23759},{"__ignoreMap":401},[23760],{"type":413,"tag":623,"props":23761,"children":23762},{"class":625,"line":626},[23763,23767,23771,23775,23779,23783,23787,23791,23795,23799,23803,23807,23811,23815,23819,23823,23827,23831,23835,23839,23843,23847,23851,23855],{"type":413,"tag":623,"props":23764,"children":23765},{"style":630},[23766],{"type":418,"value":994},{"type":413,"tag":623,"props":23768,"children":23769},{"style":630},[23770],{"type":418,"value":13992},{"type":413,"tag":623,"props":23772,"children":23773},{"style":671},[23774],{"type":418,"value":1004},{"type":413,"tag":623,"props":23776,"children":23777},{"style":671},[23778],{"type":418,"value":638},{"type":413,"tag":623,"props":23780,"children":23781},{"style":630},[23782],{"type":418,"value":14005},{"type":413,"tag":623,"props":23784,"children":23785},{"style":671},[23786],{"type":418,"value":1018},{"type":413,"tag":623,"props":23788,"children":23789},{"style":671},[23790],{"type":418,"value":2446},{"type":413,"tag":623,"props":23792,"children":23793},{"style":635},[23794],{"type":418,"value":14018},{"type":413,"tag":623,"props":23796,"children":23797},{"style":671},[23798],{"type":418,"value":2456},{"type":413,"tag":623,"props":23800,"children":23801},{"style":1058},[23802],{"type":418,"value":14027},{"type":413,"tag":623,"props":23804,"children":23805},{"style":671},[23806],{"type":418,"value":1404},{"type":413,"tag":623,"props":23808,"children":23809},{"style":1058},[23810],{"type":418,"value":14036},{"type":413,"tag":623,"props":23812,"children":23813},{"style":671},[23814],{"type":418,"value":1404},{"type":413,"tag":623,"props":23816,"children":23817},{"style":1058},[23818],{"type":418,"value":14045},{"type":413,"tag":623,"props":23820,"children":23821},{"style":671},[23822],{"type":418,"value":3327},{"type":413,"tag":623,"props":23824,"children":23825},{"style":635},[23826],{"type":418,"value":3503},{"type":413,"tag":623,"props":23828,"children":23829},{"style":671},[23830],{"type":418,"value":2456},{"type":413,"tag":623,"props":23832,"children":23833},{"style":1058},[23834],{"type":418,"value":14027},{"type":413,"tag":623,"props":23836,"children":23837},{"style":671},[23838],{"type":418,"value":1404},{"type":413,"tag":623,"props":23840,"children":23841},{"style":1058},[23842],{"type":418,"value":14036},{"type":413,"tag":623,"props":23844,"children":23845},{"style":671},[23846],{"type":418,"value":1404},{"type":413,"tag":623,"props":23848,"children":23849},{"style":1058},[23850],{"type":418,"value":14078},{"type":413,"tag":623,"props":23852,"children":23853},{"style":671},[23854],{"type":418,"value":2465},{"type":413,"tag":623,"props":23856,"children":23857},{"style":671},[23858],{"type":418,"value":2220},{"type":413,"tag":414,"props":23860,"children":23861},{},[23862,23864,23870],{"type":418,"value":23863},"To declare the resource group in which we want to create the storage account, we can use the property name of variable ",{"type":413,"tag":619,"props":23865,"children":23867},{"className":23866},[],[23868],{"type":418,"value":23869},"resourceGroup",{"type":418,"value":23871}," 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":413,"tag":612,"props":23873,"children":23875},{"className":981,"code":23874,"language":326,"meta":401,"style":401},"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",[23876],{"type":413,"tag":619,"props":23877,"children":23878},{"__ignoreMap":401},[23879,23958,23965,23992,24011,24018,24043,24050,24075],{"type":413,"tag":623,"props":23880,"children":23881},{"class":625,"line":626},[23882,23886,23891,23895,23899,23904,23908,23912,23917,23921,23925,23929,23933,23937,23941,23945,23949,23953],{"type":413,"tag":623,"props":23883,"children":23884},{"style":630},[23885],{"type":418,"value":994},{"type":413,"tag":623,"props":23887,"children":23888},{"style":630},[23889],{"type":418,"value":23890}," storageAccount",{"type":413,"tag":623,"props":23892,"children":23893},{"style":671},[23894],{"type":418,"value":1004},{"type":413,"tag":623,"props":23896,"children":23897},{"style":671},[23898],{"type":418,"value":638},{"type":413,"tag":623,"props":23900,"children":23901},{"style":630},[23902],{"type":418,"value":23903}," StorageAccount",{"type":413,"tag":623,"props":23905,"children":23906},{"style":671},[23907],{"type":418,"value":1018},{"type":413,"tag":623,"props":23909,"children":23910},{"style":671},[23911],{"type":418,"value":2446},{"type":413,"tag":623,"props":23913,"children":23914},{"style":635},[23915],{"type":418,"value":23916},"stnosecretfun",{"type":413,"tag":623,"props":23918,"children":23919},{"style":671},[23920],{"type":418,"value":2456},{"type":413,"tag":623,"props":23922,"children":23923},{"style":1058},[23924],{"type":418,"value":14027},{"type":413,"tag":623,"props":23926,"children":23927},{"style":671},[23928],{"type":418,"value":1404},{"type":413,"tag":623,"props":23930,"children":23931},{"style":1058},[23932],{"type":418,"value":14036},{"type":413,"tag":623,"props":23934,"children":23935},{"style":671},[23936],{"type":418,"value":1404},{"type":413,"tag":623,"props":23938,"children":23939},{"style":1058},[23940],{"type":418,"value":14078},{"type":413,"tag":623,"props":23942,"children":23943},{"style":671},[23944],{"type":418,"value":2465},{"type":413,"tag":623,"props":23946,"children":23947},{"style":671},[23948],{"type":418,"value":1037},{"type":413,"tag":623,"props":23950,"children":23951},{"style":671},[23952],{"type":418,"value":638},{"type":413,"tag":623,"props":23954,"children":23955},{"style":630},[23956],{"type":418,"value":23957}," StorageAccountArgs\n",{"type":413,"tag":623,"props":23959,"children":23960},{"class":625,"line":1045},[23961],{"type":413,"tag":623,"props":23962,"children":23963},{"style":671},[23964],{"type":418,"value":1051},{"type":413,"tag":623,"props":23966,"children":23967},{"class":625,"line":1054},[23968,23972,23976,23980,23984,23988],{"type":413,"tag":623,"props":23969,"children":23970},{"style":1058},[23971],{"type":418,"value":14231},{"type":413,"tag":623,"props":23973,"children":23974},{"style":671},[23975],{"type":418,"value":1066},{"type":413,"tag":623,"props":23977,"children":23978},{"style":1058},[23979],{"type":418,"value":13992},{"type":413,"tag":623,"props":23981,"children":23982},{"style":671},[23983],{"type":418,"value":1404},{"type":413,"tag":623,"props":23985,"children":23986},{"style":1058},[23987],{"type":418,"value":3350},{"type":413,"tag":623,"props":23989,"children":23990},{"style":671},[23991],{"type":418,"value":1084},{"type":413,"tag":623,"props":23993,"children":23994},{"class":625,"line":1087},[23995,23999,24003,24007],{"type":413,"tag":623,"props":23996,"children":23997},{"style":1058},[23998],{"type":418,"value":14296},{"type":413,"tag":623,"props":24000,"children":24001},{"style":671},[24002],{"type":418,"value":1066},{"type":413,"tag":623,"props":24004,"children":24005},{"style":671},[24006],{"type":418,"value":638},{"type":413,"tag":623,"props":24008,"children":24009},{"style":630},[24010],{"type":418,"value":18647},{"type":413,"tag":623,"props":24012,"children":24013},{"class":625,"line":1104},[24014],{"type":413,"tag":623,"props":24015,"children":24016},{"style":671},[24017],{"type":418,"value":1110},{"type":413,"tag":623,"props":24019,"children":24020},{"class":625,"line":1113},[24021,24025,24029,24034,24038],{"type":413,"tag":623,"props":24022,"children":24023},{"style":1058},[24024],{"type":418,"value":14365},{"type":413,"tag":623,"props":24026,"children":24027},{"style":671},[24028],{"type":418,"value":1066},{"type":413,"tag":623,"props":24030,"children":24031},{"style":1058},[24032],{"type":418,"value":24033}," SkuName",{"type":413,"tag":623,"props":24035,"children":24036},{"style":671},[24037],{"type":418,"value":1404},{"type":413,"tag":623,"props":24039,"children":24040},{"style":1058},[24041],{"type":418,"value":24042},"Standard_LRS\n",{"type":413,"tag":623,"props":24044,"children":24045},{"class":625,"line":1161},[24046],{"type":413,"tag":623,"props":24047,"children":24048},{"style":671},[24049],{"type":418,"value":1343},{"type":413,"tag":623,"props":24051,"children":24052},{"class":625,"line":1207},[24053,24057,24061,24066,24070],{"type":413,"tag":623,"props":24054,"children":24055},{"style":1058},[24056],{"type":418,"value":14263},{"type":413,"tag":623,"props":24058,"children":24059},{"style":671},[24060],{"type":418,"value":1066},{"type":413,"tag":623,"props":24062,"children":24063},{"style":1058},[24064],{"type":418,"value":24065}," Kind",{"type":413,"tag":623,"props":24067,"children":24068},{"style":671},[24069],{"type":418,"value":1404},{"type":413,"tag":623,"props":24071,"children":24072},{"style":1058},[24073],{"type":418,"value":24074},"StorageV2\n",{"type":413,"tag":623,"props":24076,"children":24077},{"class":625,"line":1251},[24078],{"type":413,"tag":623,"props":24079,"children":24080},{"style":671},[24081],{"type":418,"value":1352},{"type":413,"tag":414,"props":24083,"children":24084},{},[24085],{"type":418,"value":24086},"This is the way of declaring a consumption App Service Plan:",{"type":413,"tag":612,"props":24088,"children":24090},{"className":981,"code":24089,"language":326,"meta":401,"style":401},"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",[24091],{"type":413,"tag":619,"props":24092,"children":24093},{"__ignoreMap":401},[24094,24202,24209,24236,24264,24284,24291,24319,24343,24350],{"type":413,"tag":623,"props":24095,"children":24096},{"class":625,"line":626},[24097,24101,24105,24109,24113,24117,24121,24125,24129,24133,24137,24141,24145,24149,24153,24157,24161,24165,24169,24173,24177,24181,24185,24189,24193,24197],{"type":413,"tag":623,"props":24098,"children":24099},{"style":630},[24100],{"type":418,"value":994},{"type":413,"tag":623,"props":24102,"children":24103},{"style":630},[24104],{"type":418,"value":14109},{"type":413,"tag":623,"props":24106,"children":24107},{"style":671},[24108],{"type":418,"value":1004},{"type":413,"tag":623,"props":24110,"children":24111},{"style":671},[24112],{"type":418,"value":638},{"type":413,"tag":623,"props":24114,"children":24115},{"style":630},[24116],{"type":418,"value":14122},{"type":413,"tag":623,"props":24118,"children":24119},{"style":671},[24120],{"type":418,"value":1018},{"type":413,"tag":623,"props":24122,"children":24123},{"style":671},[24124],{"type":418,"value":2446},{"type":413,"tag":623,"props":24126,"children":24127},{"style":635},[24128],{"type":418,"value":14135},{"type":413,"tag":623,"props":24130,"children":24131},{"style":671},[24132],{"type":418,"value":2456},{"type":413,"tag":623,"props":24134,"children":24135},{"style":1058},[24136],{"type":418,"value":14027},{"type":413,"tag":623,"props":24138,"children":24139},{"style":671},[24140],{"type":418,"value":1404},{"type":413,"tag":623,"props":24142,"children":24143},{"style":1058},[24144],{"type":418,"value":14036},{"type":413,"tag":623,"props":24146,"children":24147},{"style":671},[24148],{"type":418,"value":1404},{"type":413,"tag":623,"props":24150,"children":24151},{"style":1058},[24152],{"type":418,"value":14045},{"type":413,"tag":623,"props":24154,"children":24155},{"style":671},[24156],{"type":418,"value":3327},{"type":413,"tag":623,"props":24158,"children":24159},{"style":635},[24160],{"type":418,"value":3503},{"type":413,"tag":623,"props":24162,"children":24163},{"style":671},[24164],{"type":418,"value":2456},{"type":413,"tag":623,"props":24166,"children":24167},{"style":1058},[24168],{"type":418,"value":14027},{"type":413,"tag":623,"props":24170,"children":24171},{"style":671},[24172],{"type":418,"value":1404},{"type":413,"tag":623,"props":24174,"children":24175},{"style":1058},[24176],{"type":418,"value":14036},{"type":413,"tag":623,"props":24178,"children":24179},{"style":671},[24180],{"type":418,"value":1404},{"type":413,"tag":623,"props":24182,"children":24183},{"style":1058},[24184],{"type":418,"value":14078},{"type":413,"tag":623,"props":24186,"children":24187},{"style":671},[24188],{"type":418,"value":2465},{"type":413,"tag":623,"props":24190,"children":24191},{"style":671},[24192],{"type":418,"value":1037},{"type":413,"tag":623,"props":24194,"children":24195},{"style":671},[24196],{"type":418,"value":638},{"type":413,"tag":623,"props":24198,"children":24199},{"style":630},[24200],{"type":418,"value":24201}," AppServicePlanArgs\n",{"type":413,"tag":623,"props":24203,"children":24204},{"class":625,"line":1045},[24205],{"type":413,"tag":623,"props":24206,"children":24207},{"style":671},[24208],{"type":418,"value":1051},{"type":413,"tag":623,"props":24210,"children":24211},{"class":625,"line":1054},[24212,24216,24220,24224,24228,24232],{"type":413,"tag":623,"props":24213,"children":24214},{"style":1058},[24215],{"type":418,"value":14231},{"type":413,"tag":623,"props":24217,"children":24218},{"style":671},[24219],{"type":418,"value":1066},{"type":413,"tag":623,"props":24221,"children":24222},{"style":1058},[24223],{"type":418,"value":13992},{"type":413,"tag":623,"props":24225,"children":24226},{"style":671},[24227],{"type":418,"value":1404},{"type":413,"tag":623,"props":24229,"children":24230},{"style":1058},[24231],{"type":418,"value":3350},{"type":413,"tag":623,"props":24233,"children":24234},{"style":671},[24235],{"type":418,"value":1084},{"type":413,"tag":623,"props":24237,"children":24238},{"class":625,"line":1087},[24239,24243,24247,24251,24256,24260],{"type":413,"tag":623,"props":24240,"children":24241},{"style":1058},[24242],{"type":418,"value":14263},{"type":413,"tag":623,"props":24244,"children":24245},{"style":671},[24246],{"type":418,"value":1066},{"type":413,"tag":623,"props":24248,"children":24249},{"style":671},[24250],{"type":418,"value":674},{"type":413,"tag":623,"props":24252,"children":24253},{"style":635},[24254],{"type":418,"value":24255},"Windows",{"type":413,"tag":623,"props":24257,"children":24258},{"style":671},[24259],{"type":418,"value":1023},{"type":413,"tag":623,"props":24261,"children":24262},{"style":671},[24263],{"type":418,"value":1084},{"type":413,"tag":623,"props":24265,"children":24266},{"class":625,"line":1104},[24267,24271,24275,24279],{"type":413,"tag":623,"props":24268,"children":24269},{"style":1058},[24270],{"type":418,"value":14296},{"type":413,"tag":623,"props":24272,"children":24273},{"style":671},[24274],{"type":418,"value":1066},{"type":413,"tag":623,"props":24276,"children":24277},{"style":671},[24278],{"type":418,"value":638},{"type":413,"tag":623,"props":24280,"children":24281},{"style":630},[24282],{"type":418,"value":24283}," SkuDescriptionArgs\n",{"type":413,"tag":623,"props":24285,"children":24286},{"class":625,"line":1113},[24287],{"type":413,"tag":623,"props":24288,"children":24289},{"style":671},[24290],{"type":418,"value":1110},{"type":413,"tag":623,"props":24292,"children":24293},{"class":625,"line":1161},[24294,24298,24302,24306,24311,24315],{"type":413,"tag":623,"props":24295,"children":24296},{"style":1058},[24297],{"type":418,"value":14332},{"type":413,"tag":623,"props":24299,"children":24300},{"style":671},[24301],{"type":418,"value":1066},{"type":413,"tag":623,"props":24303,"children":24304},{"style":671},[24305],{"type":418,"value":674},{"type":413,"tag":623,"props":24307,"children":24308},{"style":635},[24309],{"type":418,"value":24310},"Dynamic",{"type":413,"tag":623,"props":24312,"children":24313},{"style":671},[24314],{"type":418,"value":1023},{"type":413,"tag":623,"props":24316,"children":24317},{"style":671},[24318],{"type":418,"value":1084},{"type":413,"tag":623,"props":24320,"children":24321},{"class":625,"line":1207},[24322,24326,24330,24334,24339],{"type":413,"tag":623,"props":24323,"children":24324},{"style":1058},[24325],{"type":418,"value":14365},{"type":413,"tag":623,"props":24327,"children":24328},{"style":671},[24329],{"type":418,"value":1066},{"type":413,"tag":623,"props":24331,"children":24332},{"style":671},[24333],{"type":418,"value":674},{"type":413,"tag":623,"props":24335,"children":24336},{"style":635},[24337],{"type":418,"value":24338},"Y1",{"type":413,"tag":623,"props":24340,"children":24341},{"style":671},[24342],{"type":418,"value":684},{"type":413,"tag":623,"props":24344,"children":24345},{"class":625,"line":1251},[24346],{"type":413,"tag":623,"props":24347,"children":24348},{"style":671},[24349],{"type":418,"value":2097},{"type":413,"tag":623,"props":24351,"children":24352},{"class":625,"line":1296},[24353],{"type":413,"tag":623,"props":24354,"children":24355},{"style":671},[24356],{"type":418,"value":1352},{"type":413,"tag":414,"props":24358,"children":24359},{},[24360,24362,24367],{"type":418,"value":24361},"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":413,"tag":619,"props":24363,"children":24365},{"className":24364},[],[24366],{"type":418,"value":23645},{"type":418,"value":24368}," whose value is the name of the storage account.",{"type":413,"tag":612,"props":24370,"children":24372},{"className":981,"code":24371,"language":326,"meta":401,"style":401},"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",[24373],{"type":413,"tag":619,"props":24374,"children":24375},{"__ignoreMap":401},[24376,24454,24461,24489,24516,24543,24564,24571,24624,24631,24652,24659,24676,24684,24696,24704,24733,24762,24770,24781,24788,24816,24843,24850,24861,24868,24896,24920,24927,24938,24945,24972,24995,25003,25011,25018],{"type":413,"tag":623,"props":24377,"children":24378},{"class":625,"line":626},[24379,24383,24388,24392,24396,24400,24404,24408,24413,24417,24421,24425,24429,24433,24437,24441,24445,24449],{"type":413,"tag":623,"props":24380,"children":24381},{"style":630},[24382],{"type":418,"value":994},{"type":413,"tag":623,"props":24384,"children":24385},{"style":630},[24386],{"type":418,"value":24387}," functionApp",{"type":413,"tag":623,"props":24389,"children":24390},{"style":671},[24391],{"type":418,"value":1004},{"type":413,"tag":623,"props":24393,"children":24394},{"style":671},[24395],{"type":418,"value":638},{"type":413,"tag":623,"props":24397,"children":24398},{"style":630},[24399],{"type":418,"value":14446},{"type":413,"tag":623,"props":24401,"children":24402},{"style":671},[24403],{"type":418,"value":1018},{"type":413,"tag":623,"props":24405,"children":24406},{"style":671},[24407],{"type":418,"value":2446},{"type":413,"tag":623,"props":24409,"children":24410},{"style":635},[24411],{"type":418,"value":24412},"func-nosecret-",{"type":413,"tag":623,"props":24414,"children":24415},{"style":671},[24416],{"type":418,"value":2456},{"type":413,"tag":623,"props":24418,"children":24419},{"style":1058},[24420],{"type":418,"value":14027},{"type":413,"tag":623,"props":24422,"children":24423},{"style":671},[24424],{"type":418,"value":1404},{"type":413,"tag":623,"props":24426,"children":24427},{"style":1058},[24428],{"type":418,"value":14036},{"type":413,"tag":623,"props":24430,"children":24431},{"style":671},[24432],{"type":418,"value":1404},{"type":413,"tag":623,"props":24434,"children":24435},{"style":1058},[24436],{"type":418,"value":14078},{"type":413,"tag":623,"props":24438,"children":24439},{"style":671},[24440],{"type":418,"value":2465},{"type":413,"tag":623,"props":24442,"children":24443},{"style":671},[24444],{"type":418,"value":1037},{"type":413,"tag":623,"props":24446,"children":24447},{"style":671},[24448],{"type":418,"value":638},{"type":413,"tag":623,"props":24450,"children":24451},{"style":630},[24452],{"type":418,"value":24453}," WebAppArgs\n",{"type":413,"tag":623,"props":24455,"children":24456},{"class":625,"line":1045},[24457],{"type":413,"tag":623,"props":24458,"children":24459},{"style":671},[24460],{"type":418,"value":1051},{"type":413,"tag":623,"props":24462,"children":24463},{"class":625,"line":1054},[24464,24468,24472,24476,24481,24485],{"type":413,"tag":623,"props":24465,"children":24466},{"style":1058},[24467],{"type":418,"value":14263},{"type":413,"tag":623,"props":24469,"children":24470},{"style":671},[24471],{"type":418,"value":1066},{"type":413,"tag":623,"props":24473,"children":24474},{"style":671},[24475],{"type":418,"value":674},{"type":413,"tag":623,"props":24477,"children":24478},{"style":635},[24479],{"type":418,"value":24480},"FunctionApp",{"type":413,"tag":623,"props":24482,"children":24483},{"style":671},[24484],{"type":418,"value":1023},{"type":413,"tag":623,"props":24486,"children":24487},{"style":671},[24488],{"type":418,"value":1084},{"type":413,"tag":623,"props":24490,"children":24491},{"class":625,"line":1087},[24492,24496,24500,24504,24508,24512],{"type":413,"tag":623,"props":24493,"children":24494},{"style":1058},[24495],{"type":418,"value":14231},{"type":413,"tag":623,"props":24497,"children":24498},{"style":671},[24499],{"type":418,"value":1066},{"type":413,"tag":623,"props":24501,"children":24502},{"style":1058},[24503],{"type":418,"value":13992},{"type":413,"tag":623,"props":24505,"children":24506},{"style":671},[24507],{"type":418,"value":1404},{"type":413,"tag":623,"props":24509,"children":24510},{"style":1058},[24511],{"type":418,"value":3350},{"type":413,"tag":623,"props":24513,"children":24514},{"style":671},[24515],{"type":418,"value":1084},{"type":413,"tag":623,"props":24517,"children":24518},{"class":625,"line":1104},[24519,24523,24527,24531,24535,24539],{"type":413,"tag":623,"props":24520,"children":24521},{"style":1058},[24522],{"type":418,"value":14587},{"type":413,"tag":623,"props":24524,"children":24525},{"style":671},[24526],{"type":418,"value":1066},{"type":413,"tag":623,"props":24528,"children":24529},{"style":1058},[24530],{"type":418,"value":14109},{"type":413,"tag":623,"props":24532,"children":24533},{"style":671},[24534],{"type":418,"value":1404},{"type":413,"tag":623,"props":24536,"children":24537},{"style":1058},[24538],{"type":418,"value":1447},{"type":413,"tag":623,"props":24540,"children":24541},{"style":671},[24542],{"type":418,"value":1084},{"type":413,"tag":623,"props":24544,"children":24545},{"class":625,"line":1113},[24546,24551,24555,24559],{"type":413,"tag":623,"props":24547,"children":24548},{"style":1058},[24549],{"type":418,"value":24550},"    Identity ",{"type":413,"tag":623,"props":24552,"children":24553},{"style":671},[24554],{"type":418,"value":1066},{"type":413,"tag":623,"props":24556,"children":24557},{"style":671},[24558],{"type":418,"value":638},{"type":413,"tag":623,"props":24560,"children":24561},{"style":630},[24562],{"type":418,"value":24563}," ManagedServiceIdentityArgs\n",{"type":413,"tag":623,"props":24565,"children":24566},{"class":625,"line":1161},[24567],{"type":413,"tag":623,"props":24568,"children":24569},{"style":671},[24570],{"type":418,"value":1110},{"type":413,"tag":623,"props":24572,"children":24573},{"class":625,"line":1207},[24574,24579,24583,24588,24592,24597,24601,24606,24610,24615,24619],{"type":413,"tag":623,"props":24575,"children":24576},{"style":1058},[24577],{"type":418,"value":24578},"        Type ",{"type":413,"tag":623,"props":24580,"children":24581},{"style":671},[24582],{"type":418,"value":1066},{"type":413,"tag":623,"props":24584,"children":24585},{"style":1058},[24586],{"type":418,"value":24587}," Pulumi",{"type":413,"tag":623,"props":24589,"children":24590},{"style":671},[24591],{"type":418,"value":1404},{"type":413,"tag":623,"props":24593,"children":24594},{"style":1058},[24595],{"type":418,"value":24596},"AzureNative",{"type":413,"tag":623,"props":24598,"children":24599},{"style":671},[24600],{"type":418,"value":1404},{"type":413,"tag":623,"props":24602,"children":24603},{"style":1058},[24604],{"type":418,"value":24605},"Web",{"type":413,"tag":623,"props":24607,"children":24608},{"style":671},[24609],{"type":418,"value":1404},{"type":413,"tag":623,"props":24611,"children":24612},{"style":1058},[24613],{"type":418,"value":24614},"ManagedServiceIdentityType",{"type":413,"tag":623,"props":24616,"children":24617},{"style":671},[24618],{"type":418,"value":1404},{"type":413,"tag":623,"props":24620,"children":24621},{"style":1058},[24622],{"type":418,"value":24623},"SystemAssigned\n",{"type":413,"tag":623,"props":24625,"children":24626},{"class":625,"line":1251},[24627],{"type":413,"tag":623,"props":24628,"children":24629},{"style":671},[24630],{"type":418,"value":1343},{"type":413,"tag":623,"props":24632,"children":24633},{"class":625,"line":1296},[24634,24639,24643,24647],{"type":413,"tag":623,"props":24635,"children":24636},{"style":1058},[24637],{"type":418,"value":24638},"    SiteConfig ",{"type":413,"tag":623,"props":24640,"children":24641},{"style":671},[24642],{"type":418,"value":1066},{"type":413,"tag":623,"props":24644,"children":24645},{"style":671},[24646],{"type":418,"value":638},{"type":413,"tag":623,"props":24648,"children":24649},{"style":630},[24650],{"type":418,"value":24651}," SiteConfigArgs\n",{"type":413,"tag":623,"props":24653,"children":24654},{"class":625,"line":1337},[24655],{"type":413,"tag":623,"props":24656,"children":24657},{"style":671},[24658],{"type":418,"value":1110},{"type":413,"tag":623,"props":24660,"children":24661},{"class":625,"line":1346},[24662,24667,24671],{"type":413,"tag":623,"props":24663,"children":24664},{"style":1058},[24665],{"type":418,"value":24666},"        AppSettings ",{"type":413,"tag":623,"props":24668,"children":24669},{"style":671},[24670],{"type":418,"value":1066},{"type":413,"tag":623,"props":24672,"children":24673},{"style":671},[24674],{"type":418,"value":24675}," new[]\n",{"type":413,"tag":623,"props":24677,"children":24678},{"class":625,"line":2100},[24679],{"type":413,"tag":623,"props":24680,"children":24681},{"style":671},[24682],{"type":418,"value":24683},"        {\n",{"type":413,"tag":623,"props":24685,"children":24686},{"class":625,"line":2897},[24687,24691],{"type":413,"tag":623,"props":24688,"children":24689},{"style":671},[24690],{"type":418,"value":16236},{"type":413,"tag":623,"props":24692,"children":24693},{"style":630},[24694],{"type":418,"value":24695}," NameValuePairArgs\n",{"type":413,"tag":623,"props":24697,"children":24698},{"class":625,"line":2926},[24699],{"type":413,"tag":623,"props":24700,"children":24701},{"style":671},[24702],{"type":418,"value":24703},"            {\n",{"type":413,"tag":623,"props":24705,"children":24706},{"class":625,"line":2957},[24707,24712,24716,24720,24725,24729],{"type":413,"tag":623,"props":24708,"children":24709},{"style":1058},[24710],{"type":418,"value":24711},"                Name ",{"type":413,"tag":623,"props":24713,"children":24714},{"style":671},[24715],{"type":418,"value":1066},{"type":413,"tag":623,"props":24717,"children":24718},{"style":671},[24719],{"type":418,"value":674},{"type":413,"tag":623,"props":24721,"children":24722},{"style":635},[24723],{"type":418,"value":24724},"runtime",{"type":413,"tag":623,"props":24726,"children":24727},{"style":671},[24728],{"type":418,"value":1023},{"type":413,"tag":623,"props":24730,"children":24731},{"style":671},[24732],{"type":418,"value":1084},{"type":413,"tag":623,"props":24734,"children":24735},{"class":625,"line":2988},[24736,24741,24745,24749,24754,24758],{"type":413,"tag":623,"props":24737,"children":24738},{"style":1058},[24739],{"type":418,"value":24740},"                Value ",{"type":413,"tag":623,"props":24742,"children":24743},{"style":671},[24744],{"type":418,"value":1066},{"type":413,"tag":623,"props":24746,"children":24747},{"style":671},[24748],{"type":418,"value":674},{"type":413,"tag":623,"props":24750,"children":24751},{"style":635},[24752],{"type":418,"value":24753},"dotnet",{"type":413,"tag":623,"props":24755,"children":24756},{"style":671},[24757],{"type":418,"value":1023},{"type":413,"tag":623,"props":24759,"children":24760},{"style":671},[24761],{"type":418,"value":1084},{"type":413,"tag":623,"props":24763,"children":24764},{"class":625,"line":3045},[24765],{"type":413,"tag":623,"props":24766,"children":24767},{"style":671},[24768],{"type":418,"value":24769},"            },\n",{"type":413,"tag":623,"props":24771,"children":24772},{"class":625,"line":7652},[24773,24777],{"type":413,"tag":623,"props":24774,"children":24775},{"style":671},[24776],{"type":418,"value":16236},{"type":413,"tag":623,"props":24778,"children":24779},{"style":630},[24780],{"type":418,"value":24695},{"type":413,"tag":623,"props":24782,"children":24783},{"class":625,"line":7980},[24784],{"type":413,"tag":623,"props":24785,"children":24786},{"style":671},[24787],{"type":418,"value":24703},{"type":413,"tag":623,"props":24789,"children":24790},{"class":625,"line":8009},[24791,24795,24799,24803,24808,24812],{"type":413,"tag":623,"props":24792,"children":24793},{"style":1058},[24794],{"type":418,"value":24711},{"type":413,"tag":623,"props":24796,"children":24797},{"style":671},[24798],{"type":418,"value":1066},{"type":413,"tag":623,"props":24800,"children":24801},{"style":671},[24802],{"type":418,"value":674},{"type":413,"tag":623,"props":24804,"children":24805},{"style":635},[24806],{"type":418,"value":24807},"FUNCTIONS_WORKER_RUNTIME",{"type":413,"tag":623,"props":24809,"children":24810},{"style":671},[24811],{"type":418,"value":1023},{"type":413,"tag":623,"props":24813,"children":24814},{"style":671},[24815],{"type":418,"value":1084},{"type":413,"tag":623,"props":24817,"children":24818},{"class":625,"line":8027},[24819,24823,24827,24831,24835,24839],{"type":413,"tag":623,"props":24820,"children":24821},{"style":1058},[24822],{"type":418,"value":24740},{"type":413,"tag":623,"props":24824,"children":24825},{"style":671},[24826],{"type":418,"value":1066},{"type":413,"tag":623,"props":24828,"children":24829},{"style":671},[24830],{"type":418,"value":674},{"type":413,"tag":623,"props":24832,"children":24833},{"style":635},[24834],{"type":418,"value":24753},{"type":413,"tag":623,"props":24836,"children":24837},{"style":671},[24838],{"type":418,"value":1023},{"type":413,"tag":623,"props":24840,"children":24841},{"style":671},[24842],{"type":418,"value":1084},{"type":413,"tag":623,"props":24844,"children":24845},{"class":625,"line":8036},[24846],{"type":413,"tag":623,"props":24847,"children":24848},{"style":671},[24849],{"type":418,"value":24769},{"type":413,"tag":623,"props":24851,"children":24852},{"class":625,"line":8373},[24853,24857],{"type":413,"tag":623,"props":24854,"children":24855},{"style":671},[24856],{"type":418,"value":16236},{"type":413,"tag":623,"props":24858,"children":24859},{"style":630},[24860],{"type":418,"value":24695},{"type":413,"tag":623,"props":24862,"children":24863},{"class":625,"line":8391},[24864],{"type":413,"tag":623,"props":24865,"children":24866},{"style":671},[24867],{"type":418,"value":24703},{"type":413,"tag":623,"props":24869,"children":24870},{"class":625,"line":8416},[24871,24875,24879,24883,24888,24892],{"type":413,"tag":623,"props":24872,"children":24873},{"style":1058},[24874],{"type":418,"value":24711},{"type":413,"tag":623,"props":24876,"children":24877},{"style":671},[24878],{"type":418,"value":1066},{"type":413,"tag":623,"props":24880,"children":24881},{"style":671},[24882],{"type":418,"value":674},{"type":413,"tag":623,"props":24884,"children":24885},{"style":635},[24886],{"type":418,"value":24887},"FUNCTIONS_EXTENSION_VERSION",{"type":413,"tag":623,"props":24889,"children":24890},{"style":671},[24891],{"type":418,"value":1023},{"type":413,"tag":623,"props":24893,"children":24894},{"style":671},[24895],{"type":418,"value":1084},{"type":413,"tag":623,"props":24897,"children":24898},{"class":625,"line":8425},[24899,24903,24907,24911,24916],{"type":413,"tag":623,"props":24900,"children":24901},{"style":1058},[24902],{"type":418,"value":24740},{"type":413,"tag":623,"props":24904,"children":24905},{"style":671},[24906],{"type":418,"value":1066},{"type":413,"tag":623,"props":24908,"children":24909},{"style":671},[24910],{"type":418,"value":674},{"type":413,"tag":623,"props":24912,"children":24913},{"style":635},[24914],{"type":418,"value":24915},"~4",{"type":413,"tag":623,"props":24917,"children":24918},{"style":671},[24919],{"type":418,"value":684},{"type":413,"tag":623,"props":24921,"children":24922},{"class":625,"line":8446},[24923],{"type":413,"tag":623,"props":24924,"children":24925},{"style":671},[24926],{"type":418,"value":24769},{"type":413,"tag":623,"props":24928,"children":24929},{"class":625,"line":8463},[24930,24934],{"type":413,"tag":623,"props":24931,"children":24932},{"style":671},[24933],{"type":418,"value":16236},{"type":413,"tag":623,"props":24935,"children":24936},{"style":630},[24937],{"type":418,"value":24695},{"type":413,"tag":623,"props":24939,"children":24940},{"class":625,"line":8471},[24941],{"type":413,"tag":623,"props":24942,"children":24943},{"style":671},[24944],{"type":418,"value":24703},{"type":413,"tag":623,"props":24946,"children":24947},{"class":625,"line":8492},[24948,24952,24956,24960,24964,24968],{"type":413,"tag":623,"props":24949,"children":24950},{"style":1058},[24951],{"type":418,"value":24711},{"type":413,"tag":623,"props":24953,"children":24954},{"style":671},[24955],{"type":418,"value":1066},{"type":413,"tag":623,"props":24957,"children":24958},{"style":671},[24959],{"type":418,"value":674},{"type":413,"tag":623,"props":24961,"children":24962},{"style":635},[24963],{"type":418,"value":23645},{"type":413,"tag":623,"props":24965,"children":24966},{"style":671},[24967],{"type":418,"value":1023},{"type":413,"tag":623,"props":24969,"children":24970},{"style":671},[24971],{"type":418,"value":1084},{"type":413,"tag":623,"props":24973,"children":24974},{"class":625,"line":8509},[24975,24979,24983,24987,24991],{"type":413,"tag":623,"props":24976,"children":24977},{"style":1058},[24978],{"type":418,"value":24740},{"type":413,"tag":623,"props":24980,"children":24981},{"style":671},[24982],{"type":418,"value":1066},{"type":413,"tag":623,"props":24984,"children":24985},{"style":1058},[24986],{"type":418,"value":23890},{"type":413,"tag":623,"props":24988,"children":24989},{"style":671},[24990],{"type":418,"value":1404},{"type":413,"tag":623,"props":24992,"children":24993},{"style":1058},[24994],{"type":418,"value":1476},{"type":413,"tag":623,"props":24996,"children":24997},{"class":625,"line":8527},[24998],{"type":413,"tag":623,"props":24999,"children":25000},{"style":671},[25001],{"type":418,"value":25002},"            }\n",{"type":413,"tag":623,"props":25004,"children":25005},{"class":625,"line":8539},[25006],{"type":413,"tag":623,"props":25007,"children":25008},{"style":671},[25009],{"type":418,"value":25010},"        },\n",{"type":413,"tag":623,"props":25012,"children":25013},{"class":625,"line":8557},[25014],{"type":413,"tag":623,"props":25015,"children":25016},{"style":671},[25017],{"type":418,"value":1343},{"type":413,"tag":623,"props":25019,"children":25020},{"class":625,"line":8575},[25021],{"type":413,"tag":623,"props":25022,"children":25023},{"style":671},[25024],{"type":418,"value":1352},{"type":413,"tag":414,"props":25026,"children":25027},{},[25028,25030,25037,25039,25045],{"type":418,"value":25029},"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":413,"tag":432,"props":25031,"children":25034},{"href":25032,"rel":25033},"https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles",[436],[25035],{"type":418,"value":25036},"this page of the Microsoft documentation",{"type":418,"value":25038}," but I hope that in the future Pulumi will provide an ",{"type":413,"tag":619,"props":25040,"children":25042},{"className":25041},[],[25043],{"type":418,"value":25044},"enum",{"type":418,"value":25046}," or something like that with the possible values to make that easier to find and assign.",{"type":413,"tag":612,"props":25048,"children":25050},{"className":981,"code":25049,"language":326,"meta":401,"style":401},"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",[25051],{"type":413,"tag":619,"props":25052,"children":25053},{"__ignoreMap":401},[25054,25108,25115,25179,25207,25235,25258],{"type":413,"tag":623,"props":25055,"children":25056},{"class":625,"line":626},[25057,25061,25066,25070,25074,25078,25082,25086,25091,25095,25099,25103],{"type":413,"tag":623,"props":25058,"children":25059},{"style":630},[25060],{"type":418,"value":994},{"type":413,"tag":623,"props":25062,"children":25063},{"style":630},[25064],{"type":418,"value":25065}," storageBlobDataOwnerRole",{"type":413,"tag":623,"props":25067,"children":25068},{"style":671},[25069],{"type":418,"value":1004},{"type":413,"tag":623,"props":25071,"children":25072},{"style":671},[25073],{"type":418,"value":638},{"type":413,"tag":623,"props":25075,"children":25076},{"style":630},[25077],{"type":418,"value":2862},{"type":413,"tag":623,"props":25079,"children":25080},{"style":671},[25081],{"type":418,"value":1018},{"type":413,"tag":623,"props":25083,"children":25084},{"style":671},[25085],{"type":418,"value":1023},{"type":413,"tag":623,"props":25087,"children":25088},{"style":635},[25089],{"type":418,"value":25090},"storageBlobDataOwner",{"type":413,"tag":623,"props":25092,"children":25093},{"style":671},[25094],{"type":418,"value":1023},{"type":413,"tag":623,"props":25096,"children":25097},{"style":671},[25098],{"type":418,"value":1037},{"type":413,"tag":623,"props":25100,"children":25101},{"style":671},[25102],{"type":418,"value":638},{"type":413,"tag":623,"props":25104,"children":25105},{"style":630},[25106],{"type":418,"value":25107}," RoleAssignmentArgs\n",{"type":413,"tag":623,"props":25109,"children":25110},{"class":625,"line":1045},[25111],{"type":413,"tag":623,"props":25112,"children":25113},{"style":671},[25114],{"type":418,"value":1051},{"type":413,"tag":623,"props":25116,"children":25117},{"class":625,"line":1054},[25118,25123,25127,25131,25135,25140,25144,25148,25152,25157,25161,25166,25170,25175],{"type":413,"tag":623,"props":25119,"children":25120},{"style":1058},[25121],{"type":418,"value":25122},"    PrincipalId ",{"type":413,"tag":623,"props":25124,"children":25125},{"style":671},[25126],{"type":418,"value":1066},{"type":413,"tag":623,"props":25128,"children":25129},{"style":1058},[25130],{"type":418,"value":24387},{"type":413,"tag":623,"props":25132,"children":25133},{"style":671},[25134],{"type":418,"value":1404},{"type":413,"tag":623,"props":25136,"children":25137},{"style":1058},[25138],{"type":418,"value":25139},"Identity",{"type":413,"tag":623,"props":25141,"children":25142},{"style":671},[25143],{"type":418,"value":1404},{"type":413,"tag":623,"props":25145,"children":25146},{"style":1407},[25147],{"type":418,"value":2187},{"type":413,"tag":623,"props":25149,"children":25150},{"style":671},[25151],{"type":418,"value":1018},{"type":413,"tag":623,"props":25153,"children":25154},{"style":630},[25155],{"type":418,"value":25156},"i",{"type":413,"tag":623,"props":25158,"children":25159},{"style":671},[25160],{"type":418,"value":2201},{"type":413,"tag":623,"props":25162,"children":25163},{"style":1058},[25164],{"type":418,"value":25165}," i",{"type":413,"tag":623,"props":25167,"children":25168},{"style":671},[25169],{"type":418,"value":1404},{"type":413,"tag":623,"props":25171,"children":25172},{"style":1058},[25173],{"type":418,"value":25174},"PrincipalId",{"type":413,"tag":623,"props":25176,"children":25177},{"style":671},[25178],{"type":418,"value":3820},{"type":413,"tag":623,"props":25180,"children":25181},{"class":625,"line":1087},[25182,25187,25191,25195,25199,25203],{"type":413,"tag":623,"props":25183,"children":25184},{"style":1058},[25185],{"type":418,"value":25186},"    PrincipalType ",{"type":413,"tag":623,"props":25188,"children":25189},{"style":671},[25190],{"type":418,"value":1066},{"type":413,"tag":623,"props":25192,"children":25193},{"style":1058},[25194],{"type":418,"value":2941},{"type":413,"tag":623,"props":25196,"children":25197},{"style":671},[25198],{"type":418,"value":1404},{"type":413,"tag":623,"props":25200,"children":25201},{"style":1058},[25202],{"type":418,"value":2950},{"type":413,"tag":623,"props":25204,"children":25205},{"style":671},[25206],{"type":418,"value":1084},{"type":413,"tag":623,"props":25208,"children":25209},{"class":625,"line":1104},[25210,25214,25218,25222,25227,25231],{"type":413,"tag":623,"props":25211,"children":25212},{"style":1058},[25213],{"type":418,"value":2963},{"type":413,"tag":623,"props":25215,"children":25216},{"style":671},[25217],{"type":418,"value":1066},{"type":413,"tag":623,"props":25219,"children":25220},{"style":671},[25221],{"type":418,"value":674},{"type":413,"tag":623,"props":25223,"children":25224},{"style":635},[25225],{"type":418,"value":25226},"/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b",{"type":413,"tag":623,"props":25228,"children":25229},{"style":671},[25230],{"type":418,"value":1023},{"type":413,"tag":623,"props":25232,"children":25233},{"style":671},[25234],{"type":418,"value":1084},{"type":413,"tag":623,"props":25236,"children":25237},{"class":625,"line":1113},[25238,25242,25246,25250,25254],{"type":413,"tag":623,"props":25239,"children":25240},{"style":1058},[25241],{"type":418,"value":2994},{"type":413,"tag":623,"props":25243,"children":25244},{"style":671},[25245],{"type":418,"value":1066},{"type":413,"tag":623,"props":25247,"children":25248},{"style":1058},[25249],{"type":418,"value":23890},{"type":413,"tag":623,"props":25251,"children":25252},{"style":671},[25253],{"type":418,"value":1404},{"type":413,"tag":623,"props":25255,"children":25256},{"style":1058},[25257],{"type":418,"value":4386},{"type":413,"tag":623,"props":25259,"children":25260},{"class":625,"line":1161},[25261],{"type":413,"tag":623,"props":25262,"children":25263},{"style":671},[25264],{"type":418,"value":1352},{"type":413,"tag":496,"props":25266,"children":25267},{"icon":557},[25268],{"type":413,"tag":414,"props":25269,"children":25270},{},[25271],{"type":418,"value":25272},"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":413,"tag":414,"props":25274,"children":25275},{},[25276,25278,25283,25285,25290],{"type":418,"value":25277},"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":413,"tag":619,"props":25279,"children":25281},{"className":25280},[],[25282],{"type":418,"value":23645},{"type":418,"value":25284},". One thing we could do is to remove the ",{"type":413,"tag":619,"props":25286,"children":25288},{"className":25287},[],[25289],{"type":418,"value":23645},{"type":418,"value":25291}," 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":413,"tag":414,"props":25293,"children":25294},{},[25295,25297,25304],{"type":418,"value":25296},"You can find all this code on ",{"type":413,"tag":432,"props":25298,"children":25301},{"href":25299,"rel":25300},"https://github.com/TechWatching/FunctionAppWithoutSecretConnectionString",[436],[25302],{"type":418,"value":25303},"this GitHub repository",{"type":418,"value":25305}," 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":413,"tag":420,"props":25307,"children":25309},{"id":25308},"how-can-we-remove-the-azurewebjobsstorage-secret-setting-using-terraform",[25310],{"type":418,"value":25311},"How can we remove the AzureWebJobsStorage secret setting using Terraform?",{"type":413,"tag":414,"props":25313,"children":25314},{},[25315],{"type":418,"value":25316},"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":413,"tag":414,"props":25318,"children":25319},{},[25320,25322,25328,25329,25335,25337,25343,25344,25350],{"type":418,"value":25321},"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":413,"tag":432,"props":25323,"children":25325},{"href":17287,"rel":25324},[436],[25326],{"type":418,"value":25327},"Terraform documentation of a Function App resource",{"type":418,"value":9856},{"type":413,"tag":619,"props":25330,"children":25332},{"className":25331},[],[25333],{"type":418,"value":25334},"azurerm_function_app",{"type":418,"value":25336},", the AzureWebJobsStorage setting is automatically filled based on the ",{"type":413,"tag":619,"props":25338,"children":25340},{"className":25339},[],[25341],{"type":418,"value":25342},"storage_account_name",{"type":418,"value":1778},{"type":413,"tag":619,"props":25345,"children":25347},{"className":25346},[],[25348],{"type":418,"value":25349},"storage_account_access_key",{"type":418,"value":25351}," 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":413,"tag":414,"props":25353,"children":25354},{},[25355],{"type":413,"tag":487,"props":25356,"children":25360},{"alt":25357,"className":25358,"src":25359},"Azure RM provider for Terraform documentation about AzureWebJobsStorage.",[491,492],"/posts/images/functionsidentity_terraform_1.png",[],{"type":413,"tag":414,"props":25362,"children":25363},{},[25364,25366,25372],{"type":418,"value":25365},"The only possibility right now is to use an ARM template in your Terraform project thanks to the ",{"type":413,"tag":619,"props":25367,"children":25369},{"className":25368},[],[25370],{"type":418,"value":25371},"azurerm_resource_group_template_deployment",{"type":418,"value":25373}," resource but that defeats the whole point of using Terraform especially for a major resource like a Function App.",{"type":413,"tag":414,"props":25375,"children":25376},{},[25377],{"type":418,"value":25378},"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":413,"tag":443,"props":25380,"children":25381},{},[25382,25395],{"type":413,"tag":447,"props":25383,"children":25384},{},[25385,25386,25393],{"type":418,"value":768},{"type":413,"tag":432,"props":25387,"children":25390},{"href":25388,"rel":25389},"https://github.com/hashicorp/terraform-provider-azurerm/issues/8977",[436],[25391],{"type":418,"value":25392},"8977 issue",{"type":418,"value":25394}," that aims at supporting a KeyVault reference for the AzureWebJobsStorage setting",{"type":413,"tag":447,"props":25396,"children":25397},{},[25398,25399,25406],{"type":418,"value":768},{"type":413,"tag":432,"props":25400,"children":25403},{"href":25401,"rel":25402},"https://github.com/hashicorp/terraform-provider-azurerm/issues/13240",[436],[25404],{"type":418,"value":25405},"13240 issue",{"type":418,"value":25407}," that aims at supporting the Managed Identity access to storage",{"type":413,"tag":414,"props":25409,"children":25410},{},[25411],{"type":418,"value":25412},"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":413,"tag":496,"props":25414,"children":25415},{"icon":557},[25416],{"type":413,"tag":414,"props":25417,"children":25418},{},[25419],{"type":418,"value":25420},"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":413,"tag":420,"props":25422,"children":25423},{"id":17596},[25424],{"type":418,"value":17599},{"type":413,"tag":414,"props":25426,"children":25427},{},[25428],{"type":418,"value":25429},"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":413,"tag":414,"props":25431,"children":25432},{},[25433],{"type":418,"value":25434},"If you did not know Pulumi before reading this article, I hope it made you want to give it a try.\nHappy learning.",{"type":413,"tag":4673,"props":25436,"children":25437},{},[25438],{"type":418,"value":4677},{"title":401,"searchDepth":1045,"depth":1045,"links":25440},[25441,25442,25443,25444,25448,25449],{"id":23543,"depth":1045,"text":23546},{"id":23583,"depth":1045,"text":23586},{"id":23608,"depth":1045,"text":23611},{"id":23678,"depth":1045,"text":23681,"children":25445},[25446,25447],{"id":23697,"depth":1054,"text":23700},{"id":23731,"depth":1054,"text":23734},{"id":25308,"depth":1045,"text":25311},{"id":17596,"depth":1045,"text":17599},"content:1.posts:23.azure-functions-without-azurewebjobsstorage.md","1.posts/23.azure-functions-without-azurewebjobsstorage.md",1716749600603]