{"id":19522872,"url":"https://github.com/techwatching/pulumi-azure-workshop","last_synced_at":"2025-04-26T09:32:21.478Z","repository":{"id":198088829,"uuid":"697996592","full_name":"TechWatching/pulumi-azure-workshop","owner":"TechWatching","description":"Workshop to get started with Pulumi on Azure","archived":false,"fork":false,"pushed_at":"2024-11-08T00:24:15.000Z","size":60,"stargazers_count":20,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-04T10:46:28.362Z","etag":null,"topics":["iac","pulumi","pulumi-azure","workshop"],"latest_commit_sha":null,"homepage":"","language":"PowerShell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc-by-sa-4.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TechWatching.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-28T23:10:56.000Z","updated_at":"2025-01-23T17:46:48.000Z","dependencies_parsed_at":"2023-10-17T04:43:27.893Z","dependency_job_id":null,"html_url":"https://github.com/TechWatching/pulumi-azure-workshop","commit_stats":null,"previous_names":["techwatching/pulumi-azure-workshop"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TechWatching%2Fpulumi-azure-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TechWatching%2Fpulumi-azure-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TechWatching%2Fpulumi-azure-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TechWatching%2Fpulumi-azure-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TechWatching","download_url":"https://codeload.github.com/TechWatching/pulumi-azure-workshop/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250967368,"owners_count":21515584,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["iac","pulumi","pulumi-azure","workshop"],"created_at":"2024-11-11T00:41:21.515Z","updated_at":"2025-04-26T09:32:21.050Z","avatar_url":"https://github.com/TechWatching.png","language":"PowerShell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Getting Started Provisioning Infrastructure on Azure with Pulumi\n\n## Prerequisites\n\n### Installations \u0026 configurations\n\n- [Azure Subscription](https://azure.microsoft.com/en-us/pricing/purchase-options/azure-account)\n- [Azure CLI](https://github.com/azure/azure-cli/releases)\n- [Pulumi CLI](https://www.pulumi.com/docs/iac/download-install/#download-install-pulumi)\n- Your preferred language runtime\n- Your favorite IDE\n\nThis [page](https://www.pulumi.com/docs/clouds/azure/get-started/begin/) in the documentation covers all you need to do to set up your environment.\n\n\u003e [!NOTE]  \n\u003e You can use the OS, language, and IDE you want for this workshop. Yet for the sake of simplicity, the samples in the tutorial won't cover every possible configuration. That should not prevent you from choosing the technologies and tools you are already familiar with to complete this workshop.\n\n\u003cdetails\u003e\n  \u003csummary\u003eExample on Windows with Winget\u003c/summary\u003e\n\nOn Windows for instance, you can set up your environment using PowerShell and Windows Package Manager like this:\n\n```powershell\n# Install Azure CLI using winget\nwinget install -e --id Microsoft.AzureCLI\n\n# Install Pulumi CLI using winget\nwinget install -e --id=Pulumi.Pulumi\n\n# (Optional) Install the .NET SDK\nwinget install Microsoft.DotNet.SDK.8\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eExample on Linux with simple script\u003c/summary\u003e\n\nOn Linux for instance, you can set up your environment using this script:\n\n```bash\n# Install Azure CLI\ncurl -L https://aka.ms/InstallAzureCli | bash\n\n# Install Pulumi CLI\ncurl -fsSL https://get.pulumi.com | sh\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eExample on MacOS with homebrew\u003c/summary\u003e\n\nOn MacOS for instance, you can set up your environment using homebrew:\n\n```bash\n# Install Azure CLI using homebrew\nbrew install azure-cli\n\n# Install Pulumi CLI using homebrew\nbrew install pulumi/tap/pulumi\n```\n\u003c/details\u003e\n\nOnce the Azure CLI is installed:\n\u003cdetails\u003e\n  \u003csummary\u003eLog in to Azure and select your Azure subscription\u003c/summary\u003e \n\n```powershell\n# Log in to Azure\n# You can specify the -t option with your tenant identifier if you have multiple tenants\naz login\n\n# (Optional) List your available subscriptions and grab the identifier of the subscription you want to use\naz account list --query \"[].{id:id, name:name}\"\n\n# (Optional) Set the correct subscription identifier, \"79400867-f366-4ec9-84ba-d1dca756beb5 in the example below\naz account set -s 79400867-f366-4ec9-84ba-d1dca756beb5\naz account show\n```\n\n\u003c/details\u003e\n\n\n### Choose a backend\n\nAs Pulumi is a declarative IaC solution that uses a state to manage the cloud resources, a place to store this state is needed: the \"backend\". An encryption provider is also needed to encrypt that will be used. You can check this [article in the documentation](https://www.pulumi.com/docs/concepts/state/#managing-state-backend-options) to see the different backends and encryption providers available. \n\nThe most convenient way of doing this workshop without worrying about configuring a backend or an encryption provider is to use Pulumi Cloud which is free for individuals. You can just create an account [here](https://app.pulumi.com/signup) (or sign in using your GitHub/GitLab account) and that's it.\n\nIf you don't want to use Pulumi Cloud, that's totally fine too, check the [documentation](https://www.pulumi.com/docs/iac/concepts/state-and-backends/#using-a-self-managed-backend) to use a self-managed backend and another encryption provider. You can also check this [article](https://www.techwatching.dev/posts/pulumi-azure-backend) that demonstrates how to use Pulumi with Azure Blob Storage as the backend and Azure Key Vault as the encryption provider (script to configure these resources is available at the end of the article).\n\nLog in to your backend using the [pulumi login CLI command](https://www.pulumi.com/docs/iac/cli/commands/pulumi_login/)\n\n\u003cdetails open\u003e\n  \u003csummary\u003eLog in to Pulumi Cloud\u003c/summary\u003e\n\n```powershell\npulumi login\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eLog in to local filesystem backend\u003c/summary\u003e\n\n```powershell\npulumi login --local\n```\n\u003c/details\u003e\n\n## Pulumi fundamentals\n\n### Create a basic Pulumi project\n\n1. Create a new directory for the workshop with a new `infra` directory in it.\n```powershell\nmkdir pulumi-workshop; cd pulumi-workshop; mkdir infra; cd infra\n```\n\n2. List the available templates\n```powershell\npulumi new -l\n```\n\nThere are several azure templates (prefixed by azure) that are already configured to provision resources to Azure, but for the purpose of this workshop you will start a project from scratch to better understand how everything works.\n\n3. Create a new Pulumi project using an empty template (corresponding to the language of your choice)\n\n```powershell\npulumi new typescript -n PulumiAzureWorkshop -s dev -d \"Workshop to learn Pulumi with Azure fundamentals\"\n```\n\nThe `-s dev` option is used to initialize the project with a stack named `dev`. A [stack](https://www.pulumi.com/docs/concepts/stack/#stacks) is an independently configurable instance of a Pulumi program. Stacks are mainly use to have a different instance for each environment (dev, staging, preprod, prod ...). or for [each developer making changes to the infrastructure](https://www.pulumi.com/blog/iac-recommended-practices-developer-stacks-git-branches/#using-developer-stacks).\n\n\u003e [!NOTE]  \n\u003e If you forget to log in before, you will be prompted to log in to Pulumi Cloud when running this command. Just use your GitHub/GitLab account or the credentials of the account you previously created. If you use a self-hosted backend, log in with the appropriate backend url before running the `pulumi new` command.\n\nOpen the project in your favorite IDE to browse the files.\n\n### Deploy a stack\n\nUse [`pulumi up`](https://www.pulumi.com/docs/cli/commands/pulumi_up/) to deploy the stack\n\nThe command will first display a preview of the changes and then ask you whether you want to apply the changes. Select yes.\n\nAs there are currently no resources in the Pulumi program, only the stack itself will be created in the state, no cloud resources will be provisioned.\n\nDepending on your template, the Pulumi program may contain an output that is displayed once the command is executed. [Outputs](https://www.pulumi.com/docs/iac/concepts/stacks/#outputs) can be used to retrieve information from a Pulumi stack like URL from provisioned cloud resources.\n\n- If there is not existing output, add an output `outputKey` with a value `outputValue`.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in C#\u003c/summary\u003e\n\n```csharp\nreturn new Dictionary\u003cstring, object?\u003e\n{\n   [\"outputKey\"] = \"outputValue\"\n};\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in TypeScript\u003c/summary\u003e\n\n```typescript\nexport const outputKey = \"outputValue\"\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in Python\u003c/summary\u003e\n\n```typescript\npulumi.export(\"outputKey\", \"outputValue\")\n```\n\u003c/details\u003e\n\n### Handle stack configuration, stack outputs, and secrets\n\n[Configuration](https://www.pulumi.com/docs/concepts/config/) allows you to configure resources with different settings depending on the stack you are using. A basic use case is to have the pricing tier of a resource in the configuration to have less expensive/powerful machines in the development environment than in production.\n\n1. Add a setting named `AppServiceSku` with the value `F1` to the stack configuration using the command [`pulumi config set`](https://www.pulumi.com/docs/cli/commands/pulumi_config_set/)\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand\u003c/summary\u003e\n  \n  ```powershell\n  pulumi config set AppServiceSku F1\n  ```\n\u003c/details\u003e\n\nThe new setting is displayed in the dev stack configuration file: `Pulumi.dev.yaml`. \n\n2. Modify the code to retrieve the `AppServiceSku` setting and put it in the outputs (cf. [doc](https://www.pulumi.com/docs/concepts/config/#code)).\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode to retrieve the configuration in C#\u003c/summary\u003e\n  \n```csharp\nvar config = new Config();\nvar appServiceSku = config.Get(\"AppServiceSku\");\n\nreturn new Dictionary\u003cstring, object?\u003e\n{\n   [\"outputKey\"] = \"outputValue\",\n   [\"appServiceSku\"] = appServiceSku\n};\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode to retrieve the configuration in TypeScript\u003c/summary\u003e\n    \n```typescript\nimport {Config} from \"@pulumi/pulumi\";\n\nconst config = new Config()\nconst appServiceSkuSetting = config.get(\"AppServiceSku\")\n\nexport const outputKey = \"outputValue\"\nexport const appServiceSku = appServiceSkuSetting\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode to retrieve the configuration in Python\u003c/summary\u003e\n\n```python\nimport pulumi \nfrom pulumi import Config\n\n\nconfig = Config()\napp_service_sku = config.get(\"AppServiceSku\")\n\npulumi.export(\"outputKey\", \"outputValue\")\npulumi.export(\"appServiceSku\", app_service_sku)\n```\n\u003c/details\u003e\n\n\u003e [!NOTE]  \n\u003e Run `pulumi up -y` (the `-y` option is to automatically approve the preview) to update the stack and verify your code is working as expected. This will not always be specified in the rest of the workshop.\n\nPulumi has built-in supports for [secrets](https://www.pulumi.com/docs/concepts/secrets/#secrets-1) that are encrypted in the state.\n\n3.  Add a new secret setting `ExternalApiKey` with the value `SecretToBeKeptSecure` to the configuration and to the outputs.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand\u003c/summary\u003e\n\n```powershell\npulumi config set --secret ExternalApiKey SecretToBeKeptSecure\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in C#\u003c/summary\u003e\n\n```csharp\nvar config = new Config();\nvar appServiceSku = config.Get(\"AppServiceSku\");\nvar externalApiKey = config.RequireSecret(\"ExternalApiKey\");\n\nreturn new Dictionary\u003cstring, object?\u003e\n{\n   [\"outputKey\"] = \"outputValue\",\n   [\"appServiceSku\"] = appServiceSku,\n   [\"apiKey\"] = externalApiKey\n};\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in TypeScript\u003c/summary\u003e\n\n```typescript\nconst config = new Config()\nconst appServiceSkuSetting = config.get(\"AppServiceSku\")\nconst externalApiKey = config.requireSecret(\"ExternalApiKey\")\n\nexport const outputKey = \"outputValue\"\nexport const appServiceSku = appServiceSkuSetting\nexport const apiKey = externalApiKey\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in Python\u003c/summary\u003e\n\n```python\nconfig = Config()\napp_service_sku = config.get(\"AppServiceSku\")\nexternal_api_key = config.require_secret(\"ExternalApiKey\")\n\npulumi.export(\"outputKey\", \"outputValue\")\npulumi.export(\"appServiceSku\", app_service_sku)\npulumi.export(\"apiKey\", external_api_key)\n```\n\u003c/details\u003e\n\nYou can see that the secret is masked in the logs and that you have to use the command `pulumi stack output --show-secrets` to display it.\n\n## Provision Azure resources\n\n### Configure the program to use the Azure provider\n\n[Providers](https://www.pulumi.com/docs/concepts/resources/providers/) are the packages that allow you to provision resources in cloud providers or SaaS. Each resource provider is specific to a cloud provider/SaaS. \n\n1. Add the [Azure Native Provider package](https://www.pulumi.com/registry/packages/azure-native/installation-configuration/#installation) to the project.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand for C#\u003c/summary\u003e\n  \n```powershell\ndotnet add package Pulumi.AzureNative\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand for TypeScript\u003c/summary\u003e\n\n```powershell\npnpm add @pulumi/azure-native\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand for Python\u003c/summary\u003e\n\n```powershell\npip install pulumi-azure-native\n## Or if you use poetry :\n## poetry add pulumi-azure-native\n```\n\u003c/details\u003e\n\n\u003e [!NOTE]\n\u003e The package is big so it can take some time to download and install especially if you are using Node.js\n\nAzure providers allows to configure a default location for Azure resources so that you don't need to specify it each time you create a new resource.\n\n2.  Configure the [default location](https://www.pulumi.com/registry/packages/azure-native/installation-configuration/#set-configuration-using-pulumi-config) for your Azure resources.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand\u003c/summary\u003e\n  \n```powershell\npulumi config set azure-native:location westeurope\n```\n\u003c/details\u003e\n\n\u003e [!NOTE]  \n\u003e All azure locations can be listed using the following command: `az account list-locations -o table`\n\n3. Ensure you are correctly logged in the azure CLI using the `az account show` command. Otherwise, use the `az login` command.  \n\n### Work with Azure resources\n\nYou can explore all Azure resources in the [documentation of the Azure API Native Provider](https://www.pulumi.com/registry/packages/azure-native/api-docs/) to find the resources you want to create. \n\n1. Create a [resource group](https://www.pulumi.com/registry/packages/azure-native/api-docs/resources/resourcegroup/) named `rg-workshop` that will contain the resources you will create next.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in C#\u003c/summary\u003e\n\n```csharp\nvar resourceGroup = new ResourceGroup(\"workshop\");   \n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in TypeScript\u003c/summary\u003e\n\n```typescript\nimport {ResourceGroup} from \"@pulumi/azure-native/resources\";\n\nconst resourceGroup = new ResourceGroup(\"workshop\");\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in Python\u003c/summary\u003e\n\n```python\nimport pulumi_azure_native as azure_native\n\nresource_group = azure_native.resources.ResourceGroup(\"workshop\")\n```\n\u003c/details\u003e\n    \nWhen executing the `pulumi up` command, you will see that pulumi detects there is a new resource to create. Apply the update and verify the resource group is created.\n\n\u003e [!NOTE]  \n\u003e You don't have to specify a location for the resource group, by default it will use the location you previously specified in the configuration.\n\n2. [Configure the resource group](https://www.pulumi.com/registry/packages/azure-native/api-docs/resources/resourcegroup/#inputs) to have the tag `Type` with the value `Demo` and the tag `ProvisionedBy` with the value `Pulumi`. \n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in C#\u003c/summary\u003e\n\n  ```csharp\n  var resourceGroup = new ResourceGroup(\"workshop\", new()\n  {\n      Tags =\n      {\n          { \"Type\", \"Demo\" },\n          { \"ProvisionedBy\", \"Pulumi\" }\n      }\n  });\n  ```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in TypeScript\u003c/summary\u003e\n\n```typescript\nconst resourceGroup = new ResourceGroup(\"workshop\", {\n  tags: {\n    Type: \"demo\",\n    ProvisionedBy: \"Pulumi\"\n  }\n});\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in Python\u003c/summary\u003e\n\n```python\nresource_group = azure_native.resources.ResourceGroup(\n    \"workshop\",\n    tags={\n        \"Type\": \"Demo\",\n        \"ProvisionedBy\": \"Pulumi\",\n    }\n)\n```\n\u003c/details\u003e\n\nWhen updating the stack, you will see that pulumi detects the resource group needs to be updated.\n\nIt's a good practice to follow a [naming convention](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming). Like the name `rg-workshop-dev` where:\n - `rg` is the abbreviation for the resource type \"resource group\"\n - `workshop` is the name of the application/workload\n - `dev` is the name of the environment/stack\n\n3. Update the resource group name to `rg-workshop-dev` for your resource group.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in C#\u003c/summary\u003e\n\n```csharp\nvar stackName = Deployment.Instance.StackName;\nvar resourceGroup = new ResourceGroup($\"rg-workshop-{stackName}\", new()\n{\n    Tags =\n    {\n        { \"Type\", \"Demo\" },\n        { \"ProvisionedBy\", \"Pulumi\" }\n    }\n});\n```\n  The stack name is directly retrieved from Pulumi to avoid hardcoding it.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in TypeScript\u003c/summary\u003e\n\n```typescript\nconst stackName = pulumi.getStack()\nconst resourceGroup = new ResourceGroup(`rg-workshop-${stackName}`, {\n  tags: {\n    Type: \"demo\",\n    ProvisionedBy: \"Pulumi\"\n  }\n});\n```\nThe stack name is directly retrieved from Pulumi to avoid hardcoding it.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in Python\u003c/summary\u003e\n\n```python\nstack_name = pulumi.get_stack()\n\nresource_group = azure_native.resources.ResourceGroup(\n    f\"rg-workshop-{stack_name}\",\n    tags={\n        \"Type\": \"Demo\",\n        \"ProvisionedBy\": \"Pulumi\",\n    }\n)\n```\nThe stack name is directly retrieved from Pulumi to avoid hardcoding it.\n\u003c/details\u003e\n\n\nWhen updating the stack, you will see that pulumi detects the resource group needs to be recreated (delete the one with the old name and create a new one with the new name). Indeed, when some input properties of a resource change, it triggers a replacement of the resource. The input properties concerned are always specified in the documentation of each resource.\n\n\u003e [!NOTE]  \n\u003e You have seen that depending on what you do, updating the stack will result in creating, updating, or deleting resources. Instead of executing the `pulumi up` command each time you want to see the result of your changes, you can use the [`pulumi watch`](https://www.pulumi.com/docs/cli/commands/pulumi_watch/) command that will act as [hot reload for your infrastructure code](https://www.techwatching.dev/posts/pulumi-watch) (each time you make a change and save your code file, pulumi will detect it, build the code, and deploy the changes ). You can use that for the rest of the workshop or continue using `pulumi up -y` if you prefer.\n\nSometimes it's not easy to find the correct type for the resource we want to create. You can use the [`pulumi ai web`](https://www.pulumi.com/blog/pulumi-insights-ai-cli/#pulumi-ai-in-the-cli) command to use natural-language prompts to generate Pulumi infrastructure-as-code. \n\n4. Use pulumi ai to provision a free Web App/App Service.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand for C#\u003c/summary\u003e\n  \n```powershell\npulumi ai web -l C# \"Using Azure Native Provider, create a free App Service.\"\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand for TypeScript\u003c/summary\u003e\n\n```powershell\npulumi ai web -l typescript \"Using Azure Native Provider, create a free App Service.\"\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand for Python\u003c/summary\u003e\n\n```powershell\npulumi ai web -l python \"Using Azure Native Provider, create a free App Service.\"\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in C#\u003c/summary\u003e\n\n```csharp\nvar appServicePlan = new AppServicePlan($\"sp-workshop-{stackName}\", new()\n{\n    ResourceGroupName = resourceGroup.Name,\n    Sku = new SkuDescriptionArgs()\n    {\n        Name = \"F1\",\n    },\n});\n\nvar appService = new WebApp($\"app-workshop-{stackName}\", new()\n{\n    ResourceGroupName = resourceGroup.Name,\n    ServerFarmId = appServicePlan.Id,\n});\n```\n  An [App Service Plan](https://www.pulumi.com/registry/packages/azure-native/api-docs/web/appserviceplan/) is needed to create an [App Service](https://www.pulumi.com/registry/packages/azure-native/api-docs/web/webapp/). \n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in TypeScript\u003c/summary\u003e\n\n```typescript\nconst appServicePlan = new AppServicePlan(`sp-workshop-${stackName}`, {\n  resourceGroupName: resourceGroup.name,\n  sku: {\n    name: \"F1\",\n  },\n});\n\nconst appService = new WebApp(`app-workshop-${stackName}`, {\n  resourceGroupName: resourceGroup.name,\n  serverFarmId: appServicePlan.id,\n});\n```\nAn [App Service Plan](https://www.pulumi.com/registry/packages/azure-native/api-docs/web/appserviceplan/) is needed to create an [App Service](https://www.pulumi.com/registry/packages/azure-native/api-docs/web/webapp/).\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in Python\u003c/summary\u003e\n\n```python\napp_service_plan = azure_native.web.AppServicePlan(\n    f\"sp-workshop-{stack_name}\",\n    resource_group_name=resource_group.name\n    sku=azure_native.web.SkuDescriptionArgs(\n        name=\"F1\"\n    )\n)\n\napp_service = azure_native.web.WebApp(\n    f\"app-workshop-{stack_name}\",\n    resource_group_name=resource_group.name,\n    server_farm_id=app_service_plan.id\n)\n```\n\u003c/details\u003e\n\n\u003e [!NOTE]  \n\u003e To access properties from other resources, you can just use variables.\n\n5. Update the infrastructure to use the `AppServiceSku` setting from the configuration instead of hard coding the SKU `F1`.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in C#\u003c/summary\u003e\n\n```csharp\nvar appServiceSku = config.Require(\"AppServiceSku\");\nvar appServicePlan = new AppServicePlan($\"sp-workshop-{stackName}\", new()\n{\n    ResourceGroupName = resourceGroup.Name,\n    Sku = new SkuDescriptionArgs()\n    {\n        Name = appServiceSku,\n    },\n});\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in TypeScript\u003c/summary\u003e\n\n```typescript\nconst appServiceSku = config.require(\"AppServiceSku\")\nconst appServicePlan = new AppServicePlan(\"appServicePlan\", {\n  resourceGroupName: resourceGroup.name,\n  sku: {\n    name: appServiceSku,\n  },\n});\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in Python\u003c/summary\u003e\n\n```python\napp_service_sku = config.require(\"AppServiceSku\")\napp_service_plan = azure_native.web.AppServicePlan(\n    f\"sp-workshop-{stack_name}\",\n    resource_group_name=resource_group.name,\n    sku=azure_native.web.SkuDescriptionArgs(\n        name=app_service_sku\n    )\n)\n```\n\u003c/details\u003e\n\nNot only does the stack have outputs, but the resources themselves also have outputs, which are properties returned from the cloud provider. Since these values are only known once the resources have been provisioned, there are certain [considerations](https://www.pulumi.com/docs/concepts/inputs-outputs/#outputs) to keep in mind when using them in your program (particularly when performing computations based on an output).\n\n6. Modify the program to make the stack only return one output, that is the URL of the app service.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in C#\u003c/summary\u003e\n\n```csharp\nvar appService = new WebApp($\"app-workshop-{stackName}\", new WebAppArgs\n{\n    ResourceGroupName = resourceGroup.Name,\n    ServerFarmId = appServicePlan.Id,\n});\n\nreturn new Dictionary\u003cstring, object?\u003e\n{\n    [\"AppServiceUrl\"] = Output.Format($\"https://{appService.DefaultHostName}\")\n};\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in TypeScript\u003c/summary\u003e\n\n```typescript\nconst appService = new WebApp(\"appService\", {\n  resourceGroupName: resourceGroup.name,\n  serverFarmId: appServicePlan.id,\n});\n\nexport const appServiceUrl = pulumi.interpolate`https://${appService.defaultHostName}`;\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in Python\u003c/summary\u003e\n\n```python\napp_service = azure_native.web.WebApp(\n    f\"app-workshop-{stack_name}\",\n    resource_group_name=resource_group.name,\n    server_farm_id=app_service_plan.id\n)\n\npulumi.export(\"app_service_url\", app_service.default_host_name.apply(lambda hostname: f\"http://{hostname}\"))\n```\n\u003c/details\u003e\n\nSometimes, you need some data that are not available as properties of a resource. That's exactly what [provider functions](https://www.pulumi.com/docs/concepts/resources/functions/#provider-functions) are for. For instance, the [ListWebAppPublishingCredentialsOutput](https://www.pulumi.com/registry/packages/azure-native/api-docs/web/listwebapppublishingcredentials/) function can be used to retrieve the [publishing credentials](https://github.com/projectkudu/kudu/wiki/Deployment-credentials#site-credentials-aka-publish-profile-credentials) of an App Service\n\n7. Add 2 outputs to the stack `PublishingUsername` and `PublishingUserPassword` that are secrets that can be used to deploy a zip package to the App Service.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in C#\u003c/summary\u003e\n\n```csharp\nvar publishingCredentials = ListWebAppPublishingCredentials.Invoke(new()  \n{  \n    ResourceGroupName = resourceGroup.Name,  \n    Name = appService.Name  \n});\n\nreturn new Dictionary\u003cstring, object?\u003e\n{\n    [\"AppServiceUrl\"] = Output.Format($\"https://{appService.DefaultHostName}\"),\n    [\"PublishingUsername\"] = Output.CreateSecret(publishingCredentials.Apply(c =\u003e c.PublishingUserName)), \n    [\"PublishingUserPassword\"] = Output.CreateSecret(publishingCredentials.Apply(c =\u003e c.PublishingPassword)),\n};\n```\n  As the function outputs are not marked as secrets, you have to manually do it.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in TypeScript\u003c/summary\u003e\n\n```typescript\nconst publishingCredentials = listWebAppPublishingCredentialsOutput({\n  name: appService.name,\n  resourceGroupName: resourceGroup.name\n})\n\nexport const appServiceUrl = pulumi.interpolate`https://${appService.defaultHostName}`;\nexport const publishingUsername = pulumi.secret(publishingCredentials.publishingUserName)\nexport const publishingPassword = pulumi.secret(publishingCredentials.publishingPassword)\n```\nAs the function outputs are not marked as secrets, you have to manually do it.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCode in Python\u003c/summary\u003e\n\n```python\npublishing_credentials = azure_native.web.list_web_app_publishing_credentials(\n    resource_group_name=resource_group.name,\n    name=app_service.name\n)\n\npulumi.export(\"app_service_url\", app_service.default_host_name.apply(lambda hostname: f\"http://{hostname}\"))\npulumi.export(\"publishing_username\", Output.secret(publishing_credentials.publishing_user_name))\npulumi.export(\"publishing_userpassword\", Output.secret(publishing_credentials.publishing_password))\n```\nAs the function outputs are not marked as secrets, you have to manually do it.\n\u003c/details\u003e\n\n## Manage stacks\n\n- Use the `pulumi about` command to get some information about the current Pulumi environment.\nIt also displays information about the current stack.\n\n### Create a new stack\n\n- Use the [`pulumi stack init`](https://www.pulumi.com/docs/iac/cli/commands/pulumi_stack/) command to create a new `prod` stack.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand\u003c/summary\u003e\n\n```powershell\npulumi stack init prod\n```\n\u003c/details\u003e\n\nYou will be automatically switched to his new stack. You can switch back to the previous stack using the `pulumi stack select` command.\n\n- Switch back to the `dev` stack\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand\u003c/summary\u003e\n\n```powershell\npulumi stack select dev\n```\n\u003c/details\u003e\n\n- List the different stacks with the `pulumi stack ls` command.\n\n### Provision the infrastructure for a new environment\n\n- Select the `prod` stack and try to provision the infrastructure for this stack. It should fail because some configuration is missing.\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand\u003c/summary\u003e\n\n```powershell\npulumi stack select prod\npulumi up\n```\n\u003c/details\u003e\n\n- Add the missing configuration and provision the infrastructure\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand\u003c/summary\u003e\n\n```powershell\npulumi config set azure-native:location westeurope\npulumi config set --secret ExternalApiKey SecretToBeKeptVerySecure\npulumi config set AppServiceSku F1\npulumi up\n```\n\u003c/details\u003e\n\n\u003e [!NOTE]\n\u003e You are on another environment so you don't have to set the same values. You can use another sku, another default azure location, another secret value... \n\n\n### Delete resources and stack\n\nTo delete all the resources in the stack you can run the command `pulumi destroy`.\n\n- Delete the resources on the `prod` environment.\n\nIf you want to delete the stack itself with its configuration and deployment history you can run the command `pulumi stack rm` command.\n\n- Delete the `prod` stack\n\n\u003cdetails\u003e\n  \u003csummary\u003eCommand\u003c/summary\u003e\n\n```powershell\npulumi stack rm prod\n```\n\u003c/details\u003e\n\n## Next\n\nTo continue this lab and see more advanced features, you can check the next parts:\n- [Use Pulumi in CI/CD Pipelines with GitHub Actions](/CI_CD.md)\n- [Use existing infrastructure](/Existing%20infrastructure.md)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechwatching%2Fpulumi-azure-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftechwatching%2Fpulumi-azure-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechwatching%2Fpulumi-azure-workshop/lists"}