diff --git a/Instructions/advanced/01-enhance-workload-traffic-manager-test-azure-chaos-studio.md b/Instructions/advanced/01-enhance-workload-traffic-manager-test-azure-chaos-studio.md index 6ec1c69..20823da 100644 --- a/Instructions/advanced/01-enhance-workload-traffic-manager-test-azure-chaos-studio.md +++ b/Instructions/advanced/01-enhance-workload-traffic-manager-test-azure-chaos-studio.md @@ -17,7 +17,6 @@ You will learn how to: - Deploy Azure App Service web apps to two Azure regions - Enhance workload resiliency by using Traffic Manager - Test workload resiliency by using Azure Chaos Studio -- Remove the resources used in the labs > **Note:** In this lab, you will deploy two instances of a .NET web app into two Azure regions (East US and West US) and then create a resilient configuration that implements the load balancing functionality of Azure Traffic Manager between the two web app instances. You will then use Azure Chaos Studio to trigger a failure of one of the apps to test the resiliency of the load balancing implementation. You can adapt the regions used to ones closer to your location if preferred. @@ -72,10 +71,8 @@ The exercise consists of the following tasks: 1. From the **Bash** session in the **Cloud Shell** pane, run the following commands to clone your forked eShopOnWeb repository: > **Note:** Replace `{YOUR_GITHUB_USERNAME}` in the command below with your actual GitHub username. For example, if your GitHub username is `johnsmith`, the command would be: - > - > ```bash - > git clone https://github.com/johnsmith/eShopOnWeb.git - > ``` + > `git clone https://github.com/johnsmith/eShopOnWeb.git` + ```bash git clone https://github.com/{YOUR_GITHUB_USERNAME}/eShopOnWeb.git cd eShopOnWeb diff --git a/Instructions/advanced/02-implement-self-service-infrastructure-bicep.md b/Instructions/advanced/02-implement-self-service-infrastructure-bicep.md new file mode 100644 index 0000000..d15fd9d --- /dev/null +++ b/Instructions/advanced/02-implement-self-service-infrastructure-bicep.md @@ -0,0 +1,667 @@ +--- +lab: + topic: Advanced + title: "Implement Self-Service Infrastructure with Bicep" + description: "Learn how to implement Infrastructure as Code using Bicep for self-service platform engineering scenarios." +--- + +# Implement Self-Service Infrastructure with Bicep + +In this lab, you will learn how to implement Infrastructure as Code (IaC) using Bicep to create self-service infrastructure capabilities for development teams. You will create Bicep templates to deploy Azure resources, enforce governance through policies and tags, and implement automated scaling for platform engineering scenarios. + +You will learn how to: + +- Set up and use Bicep for Infrastructure as Code +- Create Bicep templates to define and deploy Azure resources +- Deploy Azure App Service with SQL Database backend using Bicep +- Enforce governance using tags and Azure policies +- Implement automated scaling with Bicep + +This lab takes approximately **20** minutes to complete. + +## Before you start + +To complete the lab, you need: + +- An Azure subscription to which you have at least the Contributor-level access. If you don't already have one, you can [sign up for one](https://azure.microsoft.com/). +- Access to Azure Cloud Shell or a local environment with Azure CLI installed. +- Basic knowledge of Azure services and Infrastructure as Code concepts. +- Basic familiarity with JSON and declarative syntax. + +> **Note:** This lab uses Azure Cloud Shell, which is accessible from the Azure portal and requires no local installation. However, you can also complete this lab using a local environment with Azure CLI and Visual Studio Code installed. + +## Create and deploy Bicep templates for Azure resources + +In this exercise, you will create a Bicep template to deploy an Azure App Service with a SQL Database backend. This template will include security best practices and governance controls. + +### Set up Bicep environment + +1. Start a web browser and navigate to the Azure portal at `https://portal.azure.com`. +1. When prompted to authenticate, sign in by using your Azure account. +1. In the Azure portal, select the **Cloud Shell** icon in the top menu bar. +1. If prompted to choose between **Bash** and **PowerShell**, select **Bash**. +1. If this is your first time using Cloud Shell, follow the prompts to create a storage account. +1. Once Cloud Shell is ready, verify that Bicep is available by running: + + ```bash + az bicep version + ``` + + > **Note:** Bicep comes pre-installed in Azure Cloud Shell. You should see output indicating the Bicep CLI version. + +1. To ensure you have the latest version, run: + + ```bash + az bicep upgrade + ``` + +### Create a Bicep template + +1. In the Cloud Shell, create a new Bicep file: + + ```bash + code main.bicep + ``` + +1. Copy and paste the following Bicep code into the file: + + ```bicep + param location string = 'eastus' + param appServicePlanName string = 'asp-bicep-${uniqueString(resourceGroup().id)}' + param webAppName string = 'app-bicep-${uniqueString(resourceGroup().id)}' + param storageAccountName string = 'stbicep${uniqueString(resourceGroup().id)}' + param sqlServerName string = 'sql-bicep-${uniqueString(resourceGroup().id)}' + param sqlDatabaseName string = 'bicepdb' + param sqlAdminUser string + @secure() + param sqlAdminPassword string + + targetScope = 'resourceGroup' + + // Storage Account + resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + accessTier: 'Hot' + allowBlobPublicAccess: false + minimumTlsVersion: 'TLS1_2' + } + tags: { + Environment: 'Development' + Owner: 'PlatformEngineering' + } + } + + // App Service Plan + resource appPlan 'Microsoft.Web/serverfarms@2023-01-01' = { + name: appServicePlanName + location: location + sku: { + name: 'F1' + tier: 'Free' + } + properties: { + reserved: false + } + tags: { + Environment: 'Development' + Owner: 'PlatformEngineering' + } + } + + // Web App + resource webApp 'Microsoft.Web/sites@2023-01-01' = { + name: webAppName + location: location + properties: { + serverFarmId: appPlan.id + httpsOnly: true + siteConfig: { + minTlsVersion: '1.2' + ftpsState: 'FtpsOnly' + } + } + tags: { + Environment: 'Development' + Owner: 'PlatformEngineering' + } + } + + // SQL Server + resource sqlServer 'Microsoft.Sql/servers@2023-05-01-preview' = { + name: sqlServerName + location: location + properties: { + administratorLogin: sqlAdminUser + administratorLoginPassword: sqlAdminPassword + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + } + tags: { + Environment: 'Development' + Owner: 'PlatformEngineering' + } + } + + // SQL Database + resource sqlDb 'Microsoft.Sql/servers/databases@2023-05-01-preview' = { + name: sqlDatabaseName + location: location + parent: sqlServer + properties: { + collation: 'SQL_Latin1_General_CP1_CI_AS' + maxSizeBytes: 2147483648 + requestedServiceObjectiveName: 'Basic' + } + tags: { + Environment: 'Development' + Owner: 'PlatformEngineering' + } + } + + // SQL Server Firewall Rule (Allow Azure Services) + resource sqlFirewallRule 'Microsoft.Sql/servers/firewallRules@2023-05-01-preview' = { + name: 'AllowAzureServices' + parent: sqlServer + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + // Outputs + output webAppUrl string = 'https://${webApp.properties.defaultHostName}' + output storageAccountName string = storage.name + output sqlServerFqdn string = sqlServer.properties.fullyQualifiedDomainName + ``` + + > **Note:** This template uses the `uniqueString()` function to generate unique names for resources, ensuring that deployments don't fail due to naming conflicts. + +1. Save the file by pressing **Ctrl+S** and then close the editor by pressing **Ctrl+Q**. + +### Deploy the Bicep template + +1. Create a resource group for the deployment: + + ```bash + az group create --name rg-bicep-infrastructure --location eastus + ``` + +1. Validate the Bicep template before deployment: + + ```bash + az deployment group validate \ + --resource-group rg-bicep-infrastructure \ + --template-file main.bicep \ + --parameters sqlAdminUser='bicepAdmin' sqlAdminPassword='SecureP@ssw0rd123!' + ``` + + > **Note:** Replace the password with a strong password that meets Azure SQL Database requirements. + +1. Deploy the template to the resource group: + + ```bash + az deployment group create \ + --resource-group rg-bicep-infrastructure \ + --template-file main.bicep \ + --parameters sqlAdminUser='bicepAdmin' sqlAdminPassword='SecureP@ssw0rd123!' + ``` + + > **Note:** The deployment may take 3-5 minutes to complete. + +1. Wait for the deployment to complete. You should see a confirmation message indicating successful deployment. + +1. View the deployment outputs: + + ```bash + az deployment group show \ + --resource-group rg-bicep-infrastructure \ + --name main \ + --query properties.outputs + ``` + +1. Verify the resources were created successfully by listing the resources in the resource group: + + ```bash + az resource list --resource-group rg-bicep-infrastructure --output table + ``` + +You have successfully deployed an Azure App Service with a SQL Database backend using a Bicep template. The template includes security best practices such as HTTPS enforcement, TLS version requirements, and proper tagging for governance. + +## Enforce governance with tags and Azure policies + +In a self-service infrastructure environment, it is essential to enforce governance to ensure compliance, cost control, and proper resource management. Azure tags and policies are two key mechanisms to achieve this. In this exercise, you will implement governance controls for your infrastructure deployments. + +### Verify resource tagging + +1. In the Cloud Shell, verify that the resources were deployed with the appropriate tags: + + ```bash + az resource list --resource-group rg-bicep-infrastructure \ + --query "[].{Name:name, Type:type, Tags:tags}" \ + --output table + ``` + +1. Check the tags on the resource group itself: + + ```bash + az group show --name rg-bicep-infrastructure --query tags + ``` + +1. Add tags to the resource group to identify ownership and environment: + + ```bash + az group update --name rg-bicep-infrastructure \ + --set tags.Environment='Development' tags.Owner='PlatformEngineering' tags.Project='BicepInfrastructure' + ``` + +1. Verify the tags were applied: + + ```bash + az group show --name rg-bicep-infrastructure --query tags + ``` + +### Create and implement Azure policies for governance + +1. Create a policy definition file to enforce required tags: + + ```bash + cat > tagging-policy.json << EOF + { + "if": { + "allOf": [ + { + "field": "type", + "notIn": [ + "Microsoft.Resources/subscriptions/resourceGroups" + ] + }, + { + "anyOf": [ + { + "not": { + "field": "tags[Environment]", + "exists": "true" + } + }, + { + "not": { + "field": "tags[Owner]", + "exists": "true" + } + } + ] + } + ] + }, + "then": { + "effect": "deny" + } + } + EOF + ``` + +1. Get your subscription ID: + + ```bash + SUBSCRIPTION_ID=$(az account show --query id --output tsv) + echo "Subscription ID: $SUBSCRIPTION_ID" + ``` + +1. Create the policy definition: + + ```bash + az policy definition create \ + --name 'require-tags-policy' \ + --display-name 'Require Environment and Owner tags' \ + --description 'Deny creation of resources without Environment and Owner tags' \ + --rules tagging-policy.json \ + --mode All + ``` + +1. Assign the policy to the resource group: + + ```bash + az policy assignment create \ + --name 'require-tags-assignment' \ + --display-name 'Require tags on resources' \ + --policy "/subscriptions/$SUBSCRIPTION_ID/providers/Microsoft.Authorization/policyDefinitions/require-tags-policy" \ + --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/rg-bicep-infrastructure" + ``` + +1. Verify the policy assignment: + + ```bash + az policy assignment list --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/rg-bicep-infrastructure" --output table + ``` + +### Test policy enforcement + +1. Create a simple Bicep template without required tags to test the policy: + + ```bash + cat > test-policy.bicep << EOF + param location string = 'eastus' + param storageAccountName string = 'testpolicy\${uniqueString(resourceGroup().id)}' + + resource testStorage 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + // Note: No tags defined - this should be blocked by policy + } + EOF + ``` + +1. Attempt to deploy the template without required tags: + + ```bash + az deployment group create \ + --resource-group rg-bicep-infrastructure \ + --template-file test-policy.bicep + ``` + + > **Note:** This deployment should fail due to the policy enforcement, demonstrating that the governance controls are working correctly. + +1. Now create a compliant version with the required tags: + + ```bash + cat > test-policy-compliant.bicep << EOF + param location string = 'eastus' + param storageAccountName string = 'testcompliant\${uniqueString(resourceGroup().id)}' + + resource testStorage 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + tags: { + Environment: 'Testing' + Owner: 'PlatformEngineering' + } + } + EOF + ``` + +1. Deploy the compliant template: + + ```bash + az deployment group create \ + --resource-group rg-bicep-infrastructure \ + --template-file test-policy-compliant.bicep + ``` + + > **Note:** This deployment should succeed because it includes the required tags. + +1. Clean up the test storage account: + + ```bash + az storage account delete \ + --name "testcompliant$(az group show --name rg-bicep-infrastructure --query id --output tsv | sed 's/.*\///' | tr -d '\n' | openssl dgst -sha1 | cut -c1-13)" \ + --resource-group rg-bicep-infrastructure \ + --yes + ``` + +You have successfully implemented and tested governance controls using Azure policies. The policy ensures that all resources deployed to the resource group must include the required Environment and Owner tags, enforcing organizational standards for resource management. + +## Implement automated scaling with Bicep + +In this exercise, you will enhance your Bicep template to include automated scaling capabilities for the Azure App Service. This will allow the platform to automatically adjust resources based on demand, improving performance and cost efficiency. + +### Create an enhanced Bicep template with autoscaling + +1. Create a new Bicep template that includes autoscaling capabilities: + + ```bash + cat > main-autoscale.bicep << 'EOF' + param location string = 'eastus' + param appServicePlanName string = 'asp-autoscale-${uniqueString(resourceGroup().id)}' + param webAppName string = 'app-autoscale-${uniqueString(resourceGroup().id)}' + param sqlServerName string = 'sql-autoscale-${uniqueString(resourceGroup().id)}' + param sqlDatabaseName string = 'autoscaledb' + param sqlAdminUser string + @secure() + param sqlAdminPassword string + + targetScope = 'resourceGroup' + + // App Service Plan (Standard tier required for autoscaling) + resource appPlan 'Microsoft.Web/serverfarms@2023-01-01' = { + name: appServicePlanName + location: location + sku: { + name: 'S1' + tier: 'Standard' + capacity: 1 + } + properties: { + perSiteScaling: false + maximumElasticWorkerCount: 10 + } + tags: { + Environment: 'Development' + Owner: 'PlatformEngineering' + } + } + + // Web App + resource webApp 'Microsoft.Web/sites@2023-01-01' = { + name: webAppName + location: location + properties: { + serverFarmId: appPlan.id + httpsOnly: true + siteConfig: { + minTlsVersion: '1.2' + ftpsState: 'FtpsOnly' + alwaysOn: true + } + } + tags: { + Environment: 'Development' + Owner: 'PlatformEngineering' + } + } + + // SQL Server + resource sqlServer 'Microsoft.Sql/servers@2023-05-01-preview' = { + name: sqlServerName + location: location + properties: { + administratorLogin: sqlAdminUser + administratorLoginPassword: sqlAdminPassword + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + } + tags: { + Environment: 'Development' + Owner: 'PlatformEngineering' + } + } + + // SQL Database + resource sqlDb 'Microsoft.Sql/servers/databases@2023-05-01-preview' = { + name: sqlDatabaseName + location: location + parent: sqlServer + properties: { + collation: 'SQL_Latin1_General_CP1_CI_AS' + maxSizeBytes: 2147483648 + requestedServiceObjectiveName: 'S1' + } + tags: { + Environment: 'Development' + Owner: 'PlatformEngineering' + } + } + + // Autoscale Settings + resource autoscaleSetting 'Microsoft.Insights/autoscalesettings@2022-10-01' = { + name: 'autoscale-${appServicePlanName}' + location: location + properties: { + targetResourceUri: appPlan.id + enabled: true + profiles: [ + { + name: 'defaultProfile' + capacity: { + minimum: '1' + maximum: '5' + default: '1' + } + rules: [ + { + metricTrigger: { + metricName: 'CpuPercentage' + metricResourceUri: appPlan.id + operator: 'GreaterThan' + threshold: 75 + timeAggregation: 'Average' + timeGrain: 'PT1M' + timeWindow: 'PT5M' + statistic: 'Average' + } + scaleAction: { + direction: 'Increase' + type: 'ChangeCount' + value: '1' + cooldown: 'PT5M' + } + } + { + metricTrigger: { + metricName: 'CpuPercentage' + metricResourceUri: appPlan.id + operator: 'LessThan' + threshold: 25 + timeAggregation: 'Average' + timeGrain: 'PT1M' + timeWindow: 'PT10M' + statistic: 'Average' + } + scaleAction: { + direction: 'Decrease' + type: 'ChangeCount' + value: '1' + cooldown: 'PT10M' + } + } + ] + } + ] + } + tags: { + Environment: 'Development' + Owner: 'PlatformEngineering' + } + } + + // Outputs + output webAppUrl string = 'https://${webApp.properties.defaultHostName}' + output sqlServerFqdn string = sqlServer.properties.fullyQualifiedDomainName + output autoscaleSettingName string = autoscaleSetting.name + EOF + ``` + +1. Validate the enhanced Bicep template: + + ```bash + az deployment group validate \ + --resource-group rg-bicep-infrastructure \ + --template-file main-autoscale.bicep \ + --parameters sqlAdminUser='autoscaleAdmin' sqlAdminPassword='SecureP@ssw0rd123!' + ``` + +### Deploy the autoscaling infrastructure + +1. Deploy the enhanced template with autoscaling capabilities: + + ```bash + az deployment group create \ + --resource-group rg-bicep-infrastructure \ + --template-file main-autoscale.bicep \ + --parameters sqlAdminUser='autoscaleAdmin' sqlAdminPassword='SecureP@ssw0rd123!' + ``` + + > **Note:** This deployment will upgrade the existing App Service Plan to Standard tier and add autoscaling rules. + +1. Verify the deployment completed successfully: + + ```bash + az deployment group show \ + --resource-group rg-bicep-infrastructure \ + --name main-autoscale \ + --query properties.provisioningState + ``` + +1. Check the autoscale settings were created: + + ```bash + az monitor autoscale list \ + --resource-group rg-bicep-infrastructure \ + --output table + ``` + +1. View the details of the autoscale configuration: + + ```bash + AUTOSCALE_NAME=$(az monitor autoscale list --resource-group rg-bicep-infrastructure --query "[0].name" --output tsv) + az monitor autoscale show \ + --resource-group rg-bicep-infrastructure \ + --name "$AUTOSCALE_NAME" \ + --query "{Name:name, Enabled:enabled, MinCapacity:profiles[0].capacity.minimum, MaxCapacity:profiles[0].capacity.maximum, Rules:length(profiles[0].rules)}" + ``` + +### Validate autoscaling configuration + +1. Navigate to the Azure portal and verify the autoscaling configuration: + + - Go to your resource group `rg-bicep-infrastructure` + - Select the App Service Plan + - In the left menu, select **Scale out (App Service plan)** + - Verify that autoscaling is enabled with the rules you defined + +1. View the current instance count and scaling history: + + ```bash + APP_SERVICE_PLAN=$(az appservice plan list --resource-group rg-bicep-infrastructure --query "[0].name" --output tsv) + az appservice plan show \ + --resource-group rg-bicep-infrastructure \ + --name "$APP_SERVICE_PLAN" \ + --query "{Name:name, Sku:sku.name, CurrentInstances:numberOfWorkers, MaxWorkers:maximumNumberOfWorkers}" + ``` + + > **Note:** To fully test autoscaling behavior, you would need to generate load on the application to trigger the CPU threshold. This could be done using load testing tools, but for this lab, verifying the configuration is sufficient. + +You have successfully implemented automated scaling for Azure App Service using Bicep. The autoscaling configuration will: + +- Scale out (add instances) when CPU usage exceeds 75% for 5 minutes +- Scale in (remove instances) when CPU usage falls below 25% for 10 minutes +- Maintain between 1 and 5 instances +- Include cooldown periods to prevent rapid scaling actions + +This ensures your applications can handle varying traffic loads efficiently while optimizing costs. + +## Clean up resources + +Now that you finished the exercise, you should delete the cloud resources you created to avoid unnecessary resource usage. + +1. In your browser navigate to the Azure portal [https://portal.azure.com](https://portal.azure.com); signing in with your Azure credentials if prompted. +1. Navigate to the resource group you created and view the contents of the resources used in this exercise. +1. On the toolbar, select **Delete resource group**. +1. Enter the resource group name and confirm that you want to delete it. + +> **CAUTION:** Deleting a resource group deletes all resources contained within it. If you chose an existing resource group for this exercise, any existing resources outside the scope of this exercise will also be deleted. diff --git a/Instructions/advanced/04-configure-pipelines-code-yaml.md b/Instructions/advanced/04-configure-pipelines-code-yaml.md new file mode 100644 index 0000000..fe8b282 --- /dev/null +++ b/Instructions/advanced/04-configure-pipelines-code-yaml.md @@ -0,0 +1,306 @@ +--- +lab: + topic: Advanced + title: "Configure Pipelines as Code with YAML" + description: "Learn how to define build and release pipelines using YAML, allowing access to pipeline features with markup files managed like source code." +--- + +# Configure Pipelines as Code with YAML + +**Estimated time:** 45 minutes + +You will learn how to define build and release pipelines using YAML, allowing you to access the same pipeline features as those using the visual designer but with a markup file that can be managed like any other source file. This includes configuring CI/CD pipelines as code with YAML in Azure DevOps, adding continuous delivery stages, and implementing environment approvals. + +## Before you start + +You need: + +- **Microsoft Edge** or an [Azure DevOps supported browser](https://docs.microsoft.com/azure/devops/server/compatibility) +- **Azure subscription:** You need an active Azure subscription or can create a new one +- **Azure DevOps organization:** Create one at [Create an organization or project collection](https://docs.microsoft.com/azure/devops/organizations/accounts/create-organization) if you don't have one +- **Account permissions:** You need a Microsoft account or Microsoft Entra account with: + - Owner role in the Azure subscription + - Global Administrator role in the Microsoft Entra tenant + - For details, see [List Azure role assignments using the Azure portal](https://docs.microsoft.com/azure/role-based-access-control/role-assignments-list-portal) and [View and assign administrator roles in Azure Active Directory](https://docs.microsoft.com/azure/active-directory/roles/manage-roles-portal) + +## Create and configure the team project + +First, you'll create an Azure DevOps project for this lab. + +1. In your browser, open your Azure DevOps organization +2. Click **New Project** +3. Give your project the name **eShopOnWeb_MultiStageYAML** +4. Leave other fields with defaults +5. Click **Create** + + ![Screenshot of the create new project panel](media/create-project.png) + +## Import the eShopOnWeb Git Repository + +Next, you'll import the sample repository that contains the application code. + +1. In your Azure DevOps organization, open the **eShopOnWeb_MultiStageYAML** project +2. Click **Repos > Files** +3. Click **Import a Repository** +4. Select **Import** +5. In the **Import a Git Repository** window, paste this URL: `https://github.com/MicrosoftLearning/eShopOnWeb.git` +6. Click **Import** + + ![Screenshot of the import repository panel](media/import-repo.png) + +The repository is organized this way: + +- **.ado** folder contains Azure DevOps YAML pipelines +- **.devcontainer** folder contains setup to develop using containers +- **infra** folder contains Bicep & ARM infrastructure as code templates +- **.github** folder contains YAML GitHub workflow definitions +- **src** folder contains the .NET 8 website used in the lab scenarios + +7. Go to **Repos > Branches** +8. Hover on the **main** branch then click the ellipsis on the right +9. Click **Set as default branch** + +> **Note**: If the main branch is already the default branch, this option will be grayed out. Continue with the instructions. + +## Create Azure resources + +You'll create an Azure web app to deploy your application to. + +1. From your lab computer, navigate to the [Azure Portal](https://portal.azure.com) +2. Sign in with the user account that has the Owner role in your Azure subscription +3. In the Azure portal toolbar, click the **Cloud Shell** icon (to the right of the search box) +4. If prompted to select either **Bash** or **PowerShell**, select **Bash** + +> **Note**: If this is your first time starting Cloud Shell and you see the "Getting started" pop-up, select "No storage account required" and your subscription, then click "Apply". + +5. From the Bash prompt, run these commands to create a resource group (replace `` with your preferred Azure region like 'centralus', 'westeurope'): + + ```bash + LOCATION='' + RESOURCEGROUPNAME='az400m03l07-RG' + az group create --name $RESOURCEGROUPNAME --location $LOCATION + ``` + +6. Create a Windows App service plan: + + ```bash + SERVICEPLANNAME='az400m03l07-sp1' + az appservice plan create --resource-group $RESOURCEGROUPNAME --name $SERVICEPLANNAME --sku B3 + ``` + +> **Note**: If you get an error about the subscription not being registered to use namespace 'Microsoft.Web', run: `az provider register --namespace Microsoft.Web` and then retry the command. + +7. Create a web app with a unique name: + + ```bash + WEBAPPNAME=eshoponWebYAML$RANDOM$RANDOM + az webapp create --resource-group $RESOURCEGROUPNAME --plan $SERVICEPLANNAME --name $WEBAPPNAME + ``` + +> **Note**: Record the name of the web app. You'll need it later in this lab. + +8. Close the Azure Cloud Shell, but leave the Azure Portal open + +## Configure CI/CD Pipelines as Code with YAML + +Now you'll configure CI/CD pipelines using YAML definitions. + +### Add a YAML build definition + +1. Navigate to the **Pipelines** section in Azure DevOps +2. In the **Create your first Pipeline** window, click **Create pipeline** +3. On the **Where is your code?** pane, click **Azure Repos Git (YAML)** +4. On the **Select a repository** pane, click **eShopOnWeb_MultiStageYAML** +5. On the **Configure your pipeline** pane, scroll down and select **Existing Azure Pipelines YAML File** +6. In the **Selecting an existing YAML File** blade, specify: + - Branch: **main** + - Path: **.ado/eshoponweb-ci.yml** +7. Click **Continue** to save these settings +8. From the **Review your Pipeline YAML** screen, click **Run** to start the Build Pipeline +9. Wait for the Build Pipeline to complete successfully + +> **Note**: Each task from the YAML file is available for review, including any warnings and errors. + +### Add continuous delivery to the YAML definition + +Now you'll add continuous delivery to enable automatic deployment. + +1. On the pipeline run pane, click the ellipsis symbol (⋯) in the upper right corner +2. In the dropdown menu, click **Edit pipeline** +3. Navigate to the end of the file (line 56) and press **Enter** to add a new empty line +4. On line **57**, add this content to define the **Release** stage: + + ```yaml + - stage: Deploy + displayName: Deploy to an Azure Web App + jobs: + - job: Deploy + pool: + vmImage: "windows-latest" + steps: + ``` + +5. Set the cursor on a new line at the end of the YAML definition +6. In the list of tasks on the right side, search for and select **Azure App Service Deploy** +7. In the **Azure App Service deploy** pane, specify these settings and click **Add**: + - **Azure subscription**: Select your Azure subscription, click **Authorize**, and authenticate when prompted + - **App Service name**: Select the web app you deployed earlier + - **Package or folder**: Update to `$(Build.ArtifactStagingDirectory)/**/Web.zip` + - Open **Application and Configuration Settings** and in **App settings** add: `-UseOnlyInMemoryDatabase true -ASPNETCORE_ENVIRONMENT Development` + +The added code should look similar to this: + +```yaml +- task: AzureRmWebAppDeployment@4 + inputs: + ConnectionType: "AzureRM" + azureSubscription: "AZURE SUBSCRIPTION HERE (b999999abc-1234-987a-a1e0-27fb2ea7f9f4)" + appType: "webApp" + WebAppName: "eshoponWebYAML369825031" + packageForLinux: "$(Build.ArtifactStagingDirectory)/**/Web.zip" + AppSettings: "-UseOnlyInMemoryDatabase true -ASPNETCORE_ENVIRONMENT Development" +``` + +8. Validate the task is listed as a child of the **steps** task. If not, select all lines from the added task and press **Tab** twice to indent it properly. + +> **Note**: By default, these two stages run independently. To make the build output available to the second stage, you need to add a task to download the deployment artifact. + +9. Place the cursor on the first line under the **steps** node of the **deploy** stage and press **Enter** to add a new empty line +10. On the **Tasks** pane, search for and select **Download build artifacts** +11. Specify these parameters: + - Download Artifacts produced by: **Current Build** + - Download Type: **Specific Artifact** + - Artifact Name: **Website** + - Destination Directory: **$(Build.ArtifactStagingDirectory)** +12. Click **Add** + +The added code should look like this: + +```yaml +- task: DownloadBuildArtifacts@1 + inputs: + buildType: "current" + downloadType: "single" + artifactName: "Website" + downloadPath: "$(Build.ArtifactStagingDirectory)" +``` + +13. If the YAML indentation is off, press **Tab** twice to indent it properly +14. Click **Validate and save**, then click **Save** again to commit the change to the main branch +15. Navigate to **Pipelines** and select **Pipelines** again +16. Open the **eShopOnWeb_MultiStageYAML** Pipeline and click **Run pipeline** +17. Confirm the **Run** from the appearing pane +18. Notice the 2 different stages: **Build .Net Core Solution** and **Deploy to Azure Web App** +19. Wait for the pipeline to complete the Build Stage successfully +20. When the Deploy Stage wants to start, you'll see a **Permissions Needed** prompt +21. Click **View** +22. From the **Waiting for Review** pane, click **Permit** +23. Validate the message and confirm by clicking **Permit** +24. Wait for the Deploy Stage to complete successfully + +### Review the deployed site + +1. Switch back to the Azure portal and navigate to your Azure web app +2. On the Azure web app blade, click **Overview** +3. On the overview blade, click **Browse** to open your site in a new browser tab +4. Verify that the deployed site loads as expected, showing the eShopOnWeb E-commerce website + +## Configure Environment settings for approvals + +YAML Pipelines don't have Release/Quality Gates like Classic Release Pipelines, but you can configure similar functionality using **Environments**. + +### Set up Pipeline Environments + +1. From your Azure DevOps Project **eShopOnWeb_MultiStageYAML**, navigate to **Pipelines** +2. Under the Pipelines menu on the left, select **Environments** +3. Click **Create Environment** +4. In the **New Environment** pane, add the name **approvals** +5. Under **Resources**, select **None** +6. Click **Create** +7. Once the environment is created, select the **Approvals and Checks** tab +8. From **Add your first check**, select **Approvals** +9. Add your Azure DevOps User Account Name to the **approvers** field +10. Click **Create** + +### Update the YAML pipeline for approvals + +1. Navigate to **Repos**, browse to the **.ado** folder, and select the **eshoponweb-ci.yml** file +2. Click the **Edit** button to switch to editing mode +3. Navigate to the start of the **Deploy job** (Line 60: `- job: Deploy`) +4. Add a new empty line right below and add: + + ```yaml + environment: approvals + ``` + +5. Since environment is a deployment stage setting, change `- job: Deploy` to `- deployment: Deploy` +6. Under the vmImage line, add a new empty line and paste this YAML snippet: + + ```yaml + strategy: + runOnce: + deploy: + ``` + +7. Select the remaining snippet (from the steps onward) and use **Tab** to fix the indentation + +The resulting YAML should look like this: + +```yaml +- stage: Deploy + displayName: Deploy to an Azure Web App + jobs: + - deployment: Deploy + environment: approvals + pool: + vmImage: "windows-latest" + strategy: + runOnce: + deploy: + steps: + - task: DownloadBuildArtifacts@1 + inputs: + buildType: "current" + downloadType: "single" + artifactName: "Website" + downloadPath: "$(Build.ArtifactStagingDirectory)" + - task: AzureRmWebAppDeployment@4 + inputs: + ConnectionType: "AzureRM" + azureSubscription: "AZURE SUBSCRIPTION HERE" + appType: "webApp" + WebAppName: "eshoponWebYAML369825031" + packageForLinux: "$(Build.ArtifactStagingDirectory)/**/Web.zip" + AppSettings: "-UseOnlyInMemoryDatabase true -ASPNETCORE_ENVIRONMENT Development" +``` + +8. Click **Commit** and **Commit** again to save the changes +9. Navigate to **Pipelines > Pipelines** and open the **EshopOnWeb_MultiStageYAML** Pipeline +10. Click **Run Pipeline** to trigger a new run +11. The Build Stage will complete as before +12. For the Deploy Stage, you'll see **Waiting (1 check in progress)** and a message about needing approval +13. Click the **Review** button +14. From the **Waiting for review** pane, click **Approve** +15. This allows the Deploy Stage to proceed and successfully deploy to the Azure Web App + +> **Note**: While this example only used approvals, other checks such as Azure Monitor, REST API, etc., can be used in a similar way. + +## Clean up resources + +Remember to delete the resources created in the Azure portal to avoid unnecessary charges: + +1. In the Azure portal, navigate to the **az400m03l07-RG** resource group +2. Click **Delete resource group** +3. Type the resource group name to confirm deletion +4. Click **Delete** + +## Summary + +In this lab, you configured CI/CD pipelines as code with YAML in Azure DevOps. You learned how to: + +- Define build and release pipelines using YAML +- Add continuous delivery stages to YAML pipelines +- Configure environments and approvals for deployment stages +- Deploy applications to Azure App Service using YAML pipelines + +Many teams prefer YAML pipelines because they can be managed like any other source file, providing better version control and code review capabilities for your CI/CD processes. diff --git a/Instructions/advanced/05-control-deployments-release-gates.md b/Instructions/advanced/05-control-deployments-release-gates.md new file mode 100644 index 0000000..56e6bb1 --- /dev/null +++ b/Instructions/advanced/05-control-deployments-release-gates.md @@ -0,0 +1,352 @@ +--- +lab: + topic: Advanced + title: "Control Deployments using Release Gates" + description: "Learn how to configure deployment gates and use them to control the execution of Azure Pipelines with environment-specific deployment criteria." +--- + +# Control Deployments using Release Gates + +**Estimated time:** 75 minutes + +You will learn how to configure deployment gates and use them to control the execution of Azure Pipelines. You'll configure a release definition with two environments for an Azure Web App, deploying to the DevTest environment only when there are no blocking bugs, and marking the DevTest environment complete only when there are no active alerts in Application Insights. + +## Before you start + +You need: + +- **Microsoft Edge** or an [Azure DevOps supported browser](https://docs.microsoft.com/azure/devops/server/compatibility) +- **Azure subscription:** You need an active Azure subscription or can create a new one +- **Azure DevOps organization:** Create one at [Create an organization or project collection](https://docs.microsoft.com/azure/devops/organizations/accounts/create-organization) if you don't have one +- **Account permissions:** You need a Microsoft account or Microsoft Entra account with: + - Owner role in the Azure subscription + - Global Administrator role in the Microsoft Entra tenant + - For details, see [List Azure role assignments using the Azure portal](https://docs.microsoft.com/azure/role-based-access-control/role-assignments-list-portal) and [View and assign administrator roles in Azure Active Directory](https://docs.microsoft.com/azure/active-directory/roles/manage-roles-portal) + +## About release gates + +A release pipeline specifies the end-to-end release process for an application to be deployed across various environments. Deployments to each environment are fully automated using jobs and tasks. It's a best practice to expose updates in a phased manner - expose them to a subset of users, monitor their usage, and expose them to other users based on the experience of the initial set. + +Approvals and gates enable you to take control over the start and completion of deployments in a release. You can wait for users to approve or reject deployments manually with approvals. Using release gates, you can specify application health criteria to be met before the release is promoted to the following environment. + +Gates can be added to an environment in the release definition from the pre-deployment conditions or the post-deployment conditions panel. Multiple gates can be added to ensure all inputs are successful for the release. + +There are 4 types of gates included by default: + +- **Invoke Azure Function:** Trigger execution of an Azure Function and ensure successful completion +- **Query Azure Monitor alerts:** Observe configured Azure Monitor alert rules for active alerts +- **Invoke REST API:** Make a call to a REST API and continue if it returns a successful response +- **Query work items:** Ensure the number of matching work items returned from a query is within a threshold + +## Create and configure the team project + +First, you'll create an Azure DevOps project for this lab. + +1. In your browser, open your Azure DevOps organization +2. Click **New Project** +3. Give your project the name **eShopOnWeb** +4. Leave other fields with defaults +5. Click **Create** + + ![Screenshot of the create new project panel](media/create-project.png) + +## Import the eShopOnWeb Git Repository + +Next, you'll import the sample repository that contains the application code. + +1. In your Azure DevOps organization, open the **eShopOnWeb** project +2. Click **Repos > Files** +3. Click **Import a Repository** +4. Select **Import** +5. In the **Import a Git Repository** window, paste this URL: `https://github.com/MicrosoftLearning/eShopOnWeb.git` +6. Click **Import** + + ![Screenshot of the import repository panel](media/import-repo.png) + +The repository is organized this way: + +- **.ado** folder contains Azure DevOps YAML pipelines +- **.devcontainer** folder contains setup to develop using containers +- **infra** folder contains Bicep & ARM infrastructure as code templates +- **.github** folder contains YAML GitHub workflow definitions +- **src** folder contains the .NET 8 website used in the lab scenarios + +7. Go to **Repos > Branches** +8. Hover on the **main** branch then click the ellipsis on the right +9. Click **Set as default branch** + +## Configure CI Pipeline + +You'll add a YAML build definition to the project. + +1. Navigate to the **Pipelines** section +2. In the **Create your first Pipeline** window, click **Create pipeline** +3. On the **Where is your code?** pane, click **Azure Repos Git (YAML)** +4. On the **Select a repository** pane, click **eShopOnWeb** +5. On the **Configure your pipeline** pane, scroll down and select **Existing Azure Pipelines YAML File** +6. In the **Selecting an existing YAML File** blade, specify: + - Branch: **main** + - Path: **.ado/eshoponweb-ci.yml** +7. Click **Continue** to save these settings +8. From the **Review your Pipeline YAML** screen, click **Run** to start the Build Pipeline +9. Wait for the Build Pipeline to complete successfully +10. Go to **Pipelines > Pipelines** and click on the recently created pipeline +11. Click on the ellipsis and **Rename/move** option +12. Name it **eshoponweb-ci** and click **Save** + +## Create Azure Resources for the Release Pipeline + +You'll create two Azure web apps representing the DevTest and Production environments. + +### Create two Azure web apps + +1. From your lab computer, navigate to the [Azure Portal](https://portal.azure.com) +2. Sign in with the user account that has the Owner role in your Azure subscription +3. In the Azure portal, click the **Cloud Shell** icon (to the right of the search box) +4. If prompted to select either **Bash** or **PowerShell**, select **Bash** + +> **Note**: If this is your first time starting Cloud Shell and you see the "You have no storage mounted" message, select your subscription and click "Apply". + +5. From the Bash prompt, run this command to create a resource group (replace `` with your preferred Azure region): + + ```bash + REGION='' + RESOURCEGROUPNAME='az400m03l08-RG' + az group create -n $RESOURCEGROUPNAME -l $REGION + ``` + +6. Create an App service plan: + + ```bash + SERVICEPLANNAME='az400m03l08-sp1' + az appservice plan create -g $RESOURCEGROUPNAME -n $SERVICEPLANNAME --sku S1 + ``` + +7. Create two web apps with unique names: + + ```bash + SUFFIX=$RANDOM$RANDOM + az webapp create -g $RESOURCEGROUPNAME -p $SERVICEPLANNAME -n RGATES$SUFFIX-DevTest + az webapp create -g $RESOURCEGROUPNAME -p $SERVICEPLANNAME -n RGATES$SUFFIX-Prod + ``` + +> **Note**: Record the name of the DevTest web app. You'll need it later. + +> **Note**: If you get an error about the subscription not being registered to use namespace 'Microsoft.Web', run: `az provider register --namespace Microsoft.Web` and wait for registration to complete. + +8. Wait for the provisioning process to complete and close the Cloud Shell pane + +### Configure an Application Insights resource + +1. In the Azure portal, search for **Application Insights** and select it from the results +2. On the **Application Insights** blade, select **+ Create** +3. On the **Basics** tab, specify these settings: + + | Setting | Value | + | -------------- | ------------------------------------------------------ | + | Resource group | **az400m03l08-RG** | + | Name | the name of the DevTest web app from the previous task | + | Region | the same Azure region where you deployed the web apps | + +4. Click **Review + create** and then click **Create** +5. Wait for the provisioning process to complete +6. Navigate to the resource group **az400m03l08-RG** +7. In the list of resources, click the **DevTest** web app +8. On the DevTest web app page, in the left menu under **Monitoring**, click **Application Insights** +9. Click **Turn on Application Insights** +10. In the **Change your resource** section, click **Select existing resource** +11. Select the newly created Application Insight resource +12. Click **Apply** and when prompted for confirmation, click **Yes** +13. Wait until the change takes effect + +### Create monitor alerts + +1. From the same **Application Insights** menu, select **View Application Insights Data** +2. On the Application Insights resource blade, under **Monitoring**, click **Alerts** +3. Click **Create > Alert rule** +4. In the **Condition** section, click **See all signals** +5. Type **Requests** and from the results, select **Failed Requests** +6. In the **Condition** section, leave **Threshold** set to **Static** and validate these defaults: + - Aggregation Type: Count + - Operator: Greater Than + - Unit: Count +7. In the **Threshold value** textbox, type **0** +8. Click **Next:Actions** +9. Don't make changes in Actions, and define these parameters under **Details**: + + | Setting | Value | + | ---------------------------------------------- | -------------------------------- | + | Severity | **2- Warning** | + | Alert rule name | **RGATESDevTest_FailedRequests** | + | Advanced Options: Automatically resolve alerts | **cleared** | + +10. Click **Review+Create**, then **Create** +11. Wait for the alert rule to be created successfully + +> **Note**: Metric alert rules might take up to 10 minutes to activate. + +## Configure the Release Pipeline + +You'll configure a release pipeline with deployment gates. + +### Set Up Release Tasks + +1. From the **eShopOnWeb** project in Azure DevOps, select **Pipelines** and then **Releases** +2. Click **New Pipeline** +3. From the **Select a template** window, choose **Azure App Service Deployment** under **Featured** templates +4. Click **Apply** +5. In the Stage window, update the default "Stage 1" name to **DevTest** +6. Close the popup window using the **X** button +7. On the top of the page, rename the pipeline from **New release pipeline** to **eshoponweb-cd** +8. Hover over the DevTest Stage and click the **Clone** button +9. Name the cloned stage **Production** + +> **Note**: The pipeline now contains two stages named **DevTest** and **Production**. + +10. On the **Pipeline** tab, select the **Add an Artifact** rectangle +11. Select **eshoponweb-ci** in the **Source (build pipeline)** field +12. Click **Add** to confirm the selection +13. From the **Artifacts** rectangle, click the **Continuous deployment trigger** (lightning bolt) +14. Click **Disabled** to toggle the switch and enable it +15. Leave all other settings at default and close the pane + +### Configure DevTest stage + +1. Within the **DevTest Environments** stage, click the **1 job, 1 task** label +2. In the **Azure subscription** dropdown, select your Azure subscription and click **Authorize** +3. Authenticate using the user account with the Owner role in the Azure subscription +4. Confirm the App Type is set to "Web App on Windows" +5. In the **App Service name** dropdown, select the name of the **DevTest** web app +6. Select the **Deploy Azure App Service** task +7. In the **Package or Folder** field, update the default value to: `$(System.DefaultWorkingDirectory)/**/Web.zip` +8. Open **Application and Configuration Settings** and enter this in **App settings**: `-UseOnlyInMemoryDatabase true -ASPNETCORE_ENVIRONMENT Development` + +### Configure Production stage + +1. Navigate to the **Pipeline** tab and within the **Production** Stage, click **1 job, 1 task** +2. Under the Tasks tab, in the **Azure subscription** dropdown, select the Azure subscription you used for the DevTest stage +3. In the **App Service name** dropdown, select the name of the **Prod** web app +4. Select the **Deploy Azure App Service** task +5. In the **Package or Folder** field, update the default value to: `$(System.DefaultWorkingDirectory)/**/Web.zip` +6. Open **Application and Configuration Settings** and enter this in **App settings**: `-UseOnlyInMemoryDatabase true -ASPNETCORE_ENVIRONMENT Development` +7. Click **Save** and in the Save dialog box, click **OK** + +### Test the release pipeline + +1. In the **Pipelines** section, click **Pipelines** +2. Click the **eshoponweb-ci** build pipeline and then click **Run Pipeline** +3. Accept the default settings and click **Run** to trigger the pipeline +4. Wait for the build pipeline to finish + +> **Note**: After the build succeeds, the release will be triggered automatically and the application will be deployed to both environments. + +5. In the **Pipelines** section, click **Releases** +6. On the **eshoponweb-cd** pane, click the entry representing the most recent release +7. Track the progress of the release and verify that deployment to both web apps completed successfully +8. Switch to the Azure portal, navigate to the **az400m03l08-RG** resource group +9. Click the **DevTest** web app, then click **Browse** +10. Verify that the web page loads successfully in a new browser tab +11. Repeat for the **Production** web app +12. Close the browser tabs displaying the EShopOnWeb web site + +## Configure Release Gates + +You'll set up Quality Gates in the release pipeline. + +### Configure pre-deployment gates for approvals + +1. In the Azure DevOps portal, open the **eShopOnWeb** project +2. In **Pipelines > Releases**, click **eshoponweb-cd** and then **Edit** +3. On the left edge of the **DevTest Environment** stage, click the oval shape representing **Pre-deployment conditions** +4. On the **Pre-deployment conditions** pane, set the **Pre-deployment approvals** slider to **Enabled** +5. In the **Approvers** text box, type and select your Azure DevOps account name + +> **Note**: In a real-life scenario, this should be a DevOps Team name alias instead of your own name. + +6. **Save** the pre-approval settings and close the popup window +7. Click **Create Release** and confirm by pressing **Create** +8. Notice "Release-2" has been created. Click the "Release-2" link to navigate to its details +9. Notice the **DevTest** Stage is in a **Pending Approval** state +10. Click the **Approve** button to trigger the DevTest Stage + +### Configure post-deployment gates for Azure Monitor + +1. Back on the **eshoponweb-cd** pane, on the right edge of the **DevTest Environment** stage, click the oval shape representing **Post-deployment conditions** +2. Set the **Gates** slider to **Enabled** +3. Click **+ Add** and select **Query Azure Monitor Alerts** +4. In the **Query Azure Monitor Alerts** section: + - **Azure subscription**: Select the service connection representing your Azure subscription + - **Resource group**: Select **az400m03l08-RG** +5. Expand the **Advanced** section and configure: + - Filter type: **None** + - Severity: **Sev0, Sev1, Sev2, Sev3, Sev4** + - Time Range: **Past Hour** + - Alert State: **Acknowledged, New** + - Monitor Condition: **Fired** +6. Expand **Evaluation options** and configure: + - **Time between re-evaluation of gates**: **5 Minutes** + - **Timeout after which gates fail**: **8 Minutes** + - Select **On successful gates, ask for approvals** + +> **Note**: The sampling interval and timeout work together so that gates will call their functions at suitable intervals and reject the deployment if they don't succeed within the timeout period. + +7. Close the **Post-deployment conditions** pane +8. Click **Save** and in the Save dialog box, click **OK** + +## Test Release Gates + +You'll test the release gates by updating the application and triggering a deployment. + +### Generate alerts and test the release process + +1. From the Azure Portal, browse to the **DevTest Web App** resource +2. From the Overview pane, notice the **URL** field showing the web application hyperlink +3. Click this link to open the eShopOnWeb web application +4. To simulate a **Failed Request**, add **/discount** to the URL, which will result in an error since that page doesn't exist +5. Refresh this page several times to generate multiple events +6. From the Azure Portal, search for **Application Insights** and select the **DevTest-AppInsights** resource +7. Navigate to **Alerts** +8. There should be at least **1** new alert with **Severity 2 - Warning** showing up + +> **Note**: If no Alert shows up yet, wait another few minutes. + +9. Return to the Azure DevOps Portal and open the **eShopOnWeb** Project +10. Navigate to **Pipelines > Releases** and select **eshoponweb-cd** +11. Click the **Create Release** button +12. Wait for the Release pipeline to start and **approve** the DevTest Stage release action +13. Wait for the DevTest release Stage to complete successfully +14. Notice how the **Post-deployment Gates** switches to **Evaluation Gates** status +15. Click the **Evaluation Gates** icon +16. For **Query Azure Monitor Alerts**, notice an initial failed state +17. Let the Release pipeline remain in pending state for the next 5 minutes +18. After 5 minutes pass, notice the 2nd evaluation failing again + +This is expected behavior, since there's an Application Insights Alert triggered for the DevTest Web App. + +> **Note**: Since there's an alert triggered by the exception, **Query Azure Monitor** gate will fail. This prevents deployment to the **Production** environment. + +19. Wait a couple more minutes and validate the status of the Release Gates again +20. Within a few minutes after the initial Release Gates check, since the initial Application Insight Alert was triggered with "Fired" action, it should result in a successful Release Gate, allowing deployment to the Production Release Stage + +> **Note**: If your gate fails, close the alert in Azure Monitor. + +## Clean up resources + +Remember to delete the resources created in the Azure portal to avoid unnecessary charges: + +1. In the Azure portal, navigate to the **az400m03l08-RG** resource group +2. Click **Delete resource group** +3. Type the resource group name to confirm deletion +4. Click **Delete** + +## Summary + +In this lab, you configured release pipelines and then configured and tested release gates. You learned how to: + +- Configure release pipelines with multiple environments +- Set up pre-deployment approvals for manual control +- Configure post-deployment gates with Azure Monitor alerts +- Test release gates by generating application alerts +- Control deployments based on application health criteria + +Release gates enable you to specify application health criteria that must be met before a release is promoted to the following environment, providing automated quality controls in your deployment pipeline. diff --git a/Instructions/advanced/06-integrate-azure-key-vault-azure-devops.md b/Instructions/advanced/06-integrate-azure-key-vault-azure-devops.md new file mode 100644 index 0000000..3a190a6 --- /dev/null +++ b/Instructions/advanced/06-integrate-azure-key-vault-azure-devops.md @@ -0,0 +1,228 @@ +--- +lab: + topic: Advanced + title: "Integrate Azure Key Vault with Azure DevOps" + description: "Learn how to integrate Azure Key Vault with an Azure Pipeline to securely store and retrieve sensitive data such as passwords and keys." +--- + +# Integrate Azure Key Vault with Azure DevOps + +**Estimated time:** 40 minutes + +You will learn how to integrate Azure Key Vault with an Azure Pipeline to securely store and retrieve sensitive data such as passwords and keys. You'll create an Azure Key Vault, store an Azure Container Registry password as a secret, and configure a pipeline to retrieve and use that secret for container deployment. + +## Before you start + +You need: + +- **Microsoft Edge** or an [Azure DevOps supported browser](https://learn.microsoft.com/azure/devops/server/compatibility) +- **Azure subscription:** You need an active Azure subscription or can create a new one +- **Azure DevOps organization:** Create one at [Create an organization or project collection](https://learn.microsoft.com/azure/devops/organizations/accounts/create-organization) if you don't have one + +## About Azure Key Vault + +Azure Key Vault provides secure storage and management of sensitive data, such as keys, passwords, and certificates. It includes support for hardware security modules, as well as a range of encryption algorithms and key lengths. By using Azure Key Vault, you can minimize the possibility of disclosing sensitive data through source code, which is a common mistake made by developers. Access to Azure Key Vault requires proper authentication and authorization, supporting fine-grained permissions to its content. + +## Create and configure the team project + +First, you'll create an Azure DevOps project for this lab. + +1. In your browser, open your Azure DevOps organization +2. Click **New Project** +3. Give your project the name **eShopOnWeb** +4. Leave other fields with defaults +5. Click **Create** + + ![Screenshot of the create new project panel](media/create-project.png) + +## Import the eShopOnWeb Git Repository + +Next, you'll import the sample repository that contains the application code. + +1. In your Azure DevOps organization, open the **eShopOnWeb** project +2. Click **Repos > Files** +3. Click **Import** +4. In the **Import a Git Repository** window, paste this URL: `https://github.com/MicrosoftLearning/eShopOnWeb.git` +5. Click **Import** + + ![Screenshot of the import repository panel](media/import-repo.png) + +The repository is organized this way: + +- **.ado** folder contains Azure DevOps YAML pipelines +- **.devcontainer** folder contains setup to develop using containers +- **infra** folder contains Bicep & ARM infrastructure as code templates +- **.github** folder contains YAML GitHub workflow definitions +- **src** folder contains the .NET 8 website used in the lab scenarios + +6. Go to **Repos > Branches** +7. Hover on the **main** branch then click the ellipsis on the right +8. Click **Set as default branch** + +## Setup CI pipeline to build eShopOnWeb container + +You'll create a CI pipeline that builds and pushes container images to an Azure Container Registry (ACR). + +### Setup and Run CI pipeline + +You'll import an existing CI YAML pipeline definition that creates an Azure Container Registry and builds/publishes container images. + +1. From your lab computer, navigate to the Azure DevOps **eShopOnWeb** project +2. Go to **Pipelines > Pipelines** and click **Create Pipeline** (or **New pipeline**) +3. On the **Where is your code?** window, select **Azure Repos Git (YAML)** +4. Select the **eShopOnWeb** repository +5. On the **Configure** section, choose **Existing Azure Pipelines YAML file** +6. Select branch: **main** +7. Provide the path: **/.ado/eshoponweb-ci-dockercompose.yml** +8. Click **Continue** + + ![Screenshot of the existing pipeline YAML](media/select-ci-container-compose.png) + +9. In the YAML pipeline definition, customize your Resource Group name by replacing **NAME** in **AZ400-EWebShop-NAME** with a unique value +10. Replace **YOUR-SUBSCRIPTION-ID** with your own Azure subscription ID +11. Click **Save and Run** and wait for the pipeline to execute successfully + +> **Important**: If you see the message "This pipeline needs permission to access resources before this run can continue to Docker Compose to ACI", click on View, Permit and Permit again. This is needed to allow the pipeline to create the resource. + +The build definition consists of these tasks: + +- **AzureResourceManagerTemplateDeployment** uses **bicep** to deploy an Azure Container Registry +- **PowerShell** task takes the bicep output (ACR login server) and creates pipeline variable +- **DockerCompose** task builds and pushes the container images for eShopOnWeb to the Azure Container Registry + +12. Your pipeline will take a name based on the project name. Let's **rename** it for better identification +13. Go to **Pipelines > Pipelines** and click on the recently created pipeline +14. Click on the ellipsis and **Rename/Remove** option +15. Name it **eshoponweb-ci-dockercompose** and click **Save** + +16. Once execution is finished, in the Azure Portal, open the previously defined Resource Group +17. You should find an Azure Container Registry (ACR) with the created container images **eshoppublicapi** and **eshopwebmvc** + +![Screenshot of container images in ACR](media/azure-container-registry.png) + +18. Click on **Access Keys**, enable the **Admin user** if not done already, and copy the **password** value + +![Screenshot of the ACR password location](media/acr-password.png) + +You'll use this password in the following task as a secret in Azure Key Vault. + +## Create an Azure Key Vault + +You'll create an Azure Key Vault to store the ACR password as a secret. + +1. In the Azure portal, in the **Search resources, services, and docs** text box, type **Key vault** and press **Enter** +2. Select **Key vault** blade, click **Create > Key Vault** +3. On the **Basics** tab of the **Create a key vault** blade, specify these settings and click **Next**: + + | Setting | Value | + | ----------------------------- | --------------------------------------------------------------- | + | Subscription | the name of the Azure subscription you are using in this lab | + | Resource group | the name of your resource group **AZ400-EWebShop-NAME** | + | Key vault name | any unique valid name, like **ewebshop-kv-NAME** (replace NAME) | + | Region | an Azure region close to the location of your lab environment | + | Pricing tier | **Standard** | + | Days to retain deleted vaults | **7** | + | Purge protection | **Disable purge protection** | + +4. On the **Access configuration** tab, select **Vault access policy** +5. In the **Access policies** section, click **+ Create** to setup a new policy + +> **Note**: You need to secure access to your key vaults by allowing only authorized applications and users. To access the data from the vault, you need to provide read (Get/List) permissions to the service connection for authentication in the pipeline. + +6. On the **Permission** blade, below **Secret permissions**, check **Get** and **List** permissions +7. Click **Next** +8. On the **Principal** blade, search for your **Azure subscription service connection** +9. Select it from the list and click **Next**, **Next**, **Create** (access policy) +10. Back on the **Create a key vault** blade, click **Review + Create > Create** + +> **Note**: Wait for the Azure Key Vault to be provisioned. This should take less than 1 minute. + +11. On the **Your deployment is complete** blade, click **Go to resource** +12. On the Azure Key Vault blade, in the vertical menu on the left side, in the **Objects** section, click **Secrets** +13. On the **Secrets** blade, click **Generate/Import** +14. On the **Create a secret** blade, specify these settings and click **Create**: + +| Setting | Value | +| -------------- | ------------------------------------------- | +| Upload options | **Manual** | +| Name | **acr-secret** | +| Secret value | ACR access password copied in previous task | + +## Create a Variable Group connected to Azure Key Vault + +You'll create a Variable Group in Azure DevOps that will retrieve the ACR password secret from Key Vault. + +1. On your lab computer, navigate to the Azure DevOps project **eShopOnWeb** +2. In the vertical navigational pane, select **Pipelines > Library** +3. Click **+ Variable Group** +4. On the **New variable group** blade, specify these settings: + + | Setting | Value | + | ------------------------------------ | --------------------------------------------------- | + | Variable Group Name | **eshopweb-vg** | + | Link secrets from an Azure Key Vault | **enable** | + | Azure subscription | **Available Azure service connection > Azure subs** | + | Key vault name | Your key vault name | + +5. Under **Variables**, click **+ Add** and select the **acr-secret** secret +6. Click **OK** +7. Click **Save** + + ![Screenshot of the variable group creation](media/vg-create.png) + +## Setup CD Pipeline to deploy container in Azure Container Instance (ACI) + +You'll import a CD pipeline, customize it, and run it to deploy the container image in an Azure Container Instance. + +1. From your lab computer, navigate to the Azure DevOps **eShopOnWeb** project +2. Go to **Pipelines > Pipelines** and click **New Pipeline** +3. On the **Where is your code?** window, select **Azure Repos Git (YAML)** +4. Select the **eShopOnWeb** repository +5. On the **Configure** section, choose **Existing Azure Pipelines YAML file** +6. Select branch: **main** +7. Provide the path: **/.ado/eshoponweb-cd-aci.yml** +8. Click **Continue** + +9. In the YAML pipeline definition, customize: + + - **YOUR-SUBSCRIPTION-ID** with your Azure subscription id + - **az400eshop-NAME** replace NAME to make it globally unique + - **YOUR-ACR.azurecr.io** and **ACR-USERNAME** with your ACR login server (both need the ACR name, can be reviewed on the ACR > Access Keys) + - **AZ400-EWebShop-NAME** with the resource group name defined before in the lab + +10. Click **Save and Run** +11. Open the pipeline and wait for it to execute successfully + +> **Important**: If you see the message "This pipeline needs permission to access resources before this run can continue to Docker Compose to ACI", click on View, Permit and Permit again. This is needed to allow the pipeline to create the resource. + +The CD definition consists of these tasks: + +- **Resources**: prepared to automatically trigger based on CI pipeline completion. It also downloads the repository for the bicep file +- **Variables (for Deploy stage)** connects to the variable group to consume the Azure Key Vault secret **acr-secret** +- **AzureResourceManagerTemplateDeployment** deploys the Azure Container Instance (ACI) using bicep template and provides the ACR login parameters to allow ACI to download the previously created container image from Azure Container Registry (ACR) + +12. To verify the results of the pipeline deployment, in the Azure portal, search for and select the **AZ400-EWebShop-NAME** resource group +13. In the list of resources, verify that the **az400eshop** container instance was created by the pipeline +14. Rename the pipeline to **eshoponweb-cd-aci** for better identification + +## Clean up resources + +Remember to delete the resources created in the Azure portal to avoid unnecessary charges: + +1. In the Azure portal, navigate to the **AZ400-EWebShop-NAME** resource group +2. Click **Delete resource group** +3. Type the resource group name to confirm deletion +4. Click **Delete** + +## Summary + +In this lab, you integrated Azure Key Vault with Azure DevOps pipeline by: + +- Creating an Azure Key Vault to store an ACR password as a secret +- Providing access to secrets in the Azure Key Vault +- Configuring permissions to read the secret +- Configuring a pipeline to retrieve the password from the Azure Key Vault and pass it to subsequent tasks +- Deploying a container image to Azure Container Instance (ACI) using the secret +- Creating a Variable Group connected to Azure Key Vault + +Azure Key Vault integration with Azure DevOps enables secure handling of sensitive data in your CI/CD pipelines, following security best practices by keeping secrets out of your source code and configuration files. diff --git a/Instructions/advanced/07-enable-dynamic-configuration-feature-flags.md b/Instructions/advanced/07-enable-dynamic-configuration-feature-flags.md new file mode 100644 index 0000000..c55c9e5 --- /dev/null +++ b/Instructions/advanced/07-enable-dynamic-configuration-feature-flags.md @@ -0,0 +1,274 @@ +--- +lab: + topic: Advanced + title: "Enable Dynamic Configuration and Feature Flags" + description: "Learn how to use Azure App Configuration to manage application settings and feature flags centrally with dynamic feature toggling." +--- + +# Enable Dynamic Configuration and Feature Flags + +**Estimated time:** 45 minutes + +You will learn how to use Azure App Configuration to manage application settings and feature flags centrally. You'll explore how modern cloud applications can benefit from centralized configuration management and dynamic feature toggling without requiring application redeployment. + +## Before you start + +You need: + +- **Microsoft Edge** or an [Azure DevOps supported browser](https://learn.microsoft.com/azure/devops/server/compatibility) +- **Azure subscription:** You need an active Azure subscription or can create a new one +- **Azure DevOps organization:** Create one at [Create an organization or project collection](https://learn.microsoft.com/azure/devops/organizations/accounts/create-organization?view=azure-devops) if you don't have one +- **Account permissions:** You need a Microsoft account or Microsoft Entra account with Contributor or Owner role in the Azure subscription + - For details, see [List Azure role assignments using the Azure portal](https://learn.microsoft.com/azure/role-based-access-control/role-assignments-list-portal) and [View and assign administrator roles in Azure Active Directory](https://learn.microsoft.com/azure/active-directory/roles/manage-roles-portal) + +## About Azure App Configuration + +[Azure App Configuration](https://learn.microsoft.com/azure/azure-app-configuration/overview) provides a service to manage application settings and feature flags centrally. Modern programs, especially those running in a cloud, generally have many distributed components. Spreading configuration settings across these components can lead to hard-to-troubleshoot errors during application deployment. Use App Configuration to store all the settings for your application and secure their access in one place. + +Key benefits include: + +- **Centralized management** of application settings and feature flags +- **Dynamic configuration** changes without application restart +- **Feature flag management** for controlled feature rollouts +- **Point-in-time configuration** snapshots for rollback scenarios +- **Integration** with Azure Key Vault for sensitive settings + +## Create and configure the team project + +First, you'll create an Azure DevOps project for this lab. + +1. In your browser, open your Azure DevOps organization +2. Click **New Project** +3. Give your project the name **eShopOnWeb** +4. Choose **Scrum** on the **Work Item process** dropdown +5. Click **Create** + +## Import the eShopOnWeb Git Repository + +Next, you'll import the sample repository that contains the application code. + +1. In your Azure DevOps organization, open the **eShopOnWeb** project +2. Click **Repos > Files** +3. Click **Import** +4. In the **Import a Git Repository** window, paste this URL: `https://github.com/MicrosoftLearning/eShopOnWeb.git` +5. Click **Import** + +The repository is organized this way: + +- **.ado** folder contains Azure DevOps YAML pipelines +- **src** folder contains the .NET 8 website used in the lab scenarios + +6. Go to **Repos > Branches** +7. Hover on the **main** branch then click the ellipsis on the right +8. Click **Set as default branch** + +## Import and run CI/CD Pipelines + +You'll import CI/CD pipelines to build and deploy the eShopOnWeb application. The CI pipeline is prepared to build the application and run tests. The CD pipeline will deploy the application to an Azure Web App. + +### Import and run the CI pipeline + +Let's start by importing the CI pipeline named [eshoponweb-ci.yml](https://github.com/MicrosoftLearning/eShopOnWeb/blob/main/.ado/eshoponweb-ci.yml). + +1. Go to **Pipelines > Pipelines** +2. Click **Create Pipeline** (if there are no pipelines) or **New pipeline** (if there are already created pipelines) +3. Select **Azure Repos Git (Yaml)** +4. Select the **eShopOnWeb** repository +5. Select **Existing Azure Pipelines YAML File** +6. Select the **main** branch and the **/.ado/eshoponweb-ci.yml** file +7. Click **Continue** +8. Click the **Run** button to run the pipeline +9. Your pipeline will take a name based on the project name. Let's **rename** it for better identification +10. Go to **Pipelines > Pipelines** and click on the recently created pipeline +11. Click on the ellipsis and **Rename/Remove** option +12. Name it **eshoponweb-ci** and click **Save** + +### Import and run the CD pipeline + +Let's import the CD pipeline named [eshoponweb-cd-webapp-code.yml](https://github.com/MicrosoftLearning/eShopOnWeb/blob/main/.ado/eshoponweb-cd-webapp-code.yml). + +1. Go to **Pipelines > Pipelines** +2. Click **New pipeline** +3. Select **Azure Repos Git (Yaml)** +4. Select the **eShopOnWeb** repository +5. Select **Existing Azure Pipelines YAML File** +6. Select the **main** branch and the **/.ado/eshoponweb-cd-webapp-code.yml** file +7. Click **Continue** +8. In the YAML pipeline definition, set the variable section: + - **resource-group**: the name of the resource group, for example **rg-az400-container-NAME** (replace NAME) + - **location**: the name of the Azure region, for example **southcentralus** + - **templateFile**: **'infra/webapp.bicep'** + - **subscriptionid**: your Azure subscription id + - **azureserviceconnection**: **'azure subs'** + - **webappname**: the globally unique name of the web app, for example **az400-webapp-NAME** +9. Click **Save and Run** +10. Open the pipeline and wait for it to execute successfully + +> **Important**: If you see the message "This pipeline needs permission to access resources before this run can continue", click on View, Permit and Permit again. + +11. Rename the pipeline to **eshoponweb-cd-webapp-code** for better identification + +## Create Azure App Configuration service + +You'll create the Azure App Configuration service to centrally store the application configuration and feature flags. + +1. In the Azure portal, search for **App Configuration** and click **Create app configuration** +2. Select or create a resource group +3. Specify the location for the app configuration resource +4. Enter a name for the configuration store (must be globally unique) +5. Select the **Standard** pricing tier for this lab (required for feature flags) +6. Click **Review + create** then **Create** +7. Once the resource is created, go to the resource + +## Set up configuration keys in App Configuration + +You'll add configuration keys that your application will consume. + +1. In the left pane of the App Configuration service, select **Configuration explorer** +2. Click **Create > Key-value** and add: + - **Key**: eShopOnWeb:Settings:ShowPipelineInfo + - **Value**: true + - **Label**: leave empty + - **Content type**: leave empty +3. Click **Apply** and repeat the process to add these keys: + - **Key**: eShopOnWeb:Settings:ShowImageDevVersion, **Value**: false + - **Key**: eShopOnWeb:Settings:ShowImageProdVersion, **Value**: true + +## Set up feature flags in App Configuration + +You'll create feature flags to control application features dynamically. + +1. In the left pane of the App Configuration service, select **Feature manager** +2. Click **Create** and add: + - **Enable feature flag**: checked + - **Feature flag name**: ShoppingCart + - **Label**: leave empty + - **Description**: Enable the shopping cart feature +3. Click **Apply** +4. Repeat to create another feature flag: + - **Feature flag name**: Pipeline + - **Description**: Enable the pipeline information display + +## Configure the application to use App Configuration + +You'll modify the application to connect to Azure App Configuration. + +### Add App Configuration connection string + +1. In the Azure portal, go to your App Configuration resource +2. Select **Access keys** from the left menu +3. Copy the **Primary** connection string +4. Go to your Azure Web App resource (created by the CD pipeline) +5. In the left menu, select **Configuration** +6. Click **New application setting** and add: + - **Name**: ConnectionStrings:AppConfig + - **Value**: [paste the App Configuration connection string] + - **Deployment slot setting**: leave unchecked +7. Click **OK** then **Save** + +### Update application code + +The sample application is already configured to use Azure App Configuration. The key integration points are: + +1. **Program.cs** - The application is configured to use App Configuration: + + ```csharp + builder.Host.ConfigureAppConfiguration((hostingContext, config) => + { + var settings = config.Build(); + config.AddAzureAppConfiguration(options => + { + options.Connect(settings.GetConnectionString("AppConfig")) + .UseFeatureFlags(); + }); + }); + ``` + +2. **Views** - The application uses feature flags to conditionally show content: + ```html + +
Shopping cart feature is enabled!
+
+ ``` + +## Test dynamic configuration and feature flags + +You'll test the dynamic configuration capabilities by changing settings without redeploying the application. + +### Test configuration changes + +1. Navigate to your deployed web application URL +2. Observe the current display of pipeline information +3. Go back to App Configuration in the Azure portal +4. In **Configuration explorer**, find the **eShopOnWeb:Settings:ShowPipelineInfo** key +5. Change its value from **true** to **false** +6. Click **Apply** +7. Refresh your web application (may take up to 30 seconds to refresh) +8. Notice that the pipeline information is no longer displayed + +### Test feature flag changes + +1. In your web application, observe whether the shopping cart feature is displayed +2. Go back to App Configuration in the Azure portal +3. In **Feature manager**, find the **ShoppingCart** feature flag +4. Toggle its state (enable/disable) +5. Click **Apply** +6. Refresh your web application +7. Notice that the shopping cart feature appears or disappears based on the flag state + +## Advanced feature flag scenarios + +Feature flags support more advanced scenarios: + +### Conditional activation + +1. In the Azure portal, go to your App Configuration **Feature manager** +2. Click on the **Pipeline** feature flag +3. Click **Add filter** +4. Select **Targeting filter** +5. Configure percentage-based rollout: + - **Default percentage**: 50 + - **Groups**: Leave empty for this demo +6. Click **Apply** + +This configuration will show the feature to 50% of users randomly. + +### Time-based activation + +1. Create a new feature flag called **SpecialOffer** +2. Add a **Time Window** filter +3. Set a start and end time for when the feature should be active +4. This allows you to automatically enable/disable features based on time + +## Monitor App Configuration usage + +You can monitor how your application uses App Configuration: + +1. In the Azure portal, go to your App Configuration resource +2. Select **Monitoring** from the left menu +3. Click **Metrics** to see: + - **Requests** - Number of configuration requests + - **Throttled requests** - Requests that were throttled + - **Storage utilization** - How much storage is being used + +## Clean up resources + +Remember to delete the resources created in the Azure portal to avoid unnecessary charges: + +1. Delete the resource group containing your App Configuration and Web App resources +2. In the Azure portal, navigate to your resource group +3. Click **Delete resource group** +4. Type the resource group name to confirm deletion +5. Click **Delete** + +## Summary + +In this lab, you learned how to: + +- **Enable dynamic configuration** using Azure App Configuration +- **Manage feature flags** for controlled feature rollouts +- **Configure applications** to consume centralized configuration +- **Test configuration changes** without application redeployment +- **Implement advanced feature flag scenarios** like percentage rollouts and time-based activation + +Azure App Configuration provides a powerful way to manage application settings and feature flags centrally, enabling dynamic configuration changes and controlled feature rollouts without requiring application redeployment. This leads to more flexible and maintainable cloud applications. diff --git a/Instructions/advanced/08-deployments-azure-bicep-templates.md b/Instructions/advanced/08-deployments-azure-bicep-templates.md new file mode 100644 index 0000000..81024df --- /dev/null +++ b/Instructions/advanced/08-deployments-azure-bicep-templates.md @@ -0,0 +1,243 @@ +--- +lab: + topic: Advanced + title: "Deployments using Azure Bicep templates" + description: "Learn how to create an Azure Bicep template and modularize it using Azure Bicep Modules concept with Azure YAML pipelines." +--- + +# Deployments using Azure Bicep templates + +**Estimated time:** 45 minutes + +You will learn how to create an Azure Bicep template and modularize it using the Azure Bicep Modules concept. You'll modify the main deployment template to use the module and deploy all the resources to Azure using Azure YAML pipelines. + +## Before you start + +You need: + +- **Microsoft Edge** or an [Azure DevOps supported browser](https://docs.microsoft.com/azure/devops/server/compatibility) +- **Azure subscription:** You need an active Azure subscription or can create a new one +- **Azure DevOps organization:** Create one at [Create an organization or project collection](https://docs.microsoft.com/azure/devops/organizations/accounts/create-organization) if you don't have one +- **Account permissions:** You need a Microsoft account or Microsoft Entra account with: + - Owner role in the Azure subscription + - Global Administrator role in the Microsoft Entra tenant + - For details, see [List Azure role assignments using the Azure portal](https://docs.microsoft.com/azure/role-based-access-control/role-assignments-list-portal) and [View and assign administrator roles in Azure Active Directory](https://docs.microsoft.com/azure/active-directory/roles/manage-roles-portal) + +## About Azure Bicep + +Azure Bicep is a domain-specific language (DSL) that uses declarative syntax to deploy Azure resources. It provides a more concise syntax compared to JSON templates while maintaining all the capabilities of Azure Resource Manager (ARM) templates. + +Key benefits of Bicep: + +- **Simplified syntax:** More readable and easier to write than JSON ARM templates +- **Modular design:** Support for modules to create reusable components +- **Strong typing:** IntelliSense support and compile-time validation +- **No state management:** Declarative syntax means you describe what you want, not how to get there + +## Create and configure the team project + +First, you'll create an Azure DevOps project for this lab. + +1. In your browser, open your Azure DevOps organization +2. Click **New Project** +3. Give your project the name **eShopOnWeb** +4. Leave other fields with defaults +5. Click **Create** + + ![Screenshot of the create new project panel](media/create-project.png) + +## Import the eShopOnWeb Git Repository + +Next, you'll import the sample repository that contains the application code and infrastructure templates. + +1. In your Azure DevOps organization, open the **eShopOnWeb** project +2. Click **Repos > Files** +3. Click **Import a Repository** +4. Select **Import** +5. In the **Import a Git Repository** window, paste this URL: `https://github.com/MicrosoftLearning/eShopOnWeb.git` +6. Click **Import** + + ![Screenshot of the import repository panel](media/import-repo.png) + +The repository is organized this way: + +- **.ado** folder contains Azure DevOps YAML pipelines +- **.devcontainer** folder contains setup to develop using containers +- **infra** folder contains Bicep & ARM infrastructure as code templates used in lab scenarios +- **.github** folder contains YAML GitHub workflow definitions +- **src** folder contains the .NET 8 website used in the lab scenarios + +7. Go to **Repos > Branches** +8. Hover on the **main** branch then click the ellipsis on the right +9. Click **Set as default branch** + +## Understand an Azure Bicep template and simplify it using a reusable module + +You'll review an Azure Bicep template and simplify it using a reusable module. + +### Create Azure Bicep template + +You'll use the Azure DevOps editor to examine an Azure Bicep template. + +1. In your Azure DevOps project, navigate to **Repos** and **Files** +2. Open the `infra` folder and find the `simple-windows-vm.bicep` file + + ![Screenshot of the simple-windows-vm.bicep file path](media/browsebicepfile.png) + +3. Review the template to get a better understanding of its structure + +The template includes: + +- **Parameters** with types, default values and validation +- **Variables** for computed values +- **Resources** with these types: + - Microsoft.Storage/storageAccounts + - Microsoft.Network/publicIPAddresses + - Microsoft.Network/virtualNetworks + - Microsoft.Network/networkInterfaces + - Microsoft.Compute/virtualMachines + +Notice how simple the resource definitions are and the ability to implicitly reference symbolic names instead of explicit `dependsOn` throughout the template. + +### Create a bicep module for storage resources + +You'll create a storage template module **storage.bicep** which will create a storage account only and will be imported by the main template. + +1. First, remove the storage resource from the main template. Click the **Edit** button: + + ![Screenshot of the pipeline edit button](media/edit.png) + +2. Delete the storage resource: + + ```bicep + resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'Storage' + } + ``` + +3. Change the default value of the `publicIPAllocationMethod` parameter from `Dynamic` to `Static` at line 20 +4. Change the default value of the `publicIpSku` parameter from `Basic` to `Standard` at line 27 +5. Commit the file (we're not done with it yet) + + ![Screenshot of the file commit button](media/commit.png) + +6. Next, hover your mouse over the `Infra` folder and click the ellipsis icon +7. Select **New**, and **File** +8. Enter **storage.bicep** for the name and click **Create** + + ![Screenshot of the new file menu](media/newfile.png) + +9. Copy the following code snippet into the file and commit your changes: + + ```bicep + @description('Location for all resources.') + param location string = resourceGroup().location + + @description('Name for the storage account.') + param storageAccountName string + + resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'Storage' + } + + output storageURI string = storageAccount.properties.primaryEndpoints.blob + ``` + +### Modify the simple-windows-vm template to use the template module + +You'll modify the `simple-windows-vm.bicep` template to reference the template module you created. + +1. Navigate back to the `simple-windows-vm.bicep` file and click **Edit** +2. Add the following code after the variables: + + ```bicep + module storageModule './storage.bicep' = { + name: 'linkedTemplate' + params: { + location: location + storageAccountName: storageAccountName + } + } + ``` + +3. Modify the reference to the storage account blob URI in the virtual machine resource to use the output of the module +4. Find the virtual machine resource and replace the diagnosticsProfile section with: + + ```bicep + diagnosticsProfile: { + bootDiagnostics: { + enabled: true + storageUri: storageModule.outputs.storageURI + } + } + ``` + +5. Review the following details in the main template: + + - A module in the main template is used to link to another template + - The module has a symbolic name called `storageModule` used for configuring dependencies + - You can only use **Incremental** deployment mode when using template modules + - A relative path is used for your template module + - Parameters pass values from the main template to the template modules + +6. Commit the template + +## Deploy the templates to Azure using YAML pipelines + +You'll use an Azure DevOps YAML pipeline to deploy your template to your Azure environment. + +### Deploy resources to Azure by YAML pipelines + +1. Navigate back to the **Pipelines** pane in the **Pipelines** hub +2. In the **Create your first Pipeline** window, click **Create pipeline** +3. On the **Where is your code?** pane, click **Azure Repos Git (YAML)** +4. On the **Select a repository** pane, click **eShopOnWeb** +5. On the **Configure your pipeline** pane, scroll down and select **Existing Azure Pipelines YAML File** +6. In the **Selecting an existing YAML File** blade, specify: + - Branch: **main** + - Path: **.ado/eshoponweb-cd-windows-cm.yml** +7. Click **Continue** to save these settings +8. In the variables section: + - Choose a name for your resource group + - Set the desired location + - Replace the value of the service connection with one of your existing service connections +9. Click the **Save and run** button from the top right corner + + ![Screenshot of the save and run button](media/saveandrun.png) + +10. When the commit dialog appears, click **Save and run** again +11. Wait for the deployment to finish and review the results + + ![Screenshot of the successful resource deployment to Azure using YAML pipelines](media/deploy.png) + +> **Note**: Remember to give the pipeline permission to use the previously created Service Connection if prompted. + +## Clean up resources + +Remember to delete the resources created in the Azure portal to avoid unnecessary charges: + +1. In the Azure portal, navigate to the resource group you created +2. Click **Delete resource group** +3. Type the resource group name to confirm deletion +4. Click **Delete** + +## Summary + +In this lab, you learned how to: + +- **Understand an Azure Bicep template's structure** including parameters, variables, and resources +- **Create a reusable Bicep module** for storage resources +- **Modify the main template** to use the module and updated dependencies +- **Deploy templates to Azure** using YAML pipelines + +Azure Bicep provides a powerful way to define and deploy Azure infrastructure as code. By using modules, you can create reusable components that make your infrastructure templates more maintainable and organized. The integration with Azure DevOps pipelines enables automated deployment of your infrastructure alongside your applications. diff --git a/Instructions/advanced/09-monitor-application-performance-azure-load-testing.md b/Instructions/advanced/09-monitor-application-performance-azure-load-testing.md new file mode 100644 index 0000000..53aca59 --- /dev/null +++ b/Instructions/advanced/09-monitor-application-performance-azure-load-testing.md @@ -0,0 +1,443 @@ +--- +lab: + topic: Advanced + title: "Monitor Application Performance with Azure Load Testing" + description: "Learn how to use Azure Load Testing to simulate performance testing against live-running web applications with different load scenarios." +--- + +# Monitor Application Performance with Azure Load Testing + +**Estimated time:** 60 minutes + +You will learn how to use Azure Load Testing to simulate performance testing against a live-running web application with different load scenarios. Azure Load Testing is a fully managed load-testing service that enables you to generate high-scale load and abstracts the complexity and infrastructure needed to run your load test at scale. + +## Before you start + +You need: + +- **Microsoft Edge** or an [Azure DevOps supported browser](https://docs.microsoft.com/azure/devops/server/compatibility) +- **Azure DevOps organization:** Create one if you don't have one +- **Azure subscription:** Create one or use an existing one +- **Microsoft account or Microsoft Entra account** with Owner role in the Azure subscription and Global Administrator role in the Microsoft Entra tenant +- **Azure CLI:** [Install the Azure CLI](https://docs.microsoft.com/cli/azure/install-azure-cli) + +## About Azure Load Testing + +Azure Load Testing is a fully managed load-testing service that enables you to generate high-scale load. The service simulates traffic for your applications, regardless of where they're hosted. Key benefits include: + +- **Scalable load generation** without infrastructure management +- **Integration with CI/CD pipelines** for automated testing +- **Support for Apache JMeter** test scripts +- **Real-time monitoring** and detailed analytics +- **Multiple load patterns** (constant load, step-up, spike testing) + +## Create and configure the team project + +First, you'll create an Azure DevOps project for this lab. + +1. In your browser, open your Azure DevOps organization +2. Click **New Project** +3. Give your project the name **eShopOnWeb** and choose **Scrum** on the **Work Item process** dropdown +4. Click **Create** + +## Import eShopOnWeb Git Repository + +Next, you'll import the sample repository that contains the application code. + +1. In your Azure DevOps organization, open the **eShopOnWeb** project +2. Click **Repos > Files**, then **Import** +3. In the **Import a Git Repository** window, paste this URL: `https://github.com/MicrosoftLearning/eShopOnWeb.git` +4. Click **Import** + +The repository structure: + +- **.ado** folder contains Azure DevOps YAML pipelines +- **.devcontainer** folder container setup to develop using containers +- **infra** folder contains Bicep & ARM infrastructure as code templates +- **.github** folder contains YAML GitHub workflow definitions +- **src** folder contains the .NET 8 website used in lab scenarios + +5. Go to **Repos > Branches** +6. Hover on the **main** branch then click the ellipsis on the right +7. Click **Set as default branch** + +## Create Azure resources + +You'll create Azure resources needed for this lab using Azure Cloud Shell. + +1. Navigate to the [Azure Portal](https://portal.azure.com) and sign in +2. In the toolbar, click the **Cloud Shell** icon located directly to the right of the search text box +3. If prompted to select either **Bash** or **PowerShell**, select **Bash** + +> **Note**: If this is the first time you are starting Cloud Shell and you are presented with the "You have no storage mounted" message, select the subscription you are using in this lab, and select **Create storage**. + +4. From the **Bash** prompt, run the following command to create a resource group (replace `` with the name of the Azure region closest to you such as 'eastus'): + + ```bash + RESOURCEGROUPNAME='az400m08l14-RG' + LOCATION='' + az group create --name $RESOURCEGROUPNAME --location $LOCATION + ``` + +5. Create a Windows App service plan: + + ```bash + SERVICEPLANNAME='az400l14-sp' + az appservice plan create --resource-group $RESOURCEGROUPNAME \ + --name $SERVICEPLANNAME --sku B3 + ``` + +6. Create a web app with a unique name: + + ```bash + WEBAPPNAME=az400eshoponweb$RANDOM$RANDOM + az webapp create --resource-group $RESOURCEGROUPNAME --plan $SERVICEPLANNAME --name $WEBAPPNAME + ``` + +> **Note**: Record the name of the web app. You will need it later in this lab. + +## Configure CI/CD Pipelines with YAML + +You'll configure CI/CD Pipelines as code with YAML in Azure DevOps. + +### Add a YAML build and deploy definition + +1. Navigate to the **Pipelines** pane in the **Pipelines** hub +2. Click **New pipeline** (or Create Pipeline if this is the first one you create) +3. On the **Where is your code?** pane, click **Azure Repos Git (YAML)** option +4. On the **Select a repository** pane, click **eShopOnWeb** +5. On the **Configure your pipeline** pane, scroll down and select **Starter Pipeline** +6. **Select** all lines from the Starter Pipeline, and delete them +7. **Copy** the full template pipeline from below: + + ```yml + #Template Pipeline for CI/CD + # trigger: + # - main + + resources: + repositories: + - repository: self + trigger: none + + stages: + - stage: Build + displayName: Build .Net Core Solution + jobs: + - job: Build + pool: + vmImage: ubuntu-latest + steps: + - task: DotNetCoreCLI@2 + displayName: Restore + inputs: + command: "restore" + projects: "**/*.sln" + feedsToUse: "select" + + - task: DotNetCoreCLI@2 + displayName: Build + inputs: + command: "build" + projects: "**/*.sln" + + - task: DotNetCoreCLI@2 + displayName: Publish + inputs: + command: "publish" + publishWebProjects: true + arguments: "-o $(Build.ArtifactStagingDirectory)" + + - task: PublishBuildArtifacts@1 + displayName: Publish Artifacts ADO - Website + inputs: + pathToPublish: "$(Build.ArtifactStagingDirectory)" + artifactName: Website + + - stage: Deploy + displayName: Deploy to an Azure Web App + jobs: + - job: Deploy + pool: + vmImage: "windows-2019" + steps: + - task: DownloadBuildArtifacts@1 + inputs: + buildType: "current" + downloadType: "single" + artifactName: "Website" + downloadPath: "$(Build.ArtifactStagingDirectory)" + ``` + +8. Set the cursor on a new line at the end of the YAML definition at the indentation of the previous task level +9. Click **Show Assistant** from the right hand side of the portal +10. In the list of tasks, search for and select the **Azure App Service Deploy** task +11. In the **Azure App Service deploy** pane, specify the following settings and click **Add**: + + - In the **Azure subscription** drop-down list, select your Azure subscription + - Validate **App Service Type** points to Web App on Windows + - In the **App Service name** dropdown list, select the name of the web app you deployed earlier (az400eshoponweb...) + - In the **Package or folder** text box, **update** the Default Value to `$(Build.ArtifactStagingDirectory)/**/Web.zip` + - Expand **Application and Configuration Settings**, and in the App settings text box, add: `-UseOnlyInMemoryDatabase true -ASPNETCORE_ENVIRONMENT Development` + +12. Confirm the settings from the Assistant pane by clicking the **Add** button + +The snippet of code added should look similar to this: + +```yml +- task: AzureRmWebAppDeployment@4 + inputs: + ConnectionType: "AzureRM" + azureSubscription: "SERVICE CONNECTION NAME" + appType: "webApp" + WebAppName: "az400eshoponWeb369825031" + packageForLinux: "$(Build.ArtifactStagingDirectory)/**/Web.zip" + AppSettings: "-UseOnlyInMemoryDatabase true -ASPNETCORE_ENVIRONMENT Development" +``` + +13. Before saving the updates, give it a clear name. On top of the yaml-editor window, rename **azure-pipelines-#.yml** to **m08l14-pipeline.yml** +14. Click **Save**, on the **Save** pane, click **Save** again to commit the change directly into the main branch +15. Navigate to **Pipelines** and select **Pipelines** again. Select **All** to open all pipeline definitions +16. Select the pipeline and confirm to run it by clicking **Run** and confirm by clicking **Run** once more +17. Notice the 2 different Stages: **Build .Net Core Solution** and **Deploy to Azure Web App** +18. Wait for the pipeline to complete successfully + +### Review the deployed site + +1. Switch back to the Azure portal and navigate to the Azure web app blade +2. On the Azure web app blade, click **Overview** and click **Browse** to open your site in a new browser tab +3. Verify that the deployed site loads as expected, showing the eShopOnWeb E-commerce website + +## Deploy and Setup Azure Load Testing + +You'll deploy an Azure Load Testing Resource and configure different Load Testing scenarios. + +> **Important**: Azure Load Testing is a **paid service**. You will incur costs for running load tests. Make sure to clean up resources after completing the lab to avoid additional costs. See the [Azure Load Testing pricing page](https://azure.microsoft.com/pricing/details/load-testing) for more information. + +### Deploy Azure Load Testing + +1. From the Azure Portal, navigate to **Create Azure Resource** +2. In the search field, enter **Azure Load Testing** +3. Select **Azure Load Testing** (published by Microsoft) from the search results +4. Click **Create** to start the deployment process +5. Provide the necessary details for the resource deployment: + - **Subscription**: select your Azure Subscription + - **Resource Group**: select the Resource Group you used for deploying the Web App Service + - **Name**: `eShopOnWebLoadTesting` + - **Region**: Select a region close to your region + +> **Note**: Azure Load Testing service is not available in all Azure Regions. + +6. Click **Review and Create**, to validate your settings +7. Click **Create** to confirm and deploy the Azure Load Testing resource +8. Wait for the deployment to complete successfully +9. Click **Go to Resource** to navigate to the **eShopOnWebLoadTesting** Azure Load Testing resource + +### Create Azure Load Testing tests + +You'll create different Azure Load Testing tests using different load configuration settings. + +#### Create first load test (Virtual Users) + +1. From the **eShopOnWebLoadTesting** Azure Load Testing Resource blade, navigate to **Tests** under **Tests** +2. Click the **+ Create** menu option, and select **Create a URL-based test** +3. Complete the following parameters and settings: + + - **Test URL**: Enter the URL from the Azure App Service you deployed (az400eshoponweb...azurewebsites.net), **including https://** + - **Specify Load**: Virtual Users + - **Number of Virtual Users**: 50 + - **Test Duration (minutes)**: 5 + - **Ramp-up time (minutes)**: 1 + +4. Click **Review and Create**, then **Create** +5. The test will run for 5 minutes + +#### Create second load test (Requests per Second) + +1. From the top menu, click **Create**, **Create a URL-based test** +2. Complete the following parameters and settings: + + - **Test URL**: Enter the URL from the Azure App Service (including https://) + - **Specify Load**: Requests per Second (RPS) + - **Requests per second (RPS)**: 100 + - **Response time (milliseconds)**: 500 + - **Test Duration (minutes)**: 5 + - **Ramp-up time (minutes)**: 1 + +3. Click **Review + create**, then **Create** +4. The test will run for about 5 minutes + +### Validate Azure Load Testing results + +With both tests complete, you'll validate the outcome of the Azure Load Testing TestRuns. + +1. From **Azure Load Testing**, navigate to **Tests** +2. Select either of the test definitions to open a detailed view by **clicking** on one of the tests +3. Select the **TestRun_mm/dd/yy-hh:hh** from the resulting list +4. From the detailed **TestRun** page, identify the actual outcome of the simulation: + + - Load / Total Requests + - Duration + - Response Time (90th percentile response time) + - Throughput in requests per second + +5. Review the dashboard graph line and chart views below +6. Take time to **compare the results** of both simulated tests and **identify the impact** of more users on the App Service performance + +## Automate Load Test with CI/CD in Azure Pipelines + +You'll add load testing automation to a CI/CD pipeline by exporting configuration files and configuring Azure Pipelines. + +### Identify Azure DevOps Service Connection details + +You'll grant the required permissions to the Azure DevOps Service Connection. + +1. From the **Azure DevOps Portal**, navigate to the **eShopOnWeb** Project +2. From the bottom left corner, select **Project Settings** +3. Under the **Pipelines** section, select **Service Connections** +4. Notice the Service Connection with the name of your Azure Subscription +5. **Select the Service Connection**. From the **Overview** tab, navigate to **Details** and select **Manage service connection roles** +6. This redirects you to the Azure Portal, opening the resource group details in the access control (IAM) blade + +### Grant permissions to the Azure Load Testing resource + +Azure Load Testing uses Azure RBAC to grant permissions for performing specific activities on your load testing resource. You'll grant the **Load Test Contributor** role to the Azure DevOps service connection. + +1. Select **+ Add** and **Add role assignment** +2. In the **Role tab**, select **Load Test Contributor** in the list of job function roles +3. In the **Members tab**, select **Select members**, find and select your user account and click **Select** +4. In the **Review + assign tab**, select **Review + assign** to add the role assignment + +You can now use the service connection in your Azure Pipelines workflow to access your Azure load testing resource. + +### Export load test input files and Import to Azure Repos + +To run a load test with Azure Load Testing in a CI/CD workflow, you need to add the load test configuration settings and input files in your source control repository. + +1. In the **Azure portal**, go to your **Azure Load Testing** resource +2. On the left pane, select **Tests** to view the list of load tests, then select **your test** +3. Select the **ellipsis (...)** next to the test run, then select **Download input file** +4. The browser downloads a zipped folder that contains the load test input files +5. Use any zip tool to extract the input files. The folder contains: + + - _config.yaml_: the load test YAML configuration file + - _quick_test.jmx_: the JMeter test script + +6. Navigate to the **Azure DevOps Portal** and the **eShopOnWeb** DevOps Project +7. Select **Repos**. In the source code folder structure, notice the **tests** subfolder +8. Notice the ellipsis (...), and select **New > Folder** +9. Specify **jmeter** as folder name, and **placeholder.txt** for the file name +10. Click **Commit** to confirm the creation of the placeholder file and jmeter folder +11. From the **Folder structure**, navigate to the new created **jmeter** subfolder +12. Click the **ellipsis(...)** and select **Upload File(s)** +13. Using the **Browse** option, navigate to the location of the extracted zip-file, and select both **config.yaml** and **quick_test.jmx** +14. Click **Commit** to confirm the file upload into source control +15. Within Repos, browse to the **/tests/jmeter** subfolder +16. Open the Load Testing **config.yaml** file. Click **Edit** to allow editing +17. Replace the **displayName** and **testId** attributes with the value **ado_load_test** +18. Click **Commit** to save the changes + +### Update the CI/CD workflow YAML definition file + +1. Open the [Azure Load Testing task extension](https://marketplace.visualstudio.com/items?itemName=AzloadTest.AzloadTesting) in the Azure DevOps Marketplace, and select **Get it free** +2. Select your Azure DevOps organization, and then select **Install** to install the extension +3. From the Azure DevOps Portal, navigate to **Pipelines** and select the pipeline created earlier. Click **Edit** +4. In the YAML script, navigate to **line 56** and press ENTER/RETURN to add a new empty line (right before the Deploy Stage) +5. At line 57, select the Tasks Assistant and search for **Azure Load Testing** +6. Complete the graphical pane with the correct settings: + + - Azure Subscription: Select the subscription which runs your Azure Resources + - Load Test File: '$(Build.SourcesDirectory)/tests/jmeter/config.yaml' + - Load Test Resource Group: The Resource Group which holds your Azure Load Testing Resources + - Load Test Resource Name: `eShopOnWebLoadTesting` + - Load Test Run Name: ado_run + - Load Test Run Description: load testing from ADO + +7. Click **Add** to inject the parameters as a YAML snippet +8. Fix indentation if needed (add 2 spaces or tab to position the snippet correctly) + +The YAML code should look like this: + +```yml +- task: AzureLoadTest@1 + inputs: + azureSubscription: "AZURE DEMO SUBSCRIPTION" + loadTestConfigFile: "$(Build.SourcesDirectory)/tests/jmeter/config.yaml" + resourceGroup: "az400m08l14-RG" + loadTestResource: "eShopOnWebLoadTesting" + loadTestRunName: "ado_run" + loadTestRunDescription: "load testing from ADO" +``` + +9. Below the inserted YAML snippet, add a new empty line +10. Add a snippet for the Publish task to show the results of the Azure Load testing task: + +```yml +- publish: $(System.DefaultWorkingDirectory)/loadTest + artifact: loadTestResults +``` + +11. Fix indentation if needed +12. **Save** the changes +13. Once saved, click **Run** to trigger the pipeline +14. Confirm the branch (main) and click **Run** to start the pipeline run +15. From the pipeline status page, click the **Build** stage to open the verbose logging details +16. Wait for the pipeline to arrive at the **AzureLoadTest** task +17. While the task is running, browse to the **Azure Load Testing** in the Azure Portal and see how the pipeline creates a new RunTest named **adoloadtest1** +18. Navigate back to the Azure DevOps CI/CD Pipeline Run view, where the **AzureLoadTest task** should complete successfully + +### Add failure/success criteria to Load Testing Pipeline + +You'll use load test fail criteria to get alerted when the application doesn't meet your quality requirements. + +1. From Azure DevOps, navigate to the eShopOnWeb Project, and open **Repos** +2. Within Repos, browse to the **/tests/jmeter** subfolder +3. Open the Load Testing **config.yaml** file. Click **Edit** to allow editing +4. Replace `failureCriteria: []` if present, otherwise append the following snippet: + + ```yaml + failureCriteria: + - avg(response_time_ms) > 300 + - percentage(error) > 50 + ``` + +5. Save the changes by clicking **Commit** and Commit once more +6. Navigate back to **Pipelines** and run the **eShopOnWeb** pipeline again +7. After a few minutes, it will complete with a **failed** status for the **AzureLoadTest** task +8. Open the verbose logging view for the pipeline, and validate the details of the **AzureLoadtest** + +The output should show something similar to: + +```text +-------------------Test Criteria --------------- +Results :1 Pass 1 Fail + +Criteria :Actual Value Result +avg(response_time_ms) > 300 1355.29 FAILED +percentage(error) > 50 PASSED + +##[error]TestResult: FAILED +``` + +9. Notice how the last line says **##[error]TestResult: FAILED**. Since we defined a **FailCriteria** having an avg response time of > 300, the task is flagged as failed when the response time exceeds this threshold + +> **Note**: In a real-life scenario, you would validate the performance of your App Service, and if performance is below a certain threshold, you could trigger additional deployments or scaling actions. + +## Summary + +In this lab, you learned how to: + +- **Deploy a web app to Azure App Service** using Azure Pipelines +- **Deploy and configure Azure Load Testing** with different test scenarios +- **Integrate load testing into CI/CD pipelines** for automated performance validation +- **Define success/failure criteria** for load tests to ensure application quality +- **Analyze load test results** to understand application performance characteristics + +Azure Load Testing provides a powerful platform for performance testing that integrates seamlessly with your CI/CD workflows, helping ensure your applications can handle expected load before deployment to production. + +## Cleanup + +Remember to delete the Azure resources created in this lab to avoid unnecessary charges: + +1. Delete the **az400m08l14-RG** resource group from the Azure Portal +2. This will remove all associated resources including the App Service, App Service Plan, and Load Testing resource diff --git a/Instructions/advanced/media/acr-password.png b/Instructions/advanced/media/acr-password.png new file mode 100644 index 0000000..1412d39 Binary files /dev/null and b/Instructions/advanced/media/acr-password.png differ diff --git a/Instructions/advanced/media/azure-container-registry.png b/Instructions/advanced/media/azure-container-registry.png new file mode 100644 index 0000000..b05926c Binary files /dev/null and b/Instructions/advanced/media/azure-container-registry.png differ diff --git a/Instructions/advanced/media/browsebicepfile.png b/Instructions/advanced/media/browsebicepfile.png new file mode 100644 index 0000000..66044ef Binary files /dev/null and b/Instructions/advanced/media/browsebicepfile.png differ diff --git a/Instructions/advanced/media/commit.png b/Instructions/advanced/media/commit.png new file mode 100644 index 0000000..a9c965f Binary files /dev/null and b/Instructions/advanced/media/commit.png differ diff --git a/Instructions/advanced/media/config_edit.png b/Instructions/advanced/media/config_edit.png new file mode 100644 index 0000000..5cf7f3d Binary files /dev/null and b/Instructions/advanced/media/config_edit.png differ diff --git a/Instructions/advanced/media/create-project.png b/Instructions/advanced/media/create-project.png new file mode 100644 index 0000000..f10adb9 Binary files /dev/null and b/Instructions/advanced/media/create-project.png differ diff --git a/Instructions/advanced/media/deploy.png b/Instructions/advanced/media/deploy.png new file mode 100644 index 0000000..064d572 Binary files /dev/null and b/Instructions/advanced/media/deploy.png differ diff --git a/Instructions/advanced/media/edit.png b/Instructions/advanced/media/edit.png new file mode 100644 index 0000000..96280c5 Binary files /dev/null and b/Instructions/advanced/media/edit.png differ diff --git a/Instructions/advanced/media/eshoponweb-m9l16-pipeline.png b/Instructions/advanced/media/eshoponweb-m9l16-pipeline.png new file mode 100644 index 0000000..6e26b19 Binary files /dev/null and b/Instructions/advanced/media/eshoponweb-m9l16-pipeline.png differ diff --git a/Instructions/advanced/media/import-repo.png b/Instructions/advanced/media/import-repo.png new file mode 100644 index 0000000..6f77107 Binary files /dev/null and b/Instructions/advanced/media/import-repo.png differ diff --git a/Instructions/advanced/media/newfile.png b/Instructions/advanced/media/newfile.png new file mode 100644 index 0000000..6e151c5 Binary files /dev/null and b/Instructions/advanced/media/newfile.png differ diff --git a/Instructions/advanced/media/saveandrun.png b/Instructions/advanced/media/saveandrun.png new file mode 100644 index 0000000..321bdfa Binary files /dev/null and b/Instructions/advanced/media/saveandrun.png differ diff --git a/Instructions/advanced/media/select-ci-container-compose.png b/Instructions/advanced/media/select-ci-container-compose.png new file mode 100644 index 0000000..1fed9b4 Binary files /dev/null and b/Instructions/advanced/media/select-ci-container-compose.png differ diff --git a/Instructions/advanced/media/vg-create.png b/Instructions/advanced/media/vg-create.png new file mode 100644 index 0000000..3b873c6 Binary files /dev/null and b/Instructions/advanced/media/vg-create.png differ diff --git a/Instructions/basic/03-implement-real-time-monitoring-azure-monitor.md b/Instructions/basic/03-implement-real-time-monitoring-azure-monitor.md new file mode 100644 index 0000000..df4795a --- /dev/null +++ b/Instructions/basic/03-implement-real-time-monitoring-azure-monitor.md @@ -0,0 +1,140 @@ +--- +lab: + topic: Basic + title: "Implement Real-Time Monitoring with Azure Monitor" + description: "Learn how to implement comprehensive monitoring and observability for applications using Azure Monitor and Application Insights." +--- + +# Implement Real-Time Monitoring with Azure Monitor + +In this lab, you will implement real-time monitoring for a web application using Azure Monitor and Application Insights. This will help you gain insights into application performance, detect anomalies, and ensure the reliability of your services. + +You will learn how to: + +- Create a sample web app. +- Enable and configure Azure Monitor and Application Insights. +- Create custom dashboards to visualize application metrics. +- Set up alerts to notify stakeholders of performance anomalies. +- Analyze performance data for potential improvements. + +This lab takes approximately **20** minutes to complete. + +## Before you start + +To complete the lab, you need: + +- An active Azure subscription. If you don't have one, you can create a free account at [Azure Free Account](https://azure.microsoft.com/free). +- Basic knowledge of monitoring and observability concepts. +- Basic understanding of Azure services. + +## Create a sample web application + +In this exercise, you will create a sample web application in Azure App Service and enable Application Insights for monitoring. + +### Create an Azure Web App + +1. Open the Azure portal. +1. In the Search bar, type App Services and select it. +1. Click + Create and select Web App. +1. In the Basics tab: + - Subscription: Select your Azure subscription. + - Resource Group: Click Create new, enter **`monitoringlab-rg`**, and click OK. + - Name: Enter a unique name, such as **`monitoringlab-webapp`**. + - Publish: Select Code. + - Runtime stack: Choose .NET 8 (LTS). + - Region: Select a region close to you. +1. Click Review + create, then click Create. +1. Wait for deployment to complete, then click "Go to resource". +1. In the Overview tab, click the URL to verify the web app is running. + +### Verify Application Insights + +1. In the Web App resource, scroll to the Monitoring section. +1. Click Application Insights. +1. Application Insights is already enabled for this web app. Click the link to open the Application Insights resource. +1. In the Application Insights resource, click in the Application Dashboard to view the performance data the default dashboard provided. + + > **Note:** The dashboard may take a few moments to fully load and display all performance data. Wait for the dashboard to completely render before proceeding to ensure optimal experience in subsequent exercises. + +## Configure Azure Monitor and dashboards + +### Access Azure Monitor + +1. In the Azure portal, search for Monitor and click on it. +1. Click Metrics in the left panel. +1. In the Scope section, select the web app under the subscription and resource group where you deployed the web app. +1. Click Apply and observe the metrics available for the web app. + +### Add key metrics to the dashboard + +1. In the Scope section, select your Web App (monitoringlab-webapp). +1. Under Metric, choose Response Time. +1. Set the Aggregation to Average and click + Add metric. +1. Repeat the process for additional metrics: + - CPU Time (Count) + - Requests (Average) +1. In the metrics chart area, click the **Pin to dashboard** button (pin icon) in the top-right corner of the chart. +1. In the **Pin to dashboard** dialog that appears: + - Select **New** to create a new dashboard + - Enter a dashboard name: **`MonitoringLab Dashboard`** + - Select the appropriate subscription + - Choose **Public** for dashboard type + - Click **Create and pin** +1. After the dashboard is created and the metrics are pinned, click **Save** in the top menu to save the dashboard. +1. Navigate to the dashboard by clicking the **Dashboard** icon in the left panel of the Azure portal, or search for "Dashboard" in the top search bar. +1. Select your newly created **MonitoringLab Dashboard** from the dashboard list. +1. Verify the metrics are displayed on the dashboard and are updating in real-time. + +## Create alerts + +### Define alert conditions and actions + +1. In Azure Monitor, click Alerts. +1. Click + Create and select Alert rule. +1. Under Scope, select your Web App (monitoringlab-webapp) and click Apply. +1. Under Condition, click in the Signal name field and select Response Time. +1. Configure the alert rule: + - Threshold type: Dynamic + - Aggregation type: Average + - Value is: Greater or Less than + - Threshold Sensitivity: High + - When to evaluate: Check every 1 minutes and look back at 5 minutes. +1. Under Actions, select Use quick actions. +1. Enter: + - Action group name: WebAppMonitoringAlerts + - Display name: WebAlert + - Email: Enter your email address. +1. Click Save. +1. Click Next: Details. +1. Enter a Name `WebAppResponseTimeAlert` and select a Severity level Verbose. +1. Click Review + create and then Create. + + > **Note:** Your alert rule is now created and will trigger an email notification when the response time exceeds the threshold. You can force the alert to trigger by sending a large number of requests to the web app. For example, you can use Azure Load testing or a tool like Apache JMeter. + +1. Go back to the Azure Monitor > Alerts. +1. Click on Alert rules and you should see the alert rule you created. + +## Analyze performance data + +### Review Collected Metrics + +1. Go to Application Insights in the Azure portal. +1. Click on the Application Dashboard. +1. Click Performance tile to analyze server response times and load. You can also view the number of requests and failed requests. +1. Click on Analyze with Workbooks and select Performance Counter Analysis. +1. Click Workbooks. +1. Select the workbook Performance Analysis under Performance. +1. You can see the performance data for the web app. + +> **Note:** You can customize the workbook to include additional metrics and filters. + +## Clean up resources + +Now that you finished the exercise, you should delete the cloud resources you created to avoid unnecessary resource usage. + +1. In your browser navigate to the Azure portal [https://portal.azure.com](https://portal.azure.com); signing in with your Azure credentials if prompted. +1. Navigate to the resource group you created and view the contents of the resources used in this exercise. +1. On the toolbar, select **Delete resource group**. +1. Enter the resource group name and confirm that you want to delete it. + +> **CAUTION:** Deleting a resource group deletes all resources contained within it. If you chose an existing resource group for this exercise, any existing resources outside the scope of this exercise will also be deleted. diff --git a/Instructions/basic/04-agile-planning-portfolio-management-azure-boards.md b/Instructions/basic/04-agile-planning-portfolio-management-azure-boards.md new file mode 100644 index 0000000..decc214 --- /dev/null +++ b/Instructions/basic/04-agile-planning-portfolio-management-azure-boards.md @@ -0,0 +1,420 @@ +--- +lab: + topic: Basic + title: "Agile Planning and Portfolio Management with Azure Boards" + description: "Learn about the agile planning and portfolio management tools and processes provided by Azure Boards and how they can help you quickly plan, manage, and track work across your entire team." +--- + +# Agile Planning and Portfolio Management with Azure Boards + +In this lab, you'll learn about the agile planning and portfolio management tools and processes provided by Azure Boards and how they can help you quickly plan, manage, and track work across your entire team. You'll explore the product backlog, sprint backlog, and task boards that can track the flow of work during an iteration. We'll also look at the enhanced tools in this release to scale for larger teams and organizations. + +You will learn how to: + +- Manage teams, areas, and iterations. +- Manage work items. +- Manage sprints and capacity. +- Customize Kanban boards. +- Define dashboards. +- Customize team process. + +This lab takes approximately **60** minutes to complete. + +## Before you start + +To complete the lab, you need: + +- **Microsoft Edge** or an [Azure DevOps supported browser.](https://docs.microsoft.com/azure/devops/server/compatibility?view=azure-devops#web-portal-supported-browsers) +- An active Azure subscription. If you don't have one, you can create a free account at [Azure Free Account](https://azure.microsoft.com/free). +- An Azure DevOps organization. If you don't already have one, create one by following the instructions at [Create an organization or project collection](https://docs.microsoft.com/azure/devops/organizations/accounts/create-organization?view=azure-devops). + +### Set up Azure DevOps organization (if needed) + +If you don't already have an Azure DevOps organization, follow these steps: + +1. Use a private browser session to get a new **personal Microsoft Account (MSA)** at `https://account.microsoft.com` (skip if you already have one). +1. Using the same browser session, sign up for a free Azure subscription at `https://azure.microsoft.com/free` (skip if you already have one). +1. Open a browser and navigate to Azure portal at `https://portal.azure.com`, then search at the top of the Azure portal screen for **Azure DevOps**. In the resulting page, click **Azure DevOps organizations**. +1. Next, click on the link labelled **My Azure DevOps Organizations** or navigate directly to `https://aex.dev.azure.com`. +1. On the **We need a few more details** page, select **Continue**. +1. In the drop-down box on the left, choose **Default Directory**, instead of **Microsoft Account**. +1. If prompted (_"We need a few more details"_), provide your name, e-mail address, and location and click **Continue**. +1. Back at `https://aex.dev.azure.com` with **Default Directory** selected click the blue button **Create new organization**. +1. Accept the _Terms of Service_ by clicking **Continue**. +1. If prompted (_"Almost done"_), leave the name for the Azure DevOps organization at default (it needs to be a globally unique name) and pick a hosting location close to you from the list. +1. Once the newly created organization opens in **Azure DevOps**, select **Organization settings** in the bottom left corner. +1. At the **Organization settings** screen select **Billing** (opening this screen takes a few seconds). +1. Select **Setup billing** and on the right-hand side of the screen, select your **Azure Subscription** and then select **Save** to link the subscription with the organization. +1. Once the screen shows the linked Azure Subscription ID at the top, change the number of **Paid parallel jobs** for **MS Hosted CI/CD** from 0 to **1**. Then select **SAVE** button at the bottom. + + > **Note**: You may **wait a couple of minutes before using the CI/CD capabilities** so that the new settings are reflected in the backend. Otherwise, you will still see the message _"No hosted parallelism has been purchased or granted"_. + +1. In **Organization Settings**, go to section **Pipelines** and click **Settings**. +1. Toggle the switch to **Off** for **Disable creation of classic build pipelines** and **Disable creation of classic release pipelines**. +1. In **Organization Settings**, go to section **Security** and click **Policies**. +1. Toggle the switch to **On** for **Allow public projects**. + +### Create and configure the Azure DevOps project (if needed) + +1. Open your browser and navigate to your Azure DevOps organization. +1. Select the **New Project** option and use the following settings: + - name: **eShopOnWeb** + - visibility: **Private** + - Advanced: Version Control: **Git** + - Advanced: Work Item Process: **Scrum** +1. Select **Create**. + + ![Screenshot of the create new project panel.](media/create-project.png) + +### Import eShopOnWeb git repository (if needed) + +1. Open the previously created **eShopOnWeb** project. +1. Select the **Repos > Files**, **Import a Repository** and then select **Import**. +1. On the **Import a Git Repository** window, paste the following URL `https://github.com/MicrosoftLearning/eShopOnWeb` and select **Import**: + + ![Screenshot of the import repository panel.](media/import-repo.png) + +1. The repository is organized the following way: + + - **.ado** folder contains Azure DevOps YAML pipelines. + - **.devcontainer** folder container setup to develop using containers (either locally in VS Code or GitHub Codespaces). + - **.azure** folder contains Bicep & ARM infrastructure as code templates. + - **.github** folder container YAML GitHub workflow definitions. + - **src** folder contains the .NET 8 website used on the lab scenarios. + +1. Leave the web browser window open. +1. Go to **Repos > Branches**. +1. Hover on the **main** branch then click the ellipsis on the right of the column. +1. Click on **Set as default branch**. + +## Manage teams, areas, and iterations + +Each new project is configured with a default team, which name matches the project name. You have the option of creating additional teams. Each team can be granted access to a suite of Agile tools and team assets. The ability to create multiple teams gives you the flexibility to choose the proper balance between autonomy and collaboration across the enterprise. + +1. On your lab computer, start a web browser and navigate to the Azure DevOps portal at `https://aex.dev.azure.com`. + + > **Note**: If prompted, sign in using the Microsoft account associated with your Azure DevOps subscription. + +1. Open the **eShopOnWeb** project under your Azure DevOps organization. + + > **Note**: Alternatively, you can access the project page directly by navigating to the URL, where the YOUR-AZURE-DEVOPS-ORGANIZATION placeholder, represents your account name, and the PROJECT-NAME placeholder represents the name of the project. + +1. Click the cogwheel icon labeled **Project settings** located in the lower left corner of the page to open the **Project settings** page. + + ![Screenshot of the Azure DevOps settings page.](media/project_settings_v1.png) + +1. In the **General** section, select the **Teams** tab. There is already a default team in this project, **eShopOnWeb Team** but you'll create a new one for this lab. Click **New Team**. + + ![Screenshoot of the Teams project settings window.](media/new_team_v1.png) + +1. On the **Create a new team** pane, in the **Team name** textbox, type **`EShop-Web`**, leave other settings with their default values, and click **Create**. + + ![Screenshot of the project settings teams page.](media/eshopweb-team_v1.png) + +1. In the list of **Teams**, select the newly created team to view its details. + + > **Note**: By default, the new team has only you as its member. You can use this view to manage such functionality as team membership, notifications, and dashboards. + +1. Click **Iterations and Area Paths** link at the top of the **EShop-Web** page to start defining the schedule and scope of the team. + + ![Screenshot of Iterations and Area Paths option."](media/EShop-WEB-iterationsareas_v1.png) + +1. At the top of the **Boards** pane, select the **Iterations** tab and then click **+ Select iteration(s)**. + + ![Screenshot of the iterations configuration page.](media/EShop-WEB-select_iteration_v1.png) + +1. Select **eShopOnWeb\Sprint 1** and click **Save and close**. Note that this first sprint will show up in the list of iterations, but the Dates are not set yet. +1. Select **Sprint 1** and click the **ellipsis (...)**. From the context menu, select **Edit**. + + > **Note**: Specify the Start Date as the first work day of last week, and count 3 full work weeks for each sprint. For example, if March 6 is the first work day of the sprint, it goes until March 24th. Sprint 2 starts on March 27, which is 3 weeks out from March 6. + +1. Repeat the previous step to add **Sprint 2** and **Sprint 3**. You could say that we are currently in the 2nd week of the first sprint. + + ![Screenshot of the sprints configuration under the iterations tab.](media/EShop-WEB-3sprints_v1.png) + +1. Still in the **Project Settings / Boards / Team Configuration** pane, at the top of the pane, select the **Areas** tab. You will find there an automatically generated area with the name matching the name of the team. + + ![Screenshot of the Areas settings page.](media/EShop-WEB-areas_v1.png) + +1. Click the ellipsis symbol (...) next to the **default area** entry and, in the dropdown list, select **Include sub areas**. + + > **Note**: The default setting for all teams is to exclude sub-area paths. We will change it to include sub-areas so that the team gets visibility into all of the work items from all teams. Optionally, the management team could also choose to not include sub-areas, which automatically removes work items from their view as soon as they are assigned to one of the teams. + +## Manage work items + +Work items play a prominent role in Azure DevOps. Whether describing work to be done, impediments to release, test definitions, or other key items, work items are the workhorse of modern projects. In this task you'll focus on using various work items to set up the plan to extend the eShopOnWeb site with a product training section. While it can be daunting to build out such a substantial part of a company's offering, Azure DevOps and the Scrum process make it very manageable. + +> **Note**: This task is designed to illustrate a variety of ways you can create different kinds of work items, as well as to demonstrate the breadth of features available on the platform. As a result, these steps should not be viewed as prescriptive guidance for project management. The features are intended to be flexible enough to fit your process needs, so explore and experiment as you go. + +1. Click on the project name in the upper left corner of the Azure DevOps portal to return to the project home page. + +1. In the vertical navigational pane of the Azure DevOps portal, select the **Boards** icon and, select **Work Items**. + + > **Note**: There are many ways to create work items in Azure DevOps, and we'll explore a few of them. Sometimes it's as simple as firing one off from a dashboard. + +1. On the **Work Items** window, click on **+ New Work Item > Epic**. + + ![Screenshot of the work item creation.](media/EShop-WEB-create_epic_v1.png) + +1. In the **Enter title** textbox, type **`Product training`**. +1. In the upper left corner, select the **No one selected** entry and, in the dropdown list, select your user account in order to assign the new work item to yourself. If your name doesn't appear to start with, begin typing your name and click **Search**. +1. Next to the **Area** entry, select the **eShopOnWeb** entry and, in the dropdown list, select **EShop-WEB**. This will set the **Area** to **eShopOnWeb\EShop-WEB**. +1. Next to the **Iteration** entry, select the **eShopOnWeb** entry and, in the dropdown list, select **Sprint 2**. This will set the **Iteration** to **eShopOnWeb\Sprint 2**. +1. Click **Save** to finalize your changes. **Do not close it**. + + > **Note**: The work item form includes all of the relevant work item settings. This includes details about who it's assigned to, its status across many parameters, and all the associated information and history for how it has been handled since creation. One of the key areas to focus on is the **Related Work**. We will explore one of the ways to add a feature to this epic. + +1. In the **Related work** section on the lower right-side, select the **Add link** entry and, in the dropdown list, select **New item**. +1. On the **Add link** panel, in the **Link Type** dropdown list, select **Child**. Next, in the **Work item type** dropdown list, select **Feature**, in the **Title** textbox, type **`Training dashboard`**. + + ![Screenshot of the work item link creation.](media/EShop-WEB-create_child_feature.png) + +1. Click **Add link** to save the Child item + + > **Note**: On the **Training dashboard** panel, note that the assignment, **Area**, and **Iteration** are already set to the same values as the epic that the feature is based on. In addition, the feature is automatically linked to the parent item it was created from. + +1. On the (New Feature) **Training dashboard** panel, click **Save and Close**. + +1. In the vertical navigation pane of the Azure DevOps portal, in the list of the **Boards** items, select **Boards**. +1. On the **Boards** panel, select the **EShop-WEB boards** entry. This will open the board for that particular team. + + ![Screenshot of the EShop-WEB board selection.](media/EShop-WEB-_boards_v1.png) + +1. On the **Boards** panel, in the upper right corner, select the **Backlog items** entry and, in the dropdown list, select **Features**. + + > **Note**: This will make it easy to add tasks and other work items to the features. + +1. Hover with the mouse pointer over the rectangle representing the **Training dashboard** feature. This will reveal the ellipsis symbol (...) in its upper right corner. +1. Click the ellipsis (...) icon and, in the dropdown list, select **Add Product Backlog Item**. + + ![Screenshot of the Add Product Backlog Item option in the Training dashboard feature.](media/EShop-WEB-add_pb_v1.png) + +1. In the textbox of the new product backlog item, type **`As a customer, I want to view new tutorials`** and press the **Enter** key to save the entry. + + > **Note**: This creates a new product backlog item (PBI) work item that is a child of the feature and shares its area and iteration. + +1. Repeat the previous step to add two more PBIs designed to enable the customer to see their recently viewed tutorials and to request new tutorials named, respectively, **`As a customer, I want to see tutorials I recently viewed`** and **`As a customer, I want to request new tutorials`**. + + ![Screenshot of the add Product Backlog.](media/EShop-WEB-pbis_v1.png) + +1. On the **Boards** panel, in the upper right corner, select the **Features** entry and, in the dropdown list, select **Backlog items**. + + > **Note**: Backlog items have a state that defines where they are relative to being completed. While you could open and edit the work item using the form, it's easier to just drag cards on the board. + +1. On the **Board** tab of the **EShop-WEB** panel, drag the first work item named **As a customer, I want to view new tutorials** from the **New** to **Approved** stage. + + > **Note**: You can also expand work item cards to get to conveniently editable details. + +1. Hover with the mouse pointer over the rectangle representing the work item you moved to the **Approved** stage. This will reveal the down facing caret symbol. +1. Click the down facing caret symbol to expand the work item card, replace the **Unassigned** entry with your name, then select your account to assign the moved PBI to yourself. +1. On the **Board** tab of the **EShop-WEB** panel, drag the second work item named **As a customer, I want to see tutorials I recently viewed** from the **New** to the **Committed** stage. +1. On the **Board** tab of the **EShop-WEB** panel, drag the third work item named **As a customer, I want to request new tutorials** from the **New** to the **Done** stage. + + > **Note**: The task board is one view into the backlog. You can also use the tabular view. + +1. On the **Board** tab of the **EShop-WEB** pane, at the top of the pane, click **View as Backlog** to display the tabular form. + + > **Note**: You can use the second plus sign directly left to the first backlog item to add a new task to it. + +1. On the **Backlog** tab of the **EShop-WEB** pane, in the upper left corner of the pane, click the plus sign next to the first work item. This will display the **NEW TASK** panel. + + ![Screenshot of the create task option in the backlog list.](media/new_task_v1.png) + +1. At the top of the **NEW TASK** panel, in the **Enter title** textbox, type **`Add page for most recent tutorials`**. +1. On the **NEW TASK** panel, in the **Remaining Work** textbox, type **5**. +1. On the **NEW TASK** panel, in the **Activity** dropdown list, select **Development**. +1. On the **NEW TASK** panel, click **Save and Close**. + + ![Screenshot of the new work item creation.](media/EShop-WEB-save_task_v1.png) + +1. Repeat the last five steps to add another task named **`Optimize data query for most recent tutorials`**. Set its **Remaining Work** to **3** and its **Activity** to **Design**. Click **Save and Close** once completed. + +## Manage sprints and capacity + +Teams build the sprint backlog during the sprint planning meeting, typically held on the first day of the sprint. Each sprint corresponds to a time-boxed interval which supports the team's ability to work using Agile processes and tools. During the planning meeting, the product owner works with the team to identify those stories or backlog items to complete in the sprint. + +Planning meetings typically consist of two parts. In the first part, the team and product owner identify the backlog items that the team feels it can commit to completing in the sprint, based on experience with previous sprints. These items get added to the sprint backlog. In the second part, the team determines how it will develop and test each item. They then define and estimate the tasks required to complete each item. Finally, the team commits to implementing some or all the items based on these estimates. + +The sprint backlog should contain all the information the team needs to successfully plan and complete work within the time allotted without having to rush at the end. Before planning the sprint, you'd want to have created, prioritized, and estimated the backlog and defined the sprints. + +1. In the vertical navigational pane of the Azure DevOps portal, select the **Boards** icon and, in the list of the **Boards** items, select **Sprints**. +1. On the **Taskboard** tab of the **Sprints** view, in the toolbar, on the right hand side, select the **View options** symbol (directly to the left of the funnel icon) and, in the **View options** dropdown list, select the **Work details** entry. Select **Sprint 2** as filter. + + > **Note**: The current sprint has a pretty limited scope. There are two tasks in the **To do** stage. At this point, neither task has been assigned. Both show a numeric value to the right of **Unassigned** entry representing the remaining work estimate. + +1. Within the **ToDo** Column, notice the Task Item **Add page for most recent tutorials**, click the **Unassigned** entry and, in the list of user accounts, select your account to assign the task to yourself. + +1. Select the **Capacity** tab of the **Sprints** view. + + > **Note**: This view enables you to define what activities a user can take on and at what level of capacity. + +1. On the **Capacity** tab of the **Sprints** view, for your user account, set the **Activity** field to **Development** and, in the **Capacity per day** textbox, type **1**. Then click **Save**. + + > **Note**: Let's assume you're also going to take some vacation. Which should be added to the capacity view too. + +1. On the **Capacity** tab of the **Sprints** view, directly next to the entry representing your user account, in the **Days off** column, click the **0 days** entry. This will display a panel where you can set your days off. +1. In the displayed panel, use the calendar view to set your vacation to span five work days during the current sprint (within the next three weeks) and, once completed, click **OK**. + + ![Screenshot of the day off configuration.](media/EShop-WEB-days_off_v1.png) + +1. Back on the **Capacity** tab of the **Sprints** view, click **Save**. + +## Customize Kanban boards + +To maximize a team's ability to consistently deliver high quality software, Kanban emphasizes two main practices. The first, visualizing the flow of work, requires that you map your team's workflow stages and configure a Kanban board to match. The second, constraining the amount of work in progress, requires that you set work-in-progress (WIP) limits. You're then ready to track progress on your Kanban board and monitor key metrics to reduce lead or cycle time. Your Kanban board turns your backlog into an interactive signboard, providing a visual flow of work. As work progresses from idea to completion, you update the items on the board. Each column represents a work stage, and each card represents a user story (blue cards) or a bug (red cards) at that stage of work. However, every team develops its own process over time, so the ability to customize the Kanban board to match the way your team works is critical for the successful delivery. + +1. In the vertical navigational pane of the Azure DevOps portal, in the list of the **Boards** items, select **Boards**. +1. On the **Boards** panel, click the **Configure board settings** cogwheel icon (directly to the right of the funnel icon). + + > **Note**: The team is emphasizing work done with data, so there is special attention paid to any task associated with accessing or storing data. + +1. On the **Settings** panel, select the **Tag colors** tab, click **+ Add tag color**, in the **Tag** textbox, type **`data`** and select the yellow rectangle. + + ![Screenshot of the tag settings.](media/EShop-WEB-tag_color_v1.png) + + > **Note**: Whenever a backlog item or bug is tagged with **data**, that tag will be highlighted. + +1. Select the **Annotations** tab. + + > **Note**: You can specify which **Annotations** you would like included on cards to make them easier to read and navigate. When an annotation is enabled, the child work items of that type are easily accessible by clicking the visualization on each card. + +1. Select the **Tests** tab. + + > **Note**: The **Tests** tab enables you to configure how tests appear and behave on the cards. + +1. Click **Save** to save and close the settings. +1. From the **Board** tab of the **EShop-WEB** panel, open the Work Item representing the **As a customer, I want to view new tutorials** backlog item. +1. From the detailed item view, at the top of the panel, to the right of the **0 comments** entry, click **Add tag**. +1. In the resulting textbox, type **`data`** and press the **Enter** key. +1. Repeat the previous step to add the **`ux`** tag. +1. Save these edits by clicking **Save and Close**. + + ![Screenshot of the two new tags visible on the card.](media/EShop-WEB-tags_v1.png) + + > **Note**: The two tags are now visible on the card, with the **data** tag highlighted in yellow as configured. + +1. On the **Boards** panel, click the **Configure board settings** cogwheel icon (directly to the right of the funnel icon). +1. On the **Settings** panel, select the **Columns** tab. + + > **Note**: This section allows you to add new stages to the workflow. + +1. Click **+ Add column**, under the **Column name** label, in the **Name** textbox, type **`QA Approved`** and, in the **WIP limit** textbox, type **1** + + > **Note**: The Work in progress limit of 1 indicates that only one work item should be in this stage at a time. You would ordinarily set this higher, but there are only two work items to demonstrate the feature. + +1. Notice the ellipsis next to the **QA Approved** column you created. Select **Move right** twice, so that the QA Approved column gets positioned in-between **Committed** and **Done**. +1. Click **Save** to save and close the settings. + +1. On the **Boards portal**, the **QA Approved** column is now visible in the Kanban board view. +1. Drag the **As a customer, I want to see tutorials I recently viewed** work item from the **Committed** stage into the **QA Approved** stage. +1. Drag the **As a customer, I want to view new tutorials** work item from the **Approved** stage into the **QA Approved** stage. + + ![Screenshot of the board that exceeds its WIP limit.](media/EShop-WEB-wip_limit_v1.png) + + > **Note**: The stage now exceeds its **WIP** limit and is colored red as a warning. + +1. Move the **As a customer, I want to see tutorials I recently viewed** backlog item back to **Committed**. +1. On the **Boards** panel, click the **Configure board settings** cogwheel icon (directly to the right of the funnel icon). +1. On the **Settings** panel, return to the **Columns** tab and select the **QA Approved** tab. + + > **Note**: A lag often exists between when work gets moved into a column and when work starts. To counter that lag and reveal the actual state of work in progress, you can turn on split columns. When split, each column contains two sub-columns: **Doing** and **Done**. Split columns let your team implement a pull model. Without split columns, teams push work forward, to signal that they've completed their stage of work. However, pushing it to the next stage doesn't necessarily mean that a team member immediately starts work on that item. + +1. On the **QA Approved** tab, enable the **Split column into doing and done** checkbox to create two separate columns. + + > **Note**: As your team updates the status of work as it progresses from one stage to the next, it helps that they agree on what **done** means. By specifying the **Definition of done** criteria for each Kanban column, you help share the essential tasks to complete before moving an item into a downstream stage. + +1. On the **QA Approved** tab, at the bottom of the panel, in the **Definition of done** textbox, type **`Passes **all** tests`**. +1. Click **Save** to save and close the settings. + + > **Note**: The **QA Approved** stage now has **Doing** and **Done** columns. You can also click the informational symbol (with letter **i** in a circle) next to the column header to read the **Definition of done**. You may need to refresh the browser to see changes. + +1. On the **Boards** panel, click the **Configure boards settings** cogwheel icon (directly to the right of the funnel icon). + + > **Note**: Your Kanban board supports your ability to visualize the flow of work as it moves from new to done. When you add **swimlanes**, you can also visualize the status of work that supports different service-level classes. You can create a swimlane to represent any other dimension that supports your tracking needs. + +1. On the **Settings** panel, select the **Swimlanes** tab. +1. On the **Swimlanes** tab, click **+ Add swimlane**, directly under the **Swimlane name** label, in the **Name** textbox, type **`Expedite`**. +1. In the **Color** dropdown list, select the **Green** rectangle. +1. Click **Save** to save and close the settings. + + ![Screenshot of Swimlane expedite creation.](media/EShop-WEB-swimlane_v1.png) + +1. Back on the **Board** tab of the **Boards** panel, drag and drop the **Committed** work item onto the **QA Approved \| Doing** stage of the **Expedite** swimlane so that it gets recognized as having priority when QA bandwidth becomes available. + + > **Note**: You may need to refresh the browser to make the swimlane visible. + +## Define dashboards + +Dashboards allow teams to visualize status and monitor progress across the project. At a glance, you can make informed decisions without having to drill down into other parts of your team project site. The Overview page provides access to a default team dashboard which you can customize by adding, removing, or rearranging the tiles. Each tile corresponds to a widget that provides access to one or more features or functions. + +1. In the vertical navigational pane of the Azure DevOps portal, select the **Overview** icon and, in the list of the **Overview** items, select **Dashboards**. +1. Select the **Overview** for the **eShopOnWeb Team** and review the existing dashboard. + + ![Screenshot of the dashboards overview page.](media/EShop-WEB-dashboard_v1.png) + +1. On the **Dashboards** pane, in the upper-right corner, select **+ New Dashboard**. + +1. On the **Create a dashboard** pane, in the **Name** textbox, type **`Product training`**, in the **Team** dropdown list, select the **EShop-WEB** team, and click **Create**. + + ![Screenshot of the create dashboard panel.](media/EShop-WEB-create_dash_v1.png) + +1. On the new dashboard pane, click **Add a widget**. +1. On the **Add Widget** panel, in the **Search widgets** textbox, type **`sprint`** to find existing widgets that focus on sprints. In the list of results, select **Sprint Overview** and click **Add**. +1. In the rectangle representing the newly added widget, click the **Settings** cogwheel icon and review the **Configuration** pane. + + > **Note**: The customization level will vary by widget. + +1. On the **Configuration** pane, click **Close** without making any changes. +1. Back on the **Add Widget** pane, in the **Search** textbox, type **`sprint`** again to find existing widgets that focus on sprints. In the list of results, select **Sprint Capacity** and click **Add**. + + > **Note**: If the widget shows "Set capacity to use the sprint capacity widget", you can select the **Set capacity** link to set the capacity. Set the Activity to Development and the Capacity to 1. Click **Save** and back to the dashboard. + +1. In the **Dashboard** view, at the top of the pane, click **Done Editing**. + + ![Screenshot of the dashboard with two new widgets.](media/EShop-WEB-finished_dashboard_v1.png) + + > **Note**: You can now review two important aspects of your current sprint on your custom dashboard. + + > **Note**: Another way of customizing dashboards is to generate charts based on work item queries, which you can share to a dashboard. + +1. In the vertical navigational pane of the Azure DevOps portal, select the **Boards** icon and, in the list of the **Boards** items, select **Queries**. +1. On the **Queries** pane, click **+ New query**. +1. On the **Editor** tab of **Queries > My Queries** pane, in the **Value** dropdown list of the **Work Item Type** row, select **Task**. +1. Click on **Add new clause**, and in the **Field** column, select **Area Path** and, in the corresponding **Value** dropdown list, select **eShopOnWeb\\EShop-WEB**. +1. Click **Save**. + + ![Screenshot of the new query in the query editor."](media/EShop-WEB-query_v1.png) + +1. In the **Enter name** textbox, type **`Web tasks`**, in the **Folder** dropdown list, select **Shared Queries**, and click **OK**. +1. On the **Queries > Shared Queries** pane, click **Web tasks** to open the query. +1. Select the **Charts** tab and click **New chart**. +1. On the **Configure Chart** panel, in the **Name** textbox, type **`Web tasks - By assignment`**, in the **Group by** dropdown list, select **Assigned To**, and click **Save chart** to save the changes. + + ![Screenshot of the new web tasks pie chart.](media/EShop-WEB-chart_v1.png) + + > **Note**: You can now add this chart to a dashboard. + +1. Return to the **Dashboards** section in the **Overview** Menu. From the **EShop-Web** section, select the **Product Training** dashboard you used earlier, to open it. + +1. Click **Edit** from the top menu. From the **Add Widget** list, search for **`Chart`** , and select **Chart for Work Items**. Click **Add** to add this widget to the EShop-Web dashboard. + +1. Click the **configure** (cogwheel) within the **Chart for Work Items** to open the widget settings. + +1. Accept the title as is. Under **Query**, select **Shared Queries / Web Tasks**. Keep **Pie** for Chart Type. Under **Group By**, select **Assigned To**. Keep the Aggregation (Count) and Sort (Value / Ascending) defaults. + +1. Confirm the configuration by clicking **Save**. + +1. Notice the query results pie chart is shown on the dashboard. **Save** the changes by pressing the **Done Editing** button on top. + +## Clean up resources + +You don't need to clean up your Azure DevOps organization or project, as they will remain available for you to use as a reference and portfolio item. Azure DevOps provides free tier usage that includes basic features for small teams. + +If you want to delete the project, you can do so by following these steps: + +1. In your browser navigate to the Azure DevOps portal at `https://aex.dev.azure.com`. +1. Navigate to the **eShopOnWeb** project you created. +1. On the project settings page, go to **Overview** and click **Delete** at the bottom of the page. +1. Type the project name to confirm deletion and click **Delete**. + +> **CAUTION:** Deleting a project deletes all work items, repositories, builds, and other project artifacts. If you used an existing project for this exercise, any existing resources outside the scope of this exercise will also be deleted. diff --git a/Instructions/basic/04-share-team-knowledge-azure-project-wiki.md b/Instructions/basic/04-share-team-knowledge-azure-project-wiki.md new file mode 100644 index 0000000..662b889 --- /dev/null +++ b/Instructions/basic/04-share-team-knowledge-azure-project-wiki.md @@ -0,0 +1,275 @@ +--- +lab: + topic: Basic + title: "Share Team Knowledge using Azure Project Wiki" + description: "Learn how to create and configure wikis in Azure DevOps, including managing markdown content and creating Mermaid diagrams." +--- + +# Share Team Knowledge using Azure Project Wiki + +**Estimated time:** 45 minutes + +You will learn how to create and configure wikis in Azure DevOps, including managing markdown content and creating Mermaid diagrams. Azure DevOps wikis provide a centralized place for teams to share knowledge, document processes, and maintain project information. + +## Before you start + +You need: + +- **Microsoft Edge** or an [Azure DevOps supported browser](https://docs.microsoft.com/azure/devops/server/compatibility) +- **Azure DevOps organization:** Create one if you don't have one +- **eShopOnWeb project:** Use the sample project from previous labs or create a new one + +## About Azure DevOps Wikis + +Azure DevOps provides two types of wikis: + +1. **Project wiki** - A wiki that exists separately from your repositories +2. **Code wiki** - A wiki created from content stored in a Git repository + +Key features: + +- **Markdown support** with rich formatting capabilities +- **Mermaid diagrams** for creating flowcharts and sequence diagrams +- **Image support** with drag-and-drop functionality +- **Version control** with revision history +- **Cross-reference links** to work items, code, and other wikis +- **Collaborative editing** with concurrent user support + +## Set up the project and repository + +First, ensure you have the eShopOnWeb project ready for this lab. + +1. In your browser, open your Azure DevOps organization +2. Open the **eShopOnWeb** project (create one if you don't have it) +3. Navigate to **Repos > Files** +4. Ensure you're on the **main** branch +5. Review the content of the main branch + +### Download brand image for later use + +1. In the **Files** pane, expand the **src** folder and browse to **Web > wwwroot > images** subfolder +2. In the **Images** subfolder, locate the **brand.png** entry +3. Hover over its right end to reveal the vertical ellipsis (three dots) menu +4. Click **Download** to download the **brand.png** file to your local computer + +> **Note**: You will use this image in the next exercise. + +### Create a Documents folder + +1. From within **Repos**, select **Files** +2. Notice the **eShopOnWeb** Repo title on top of the folder structure +3. **Select the ellipsis (3 dots)**, Choose **New > Folder** +4. Provide **`Documents`** as title for the New Folder name +5. As a repo doesn't allow empty folders, provide **`README.md`** as New File name +6. Click **Create** to confirm the creation +7. The README.md file will open in view mode +8. Click the **Commit** button to save the changes +9. In the Commit window, confirm by pressing **Commit** + +## Publish code as a wiki + +You can publish content from a Git repository as a wiki. This is useful for maintaining documentation alongside your code. + +### Publish a branch as wiki + +1. In the Azure DevOps vertical menu on the left side, click **Overview** +2. In the **Overview** section, select **Wiki** +3. Select **Publish code as wiki** +4. On the **Publish code as wiki** pane, specify the following settings and click **Publish**: + + | Setting | Value | + | ---------- | ---------------------------- | + | Repository | **eShopOnWeb** | + | Branch | **main** | + | Folder | **/Documents** | + | Wiki name | **`eShopOnWeb (Documents)`** | + +This will automatically open the Wiki section with the editor. + +### Create wiki content + +1. In the Wiki Page **Title** field, enter: `Welcome to our Online Retail Store!` + +2. In the body of the Wiki Page, paste the following content: + + ```markdown + ## Welcome to Our Online Retail Store! + + At our online retail store, we offer a **wide range of products** to meet the **needs of our customers**. Our selection includes everything from _clothing and accessories to electronics, home decor, and more_. + + We pride ourselves on providing a seamless shopping experience for our customers. Our website offers the following benefits: + + 1. user-friendly, + 1. and easy to navigate, + 1. allowing you to find what you're looking for, + 1. quickly and easily. + + We also offer a range of **_payment and shipping options_** to make your shopping experience as convenient as possible. + + ### About the team + + Our team is dedicated to providing exceptional customer service. If you have any questions or concerns, our knowledgeable and friendly support team is always available to assist you. We also offer a hassle-free return policy, so if you're not completely satisfied with your purchase, you can easily return it for a refund or exchange. + + ### Physical Stores + + | Location | Area | Hours | + | ----------- | --------------------- | --------------- | + | New Orleans | Home and DIY | 07.30am-09.30pm | + | Seattle | Gardening | 10.00am-08.30pm | + | New York | Furniture Specialists | 10.00am-09.00pm | + + ## Our Store Qualities + + - We're committed to providing high-quality products + - Our products are offered at affordable prices + - We work with reputable suppliers and manufacturers + - We ensure that our products meet our strict standards for quality and durability. + - Plus, we regularly offer sales and discounts to help you save even more. + + # Summary + + Thank you for choosing our online retail store for your shopping needs. We look forward to serving you! + ``` + +3. This sample text demonstrates several common Markdown syntax features: + + - **Titles and subtitles** (## and ###) + - **Bold text** (\*\*) + - **Italic text** (\*) + - **Numbered lists** (1.) + - **Bullet lists** (-) + - **Tables** with headers and data + +4. Once finished, press the **Save** button in the upper right corner + +5. **Refresh** your browser, or select any other DevOps portal option and return to the Wiki section + +6. Notice you are now presented with the **eShopOnWeb (Documents)** Wiki, with **Welcome to our Online Retail Store** as the **HomePage** + +### Manage published wiki content + +1. In the vertical menu on the left side, click **Repos** +2. Ensure the dropdown menu displays the **eShopOnWeb** repo and **main** branch +3. In the repo folder hierarchy, select the **Documents** folder +4. Select the **Welcome-to-our-Online-Retail-Store!.md** file +5. Notice how the Markdown format is visible as raw text, allowing you to edit the file content from here as well + +> **Note**: Since the Wiki source files are handled as source code, all traditional source control practices (Clone, Pull Requests, Approvals, etc.) can be applied to Wiki pages. + +## Create and manage a project wiki + +You can create and manage wikis independently of existing repositories. This provides flexibility for documentation that doesn't need to be version-controlled with your code. + +### Create a project wiki with Mermaid diagram + +1. In the Azure DevOps portal, navigate to the **Wiki** pane of the **eShopOnWeb** project +2. With the **eShopOnWeb (Documents)** wiki content selected, click the dropdown list header at the top +3. In the dropdown list, select **Create new project wiki** +4. In the **Page title** text box, type: `Project Design` +5. Place the cursor in the body of the page +6. Click the left-most icon in the toolbar (header setting) and select **Header 1** +7. This adds the hash character (**#**) at the beginning of the line +8. After the **#** character, type: `Authentication and Authorization` and press **Enter** +9. Click the header setting icon again and select **Header 2** +10. After the **##** characters, type: `Azure DevOps OAuth 2.0 Authorization Flow` and press **Enter** + +### Add a Mermaid diagram + +Mermaid is a diagramming and charting tool that renders Markdown-inspired text definitions to create diagrams dynamically. + +1. **Copy and paste** the following code to insert a Mermaid diagram: + + ```text + ::: mermaid + sequenceDiagram + participant U as User + participant A as Your app + participant D as Azure DevOps + U->>A: Use your app + A->>D: Request authorization for user + D-->>U: Request authorization + U->>D: Grant authorization + D-->>A: Send authorization code + A->>D: Get access token + D-->>A: Send access token + A->>D: Call REST API with access token + D-->>A: Respond to REST API + A-->>U: Relay REST API response + ::: + ``` + +> **Note**: For details about Mermaid syntax, refer to [About Mermaid](https://mermaid-js.github.io/mermaid/#/) + +2. In the preview pane, click **Load diagram** and review the outcome +3. The output should resemble a flowchart that illustrates [OAuth 2.0 authorization flow](https://docs.microsoft.com/azure/devops/integrate/get-started/authentication/oauth) + +### Save with revision message + +1. In the upper right corner of the editor pane, click the down-facing caret next to the **Save** button +2. In the dropdown menu, click **Save with revision message** +3. In the **Save page** dialog box, type: `Authentication and authorization section with the OAuth 2.0 Mermaid diagram` +4. Click **Save** + +### Add an image section + +1. Place the cursor at the end of the Mermaid element and press **Enter** to add a new line +2. Click the header setting icon and select **Header 2** +3. After the **##** characters, type: `User Interface` and press **Enter** +4. In the toolbar, click the paper clip icon representing **Insert a file** +5. In the **Open** dialog box, navigate to where you downloaded the **brand.png** file +6. Select the **brand.png** file and click **Open** +7. Review the preview pane and verify that the image displays properly +8. Click the down-facing caret next to **Save** and select **Save with revision message** +9. In the **Save page** dialog box, type: `User Interface section with the eShopOnWeb image` +10. Click **Save** +11. In the upper right corner, click **Close** + +## Manage a project wiki + +You'll now learn how to manage wiki content, including reverting changes and organizing pages. + +### Revert changes using revision history + +1. With the **Project Design** wiki selected, in the upper right corner, click the vertical ellipsis symbol +2. In the dropdown menu, click **View revisions** +3. On the **Revisions** pane, click the entry representing the most recent change +4. On the resulting pane, review the comparison between the previous and current version +5. Click **Revert** +6. When prompted for confirmation, click **Revert** again +7. Then click **Browse Page** +8. Back on the **Project Design** pane, verify that the change was successfully reverted + +### Add and organize wiki pages + +1. On the **Project Design** pane, at the bottom left corner, click **+ New page** +2. In the **Page title** text box, type: `Project Design Overview` +3. Click **Save**, then click **Close** +4. Back in the pane listing the pages within the **Project Design** project wiki, locate the **Project Design Overview** entry +5. Select it with the mouse pointer and drag and drop it above the **Project Design** page entry +6. Confirm the changes by pressing the **Move** button in the appearing window +7. Verify that the **Project Design Overview** entry is listed as the top level page with the home icon designating it as the wiki home page + +## Best practices for wiki management + +- **Use clear, descriptive titles** for pages and sections +- **Organize content hierarchically** with proper heading levels +- **Link between pages** using wiki syntax `[[Page Name]]` +- **Include images and diagrams** to enhance understanding +- **Use tables** for structured data presentation +- **Maintain consistency** in formatting and style +- **Review and update content regularly** to keep it current +- **Use revision messages** to track changes meaningfully + +## Summary + +In this lab, you learned how to: + +- **Create and publish code as a wiki** from a Git repository +- **Manage markdown content** with rich formatting +- **Create Mermaid diagrams** for visual documentation +- **Add and organize images** in wiki pages +- **Manage project wikis** independently of code repositories +- **Use revision history** to track and revert changes +- **Organize wiki pages** with hierarchical structure + +Azure DevOps wikis provide a powerful platform for team knowledge sharing, combining the flexibility of markdown with the structure of organized documentation. They integrate seamlessly with your development workflow while providing the collaboration features needed for effective team communication. diff --git a/Instructions/basic/05-version-control-git-azure-repos.md b/Instructions/basic/05-version-control-git-azure-repos.md new file mode 100644 index 0000000..6c06b9d --- /dev/null +++ b/Instructions/basic/05-version-control-git-azure-repos.md @@ -0,0 +1,392 @@ +--- +lab: + topic: Basic + title: "Version Control with Git in Azure Repos" + description: "Learn to establish a local Git repository, which can easily be synchronized with a centralized Git repository in Azure DevOps. You'll also learn about Git branching and merging support using Visual Studio Code." +--- + +# Version Control with Git in Azure Repos + +In this lab, you'll learn to establish a local Git repository, which can easily be synchronized with a centralized Git repository in Azure DevOps. In addition, you'll learn about Git branching and merging support. You'll use Visual Studio Code, but the same processes apply to using any Git-compatible client. + +Azure DevOps supports two types of version control, Git and Team Foundation Version Control (TFVC). Git is a distributed version control system where each developer has a copy of the source repository on their dev machine. Git is the default version control provider for new projects and should be used unless you need centralized version control features in TFVC. + +You will learn how to: + +- Clone an existing repository. +- Save work with commits. +- Review history of changes. +- Work with branches by using Visual Studio Code. + +This lab takes approximately **45** minutes to complete. + +## Before you start + +To complete the lab, you need: + +- **Microsoft Edge** or an [Azure DevOps supported browser.](https://docs.microsoft.com/azure/devops/server/compatibility) +- An Azure DevOps organization. If you don't already have one, create one by following the instructions at [Create an organization or project collection](https://docs.microsoft.com/azure/devops/organizations/accounts/create-organization). +- **Git 2.47.0 or later**: If you don't have Git installed yet, navigate to the [Git for Windows download page](https://gitforwindows.org/) download it, and install it. +- **Visual Studio Code**: If you don't have Visual Studio Code installed yet, navigate to the [Visual Studio Code download page](https://code.visualstudio.com/), download it, and install it. +- **Visual Studio C# extension**: If you don't have Visual Studio C# extension installed yet, navigate to the [C# extension installation page](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) and install it. + +### Set up Azure DevOps organization (if needed) + +If you don't already have an Azure DevOps organization, follow these steps: + +1. Use a private browser session to get a new **personal Microsoft Account (MSA)** at `https://account.microsoft.com` (skip if you already have one). +1. Using the same browser session, sign up for a free Azure subscription at `https://azure.microsoft.com/free` (skip if you already have one). +1. Open a browser and navigate to Azure portal at `https://portal.azure.com`, then search at the top of the Azure portal screen for **Azure DevOps**. In the resulting page, click **Azure DevOps organizations**. +1. Next, click on the link labelled **My Azure DevOps Organizations** or navigate directly to `https://aex.dev.azure.com`. +1. On the **We need a few more details** page, select **Continue**. +1. In the drop-down box on the left, choose **Default Directory**, instead of **Microsoft Account**. +1. If prompted (_"We need a few more details"_), provide your name, e-mail address, and location and click **Continue**. +1. Back at `https://aex.dev.azure.com` with **Default Directory** selected click the blue button **Create new organization**. +1. Accept the _Terms of Service_ by clicking **Continue**. +1. If prompted (_"Almost done"_), leave the name for the Azure DevOps organization at default (it needs to be a globally unique name) and pick a hosting location close to you from the list. +1. Once the newly created organization opens in **Azure DevOps**, select **Organization settings** in the bottom left corner. +1. At the **Organization settings** screen select **Billing** (opening this screen takes a few seconds). +1. Select **Setup billing** and on the right-hand side of the screen, select your **Azure Subscription** and then select **Save** to link the subscription with the organization. +1. Once the screen shows the linked Azure Subscription ID at the top, change the number of **Paid parallel jobs** for **MS Hosted CI/CD** from 0 to **1**. Then select **SAVE** button at the bottom. + + > **Note**: You may **wait a couple of minutes before using the CI/CD capabilities** so that the new settings are reflected in the backend. Otherwise, you will still see the message _"No hosted parallelism has been purchased or granted"_. + +1. In **Organization Settings**, go to section **Pipelines** and click **Settings**. +1. Toggle the switch to **Off** for **Disable creation of classic build pipelines** and **Disable creation of classic release pipelines**. +1. In **Organization Settings**, go to section **Security** and click **Policies**. +1. Toggle the switch to **On** for **Allow public projects**. + +### Create and configure the Azure DevOps project (if needed) + +1. Open your browser and navigate to your Azure DevOps organization. +1. Select the **New Project** option and use the following settings: + - name: **eShopOnWeb** + - visibility: **Private** + - Advanced: Version Control: **Git** + - Advanced: Work Item Process: **Scrum** +1. Select **Create**. + + ![Screenshot of the create new project panel.](media/create-project.png) + +### Import eShopOnWeb git repository (if needed) + +1. Open the previously created **eShopOnWeb** project. +1. Select the **Repos > Files**, **Import a Repository** and then select **Import**. +1. On the **Import a Git Repository** window, paste the following URL `https://github.com/MicrosoftLearning/eShopOnWeb.git` and select **Import**: + + ![Screenshot of the import repository panel.](media/import-repo.png) + +1. The repository is organized the following way: + + - **.ado** folder contains Azure DevOps YAML pipelines. + - **.devcontainer** folder container setup to develop using containers (either locally in VS Code or GitHub Codespaces). + - **.azure** folder contains Bicep & ARM infrastructure as code templates. + - **.github** folder container YAML GitHub workflow definitions. + - **src** folder contains the .NET 8 website used on the lab scenarios. + +1. Leave the web browser window open. +1. Go to **Repos > Branches**. +1. Hover on the **main** branch then click the ellipsis on the right of the column. +1. Click on **Set as default branch**. + +### Configure Git and Visual Studio Code + +1. On the lab computer, open **Visual Studio Code**. +1. In the Visual Studio Code interface, from the main menu, select **Terminal \| New Terminal** to open the **TERMINAL** pane. +1. Make sure that the current Terminal is running **PowerShell** by checking if the drop-down list at the top right corner of the **TERMINAL** pane shows **1: powershell** + + > **Note**: To change the current Terminal shell to **PowerShell** click the drop-down list at the top right corner of the **TERMINAL** pane and click **Select Default Shell**. At the top of the Visual Studio Code window select your preferred terminal shell **Windows PowerShell** and click the plus sign on the right-hand side of the drop-down list to open a new terminal with the selected default shell. + +1. In the **TERMINAL** pane, run the following command below to configure the credential helper. + + ```git + git config --global credential.helper wincred + ``` + +1. In the **TERMINAL** pane, run the following commands to configure a user name and email for Git commits (replace the placeholders in braces with your preferred user name and email eliminating the < and > symbols): + + ```git + git config --global user.name "John Doe" + git config --global user.email johndoe@example.com + ``` + +## Clone an existing repository + +In this exercise, you will use Visual Studio Code to commit changes to the **main** branch of the **eShopOnWeb** repository. + +> **Note**: The **main** branch is the default branch in the **eShopOnWeb** repository and is the branch that you will use for the rest of the lab. + +1. Switch to the the web browser displaying your Azure DevOps organization with the **eShopOnWeb** project you generated in the previous exercise. +1. In the vertical navigational pane of the Azure DevOps portal, select the **Repos** icon. + +1. In the upper right corner of the **eShopOnWeb** repository pane, click **Clone**. + + ![Screenshot of the clone Git repository.](media/clone-repo.png) + + > **Note**: Getting a local copy of a Git repo is called _cloning_. Every mainstream development tool supports this and will be able to connect to Azure Repos to pull down the latest source to work with. + +1. On the **Clone Repository** panel, with the **HTTPS** Command line option selected, click the **Copy to clipboard** button next to the repo clone URL. + + > **Note**: You can use this URL with any Git-compatible tool to get a copy of the codebase. + +1. Close the **Clone Repository** panel. +1. Switch to **Visual Studio Code** running on your lab computer. +1. Click the **View** menu header and, in the drop-down menu, click **Command Palette**. + + > **Note**: The Command Palette provides an easy and convenient way to access a wide variety of tasks, including those implemented as 3rd party extensions. You can use the keyboard shortcut **Ctrl+Shift+P** or **F1** to open it. + +1. At the Command Palette prompt, run the **Git: Clone** command. + + ![Screenshot of the VS Code command palette.](media/vscode-command.png) + + > **Note**: To see all relevant commands, you can start by typing **Git**. + +1. In the **Provide repository URL or pick a repository source** text box, paste the repo clone URL you copied earlier in this task and press the **Enter** key. +1. Within the **Select Folder** dialog box, navigate to the C: drive, create a new folder named **Git**, select it, and then click **Select as Repository Destination**. +1. When prompted, log in to your Azure DevOps account. +1. After the cloning process completes, once prompted, in the Visual Studio Code, click **Open** to open the cloned repository. + + > **Note**: You can ignore warnings you might receive regarding problems with loading of the project. The solution may not be in the state suitable for a build, but we're going to focus on working with Git, so building the project is not required. + +## Save work with commits + +When you make changes to your files, Git will record the changes in the local repository. You can select the changes that you want to commit by staging them. Commits are always made against your local Git repository, so you don't have to worry about the commit being perfect or ready to share with others. You can make more commits as you continue to work and push the changes to others when they are ready to be shared. + +Git commits consists of the following: + +- The file(s) changed in the commit. Git keeps the contents of all file changes in your repo in the commits. This keeps it fast and allows intelligent merging. +- A reference to the parent commit(s). Git manages your code history using these references. +- A message describing a commit. You give this message to Git when you create the commit. It's a good idea to keep this message descriptive, but to the point. + +### Commit changes + +1. In the Visual Studio Code window, at the top of the vertical toolbar, select the **EXPLORER** tab, navigate to the **/eShopOnWeb/src/Web/Program.cs** file and select it. This will automatically display its content in the details pane. +1. On the first line add the following comment: + + ```csharp + // My first change + ``` + + > **Note**: It doesn't really matter what the comment is since the goal is just to make a change. + +1. Press **Ctrl+S** to save the change. +1. In the Visual Studio Code window, select the **SOURCE CONTROL** tab to verify that Git recognized the latest change to the file residing in the local clone of the Git repository. +1. With the **SOURCE CONTROL** tab selected, at the top of the pane, in the textbox, type **`My commit`** as the commit message and press **Ctrl+Enter** to commit it locally. + + ![Screenshot of the first commit.](media/first-commit.png) + +1. If prompted whether you would like to automatically stage your changes and commit them directly, click **Always**. + + > **Note**: We will discuss **staging** later in the lab. + +1. In the lower left corner of the Visual Studio Code window, to the right of the **main** label, note the **Synchronize Changes** icon of a circle with two vertical arrows pointing in the opposite directions and the number **1** next to the arrow pointing up. Click the icon and, if prompted, whether to proceed, click **OK** to push and pull commits to and from **origin/main**. + +### Review commits + +1. Switch to the web browser window displaying the Azure DevOps interface. +1. In the vertical navigational pane of the Azure DevOps portal, in the **Repos** section, select **Commits**. +1. Verify that your commit appears at the top of list. + + ![Screenshot of the ADO repo commits.](media/ado-commit.png) + +### Stage changes + +Staging changes allows you to selectively add certain files to a commit while passing over the changes made in other files. + +1. Switch back to the **Visual Studio Code** window. +1. Update the open **Program.cs** class by changing the first comment with the following, and saving the file. + + ```csharp + //My second change + ``` + +1. In the Visual Studio Code window, switch back the **EXPLORER** tab, navigate to the **/eShopOnWeb/src/Web/Constants.cs** file and select it. This will automatically display its content in the details pane. +1. Add to the **Constants.cs** file a comment on the first line and save the file. + + ```csharp + // My third change + ``` + +1. In the Visual Studio Code window, switch to the **SOURCE CONTROL** tab, hover the mouse pointer over the **Program.cs** entry, and click the plus sign on the right side of that entry. + + > **Note**: This stages the change to the **Program.cs** file only, preparing it for commit without **Constants.cs**. + +1. With the **SOURCE CONTROL** tab selected, at the top of the pane, in the textbox, type **`Added comments`** as the commit message. + + ![Screenshot of the staged changes.](media/staged-changes.png) + +1. At the top of the **SOURCE CONTROL** tab, click the ellipsis symbol, in the drop-down menu, select **Commit** and, in the cascading menu, select **Commit Staged**. +1. In the lower left corner of the Visual Studio Code window, click the **Synchronize Changes** button to synchronize the committed changes with the server and, if prompted, whether to proceed, click **OK** to push and pull commits to and from **origin/main**. + + > **Note**: Note that since only the staged change was committed, the other change is still pending to be synchronized. + +## Review history + +Git uses the parent reference information stored in each commit to manage a full history of your development. You can easily review this commit history to find out when file changes were made and determine differences between versions of your code using the terminal or from one of the many available Visual Studio Code extensions. You can also review changes by using the Azure DevOps portal. + +Git's use of the **Branches and Merges** feature works through pull requests, so the commit history of your development doesn't necessarily form a straight, chronological line. When you use history to compare versions, think in terms of file changes between two commits instead of file changes between two points in time. A recent change to a file in the main branch may have come from a commit created two weeks ago in a feature branch that was merged yesterday. + +### Compare files + +1. With the **SOURCE CONTROL** tab of the Visual Studio Code window open, select **Constants.cs** representing the non-staged version of the file. + + ![Screenshot of the file comparison.](media/file-comparison.png) + + > **Note**: A comparison view is opened to enable you to easily locate the changes you've made. In this case, it's just the one comment. + +1. Switch to the web browser window displaying the **Commits** pane of the **Azure DevOps** portal to review the source branches and merges. These provide a convenient way to visualize when and how changes were made to the source. +1. Scroll down to the **My commit** entry (pushed before) and hover the mouse pointer over it to reveal the ellipsis symbol on the right side. +1. Click the ellipsis, in the dropdown menu, select **Browse Files**, and review the results. + + ![Screenshot of the commit browse.](media/commit-browse.png) + + > **Note**: This view represents the state of the source corresponding to the commit, allowing you to review and download each of source files. + +## Work with branches + +You can manage in your Azure DevOps Git repo from the **Branches** view of **Azure Repos** in the Azure DevOps portal. You can also customize the view to track the branches you care most about so you can stay on top of changes made by your team. + +Committing changes to a branch will not affect other branches and you can share branches with others without having to merge the changes into the main project. You can also create new branches to isolate changes for a feature or a bug fix from your main branch and other work. Since the branches are lightweight, switching between branches is quick and easy. Git does not create multiple copies of your source when working with branches, but rather uses the history information stored in commits to recreate the files on a branch when you start working on it. Your Git workflow should create and use branches for managing features and bugfixes. The rest of the Git workflow, such as sharing code and reviewing code with pull requests, all work through branches. Isolating work in branches makes it very simple to change what you are working on by simply changing your current branch. + +### Create a new branch in your local repository + +1. Switch to **Visual Studio Code** running on your lab computer. +1. With the **SOURCE CONTROL** tab selected, in the lower left corner of the Visual Studio Code window, click **main**. +1. In the pop-up window, select **+ Create new branch from...**. + + ![Screenshot of the create branch option.](media/create-branch.png) + +1. In the **Select a ref to create the branch from** textbox, select **main** as the reference branch. +1. In the **Branch name** textbox, type **`dev`** to specify the new branch and press **Enter**. + + > **Note**: At this point, you are automatically switched to the **dev** branch. + +### Delete a branch + +Git keeps track of which branch you are working on and makes sure that, when you check out a branch, your files match the most recent commit on that branch. Branches let you work with multiple versions of the source code in the same local Git repository at the same time. You can use Visual Studio Code to publish, check out and delete branches. + +1. In the **Visual Studio Code** window, with the **SOURCE CONTROL** tab selected, in the lower left corner of the Visual Studio Code window, click the **Publish changes** icon (directly to the right of the **dev** label representing your newly created branch). +1. Switch to the web browser window displaying the **Commits** pane of the **Azure DevOps** portal and select **Branches**. +1. On the **Mine** tab of the **Branches** pane, verify that the list of branches includes **dev**. +1. Hover the mouse pointer over the **dev** branch entry to reveal the ellipsis symbol on the right side. +1. Click the ellipsis, in the pop-up menu, select **Delete branch**, and, when prompted for confirmation, click **Delete**. + + ![Screenshot of the delete branch option.](media/delete-branch.png) + +1. Switch back to the **Visual Studio Code** window and, with the **SOURCE CONTROL** tab selected, in the lower left corner of the Visual Studio Code window, click the **dev** entry. This will display the existing branches in the upper portion of the Visual Studio Code window. +1. Verify that now there are two **dev** branches listed. +1. Go to the web browser displaying the **Mine** tab of the **Branches** +1. On the **Mine** tab of the **Branches** pane, select the **All** tab. +1. On the **All** tab of the **Branches** pane, in the **Search branch name** text box, type **`dev`**. +1. Review the **Deleted branches** section containing the entry representing the newly deleted branch. +1. In the **Deleted branches** section, hover the mouse pointer over the **dev** branch entry to reveal the ellipsis symbol on the right side. +1. Click the ellipsis, in the pop-up menu and select **Restore branch**. + + ![Screenshot of the restore branch option.](media/restore-branch.png) + + > **Note**: You can use this functionality to restore a deleted branch as long as you know its exact name. + +### Branch Policies + +You will use the Azure DevOps portal to add policies to the main branch and only allow changes using Pull Requests that comply with the defined policies. You want to ensure that changes in a branch are reviewed before they are merged. + +For simplicity we will work directly on the web browser repo editor (working directly in origin), instead of using the local clone in VS code (recommended for real scenarios). + +1. Switch to the web browser displaying the **Mine** tab of the **Branches** pane in the Azure DevOps portal. +1. On the **Mine** tab of the **Branches** pane, hover the mouse pointer over the **main** branch entry to reveal the ellipsis symbol on the right side. +1. Click the ellipsis and, in the pop-up menu, select **Branch Policies**. + + ![Screenshot of the branch policies option.](media/branch-policies.png) + +1. On the **main** tab of the repository settings, enable the option for **Require minimum number of reviewers**. Add **1** reviewer and check the box **Allow requestors to approve their own changes**(as you are the only user in your project for the lab) +1. On the **main** tab of the repository settings, enable the option for **Check for linked work items** and leave it with **Required** option. + + ![Screenshot of the policy settings.](media/policy-settings.png) + +### Testing branch policy + +1. In the vertical navigational pane of the of the Azure DevOps portal, in the **Repos>Files**, make sure the **main** branch is selected (dropdown above shown content). +1. To make sure policies are working, try making a change and committing it on the **main** branch, navigate to the **/eShopOnWeb/src/Web/Program.cs** file and select it. This will automatically display its content in the details pane. +1. On the first line add the following comment: + + ```csharp + // Testing main branch policy + ``` + +1. Click on **Commit > Commit**. You will see a warning: changes to the main branch can only be done using a Pull Request. + + ![Screenshot of the policy denied commit.](media/policy-denied.png) + +1. Click on **Cancel** to skip the commit. + +### Working with Pull Requests + +You will use the Azure DevOps portal to create a Pull Request, using the **dev** branch to merge a change into the protected **main** branch. An Azure DevOps work item will be linked to the changes to be able to trace pending work with code activity. + +1. In the vertical navigational pane of the of the Azure DevOps portal, in the **Boards** section, select **Work Items**. +1. Click on **+ New Work Item > Product Backlog Item**. In title field, write **Testing my first PR** and click on **Save**. +1. Now go back to the vertical navigational pane of the of the Azure DevOps portal, in the **Repos>Files**, make sure the **dev** branch is selected. +1. Navigate to the **/eShopOnWeb/src/Web/Program.cs** file and make the following change on the first line: + + ```csharp + // Testing my first PR + ``` + +1. Click on **Commit > Commit** (leave default commit message). This time the commit works, **dev** branch has no policies. +1. A message will pop-up, proposing to create a Pull Request (as you **dev** branch is now ahead in changes, compared to **main**). Click on **Create a Pull Request**. + + ![Screenshot of the create a Pull Request option.](media/create-pr.png) + +1. In the **New pull request** tab, leave defaults and click on **Create**. +1. The Pull Request will show some failed/pending requirements, based on the policies applied to our target **main** branch. + + - Proposed changes should have a work item linked + - At least 1 user should review and approve the changes. + +1. On the right side options, click on the **+** button next to **Work Items**. Link the previously created work item to the Pull Request by clicking on it. You will see one of the requirements changes status. + + ![Screenshot of the link work item.](media/link-wit.png) + +1. Next, open the **Files** tab to review the proposed changes. In a more complete Pull Request, you would be able to review files one by one (marked as reviewed) and open comments for lines that may not be clear (hovering the mouse over the line number gives you an option to post a comment). +1. Go back to the **Overview** tab, and on the top-right click on **Approve**. All the requirements will change to green. Now you can click on **Complete**. +1. On the **Complete Pull Request** tab, multiple options will be given before completing the merge: + + - **Merge Type**: 4 merge types are offered, you can review them [here](https://learn.microsoft.com/azure/devops/repos/git/complete-pull-requests?view=azure-devops&tabs=browser#complete-a-pull-request) or observing the given animations. Choose **Merge (no fast forward)**. + - **Post-complete options**: + - Check **Complete associated work item...**. It will move associated PBI to **Done** state. + +1. Click on **Complete Merge** + +### Applying tags + +The product team has decided that the current version of the site should be released as v1.1.0-beta. + +1. In the vertical navigational pane of the of the Azure DevOps portal, in the **Repos** section, select **Tags**. +1. In the **Tags** pane, click **New tag**. +1. In the **Create a tag** panel, in the **Name** text box, type **`v1.1.0-beta`**, in the **Based on** drop-down list leave the **main** entry selected, in the **Description** text box, type **`Beta release v1.1.0`** and click **Create**. + + > **Note**: You have now tagged the repository at this release (the latest commit gets linked to the tag). You could tag commits for a variety of reasons and Azure DevOps offers the flexibility to edit and delete them, as well as manage their permissions. + +## Remove Branch Policies + +When going through the different course labs in the order they are presented, the branch policy configured during this lab will block exercises in future labs. Therefore, we want you to remove the configured branch policies. + +1. From the Azure DevOps **eShopOnWeb** Project view, navigate to **Repos** and select **Branches**. Select the **Mine** tab of the **Branches** pane. +1. On the **Mine** tab of the **Branches** pane, hover the mouse pointer over the **main** branch entry to reveal the ellipsis symbol (the ...) on the right side. +1. Click the ellipsis and, in the pop-up menu, select **Branch Policies**. +1. On the **main** tab of the repository settings, disable the option for **Require minimum number of reviewers**. +1. On the **main** tab of the repository settings, disable the option for **Check for linked work items**. +1. You have now disabled/removed the branch policies for the main branch. + +## Clean up resources + +You don't need to clean up your Azure DevOps organization or project, as they will remain available for you to use as a reference and portfolio item. Azure DevOps provides free tier usage that includes basic features for small teams. + +If you want to delete the project, you can do so by following these steps: + +1. In your browser navigate to the Azure DevOps portal at `https://aex.dev.azure.com`. +1. Navigate to the **eShopOnWeb** project you created. +1. On the project settings page, go to **Overview** and click **Delete** at the bottom of the page. +1. Type the project name to confirm deletion and click **Delete**. + +> **CAUTION:** Deleting a project deletes all work items, repositories, builds, and other project artifacts. If you used an existing project for this exercise, any existing resources outside the scope of this exercise will also be deleted. diff --git a/Instructions/basic/media/3sprints_v1.png b/Instructions/basic/media/3sprints_v1.png new file mode 100644 index 0000000..d10a524 Binary files /dev/null and b/Instructions/basic/media/3sprints_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-3sprints_v1.png b/Instructions/basic/media/EShop-WEB-3sprints_v1.png new file mode 100644 index 0000000..85c9409 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-3sprints_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-_boards_v1.png b/Instructions/basic/media/EShop-WEB-_boards_v1.png new file mode 100644 index 0000000..3a5423d Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-_boards_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-add_pb_v1.png b/Instructions/basic/media/EShop-WEB-add_pb_v1.png new file mode 100644 index 0000000..b6ad20e Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-add_pb_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-areas_v1.png b/Instructions/basic/media/EShop-WEB-areas_v1.png new file mode 100644 index 0000000..444daab Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-areas_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-backlog_v1.png b/Instructions/basic/media/EShop-WEB-backlog_v1.png new file mode 100644 index 0000000..2209081 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-backlog_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-board_pbis_v1.png b/Instructions/basic/media/EShop-WEB-board_pbis_v1.png new file mode 100644 index 0000000..5f4a2ac Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-board_pbis_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-capacity-setdevelopment_v1.png b/Instructions/basic/media/EShop-WEB-capacity-setdevelopment_v1.png new file mode 100644 index 0000000..20e0414 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-capacity-setdevelopment_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-capacity_v1.png b/Instructions/basic/media/EShop-WEB-capacity_v1.png new file mode 100644 index 0000000..f2d1d20 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-capacity_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-chart_v1.png b/Instructions/basic/media/EShop-WEB-chart_v1.png new file mode 100644 index 0000000..7c1b8c2 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-chart_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-child_feature_v1.png b/Instructions/basic/media/EShop-WEB-child_feature_v1.png new file mode 100644 index 0000000..9e94bef Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-child_feature_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-create_child_feature.png b/Instructions/basic/media/EShop-WEB-create_child_feature.png new file mode 100644 index 0000000..e177a62 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-create_child_feature.png differ diff --git a/Instructions/basic/media/EShop-WEB-create_dash_v1.png b/Instructions/basic/media/EShop-WEB-create_dash_v1.png new file mode 100644 index 0000000..68e66f7 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-create_dash_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-create_epic_v1.png b/Instructions/basic/media/EShop-WEB-create_epic_v1.png new file mode 100644 index 0000000..0fd17da Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-create_epic_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-custom_scrum_v1.png b/Instructions/basic/media/EShop-WEB-custom_scrum_v1.png new file mode 100644 index 0000000..e907e60 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-custom_scrum_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-dashboard_v1.png b/Instructions/basic/media/EShop-WEB-dashboard_v1.png new file mode 100644 index 0000000..8f43a14 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-dashboard_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-days_off_v1.png b/Instructions/basic/media/EShop-WEB-days_off_v1.png new file mode 100644 index 0000000..166b906 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-days_off_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-edit_iteration_v1.png b/Instructions/basic/media/EShop-WEB-edit_iteration_v1.png new file mode 100644 index 0000000..1760dd7 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-edit_iteration_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-epic_details_v1.png b/Instructions/basic/media/EShop-WEB-epic_details_v1.png new file mode 100644 index 0000000..3b54289 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-epic_details_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-epic_with_linked_item_v1.png b/Instructions/basic/media/EShop-WEB-epic_with_linked_item_v1.png new file mode 100644 index 0000000..c2d2122 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-epic_with_linked_item_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-finished_dashboard_v1.png b/Instructions/basic/media/EShop-WEB-finished_dashboard_v1.png new file mode 100644 index 0000000..c292490 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-finished_dashboard_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-inherited_v1.png b/Instructions/basic/media/EShop-WEB-inherited_v1.png new file mode 100644 index 0000000..4cd57eb Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-inherited_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-iterationsareas_v1.png b/Instructions/basic/media/EShop-WEB-iterationsareas_v1.png new file mode 100644 index 0000000..95062e5 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-iterationsareas_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-new2ap_v1.png b/Instructions/basic/media/EShop-WEB-new2ap_v1.png new file mode 100644 index 0000000..381edcb Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-new2ap_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-over_capacity_v1.png b/Instructions/basic/media/EShop-WEB-over_capacity_v1.png new file mode 100644 index 0000000..2fc733b Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-over_capacity_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-pbi_field_layout_v1.png b/Instructions/basic/media/EShop-WEB-pbi_field_layout_v1.png new file mode 100644 index 0000000..66ced38 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-pbi_field_layout_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-pbi_field_name_v1.png b/Instructions/basic/media/EShop-WEB-pbi_field_name_v1.png new file mode 100644 index 0000000..bf3e9da Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-pbi_field_name_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-pbi_v1.png b/Instructions/basic/media/EShop-WEB-pbi_v1.png new file mode 100644 index 0000000..35c80d4 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-pbi_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-pbis_v1.png b/Instructions/basic/media/EShop-WEB-pbis_v1.png new file mode 100644 index 0000000..17ff42c Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-pbis_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-qa_2columns_v1.png b/Instructions/basic/media/EShop-WEB-qa_2columns_v1.png new file mode 100644 index 0000000..26a334e Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-qa_2columns_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-qa_column_v1.png b/Instructions/basic/media/EShop-WEB-qa_column_v1.png new file mode 100644 index 0000000..dba1a39 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-qa_column_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-query_v1.png b/Instructions/basic/media/EShop-WEB-query_v1.png new file mode 100644 index 0000000..57daaa0 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-query_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-save_task_v1.png b/Instructions/basic/media/EShop-WEB-save_task_v1.png new file mode 100644 index 0000000..a9bc0c6 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-save_task_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-select_iteration_v1.png b/Instructions/basic/media/EShop-WEB-select_iteration_v1.png new file mode 100644 index 0000000..1dff649 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-select_iteration_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-sprint-green_v1.png b/Instructions/basic/media/EShop-WEB-sprint-green_v1.png new file mode 100644 index 0000000..3cb91fc Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-sprint-green_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-styles_v1.png b/Instructions/basic/media/EShop-WEB-styles_v1.png new file mode 100644 index 0000000..b6cd3ea Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-styles_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-sub_areas_v1.png b/Instructions/basic/media/EShop-WEB-sub_areas_v1.png new file mode 100644 index 0000000..adaa75c Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-sub_areas_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-swimlane_v1.png b/Instructions/basic/media/EShop-WEB-swimlane_v1.png new file mode 100644 index 0000000..783db20 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-swimlane_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-tag_color_v1.png b/Instructions/basic/media/EShop-WEB-tag_color_v1.png new file mode 100644 index 0000000..be96d3a Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-tag_color_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-tags_v1.png b/Instructions/basic/media/EShop-WEB-tags_v1.png new file mode 100644 index 0000000..faf407e Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-tags_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-verify_v1.png b/Instructions/basic/media/EShop-WEB-verify_v1.png new file mode 100644 index 0000000..e861a28 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-verify_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-view_backlog_v1.png b/Instructions/basic/media/EShop-WEB-view_backlog_v1.png new file mode 100644 index 0000000..b820212 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-view_backlog_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-wip_limit_v1.png b/Instructions/basic/media/EShop-WEB-wip_limit_v1.png new file mode 100644 index 0000000..b1ef55c Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-wip_limit_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-work_details_v1.png b/Instructions/basic/media/EShop-WEB-work_details_v1.png new file mode 100644 index 0000000..9aeea2c Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-work_details_v1.png differ diff --git a/Instructions/basic/media/EShop-WEB-work_details_window_v1.png b/Instructions/basic/media/EShop-WEB-work_details_window_v1.png new file mode 100644 index 0000000..08742a1 Binary files /dev/null and b/Instructions/basic/media/EShop-WEB-work_details_window_v1.png differ diff --git a/Instructions/basic/media/add_pb_v1.png b/Instructions/basic/media/add_pb_v1.png new file mode 100644 index 0000000..ee8f051 Binary files /dev/null and b/Instructions/basic/media/add_pb_v1.png differ diff --git a/Instructions/basic/media/ado-commit.png b/Instructions/basic/media/ado-commit.png new file mode 100644 index 0000000..b07880c Binary files /dev/null and b/Instructions/basic/media/ado-commit.png differ diff --git a/Instructions/basic/media/board_pbis_v1.png b/Instructions/basic/media/board_pbis_v1.png new file mode 100644 index 0000000..278d945 Binary files /dev/null and b/Instructions/basic/media/board_pbis_v1.png differ diff --git a/Instructions/basic/media/board_process.png b/Instructions/basic/media/board_process.png new file mode 100644 index 0000000..9bd1436 Binary files /dev/null and b/Instructions/basic/media/board_process.png differ diff --git a/Instructions/basic/media/branch-policies.png b/Instructions/basic/media/branch-policies.png new file mode 100644 index 0000000..2ac1b75 Binary files /dev/null and b/Instructions/basic/media/branch-policies.png differ diff --git a/Instructions/basic/media/chart_v1.png b/Instructions/basic/media/chart_v1.png new file mode 100644 index 0000000..ff058f6 Binary files /dev/null and b/Instructions/basic/media/chart_v1.png differ diff --git a/Instructions/basic/media/child_feature_v1.png b/Instructions/basic/media/child_feature_v1.png new file mode 100644 index 0000000..f767d95 Binary files /dev/null and b/Instructions/basic/media/child_feature_v1.png differ diff --git a/Instructions/basic/media/clone-repo.png b/Instructions/basic/media/clone-repo.png new file mode 100644 index 0000000..ea8aec0 Binary files /dev/null and b/Instructions/basic/media/clone-repo.png differ diff --git a/Instructions/basic/media/commit-browse.png b/Instructions/basic/media/commit-browse.png new file mode 100644 index 0000000..281fdc5 Binary files /dev/null and b/Instructions/basic/media/commit-browse.png differ diff --git a/Instructions/basic/media/create-branch.png b/Instructions/basic/media/create-branch.png new file mode 100644 index 0000000..88261b0 Binary files /dev/null and b/Instructions/basic/media/create-branch.png differ diff --git a/Instructions/basic/media/create-pr.png b/Instructions/basic/media/create-pr.png new file mode 100644 index 0000000..e690841 Binary files /dev/null and b/Instructions/basic/media/create-pr.png differ diff --git a/Instructions/basic/media/create-project.png b/Instructions/basic/media/create-project.png new file mode 100644 index 0000000..f10adb9 Binary files /dev/null and b/Instructions/basic/media/create-project.png differ diff --git a/Instructions/basic/media/create_dash_v1.png b/Instructions/basic/media/create_dash_v1.png new file mode 100644 index 0000000..6a732dd Binary files /dev/null and b/Instructions/basic/media/create_dash_v1.png differ diff --git a/Instructions/basic/media/create_epic_v1.png b/Instructions/basic/media/create_epic_v1.png new file mode 100644 index 0000000..e0162bc Binary files /dev/null and b/Instructions/basic/media/create_epic_v1.png differ diff --git a/Instructions/basic/media/create_project_v1.png b/Instructions/basic/media/create_project_v1.png new file mode 100644 index 0000000..550f4a2 Binary files /dev/null and b/Instructions/basic/media/create_project_v1.png differ diff --git a/Instructions/basic/media/custom_scrum_v1.png b/Instructions/basic/media/custom_scrum_v1.png new file mode 100644 index 0000000..e9d40da Binary files /dev/null and b/Instructions/basic/media/custom_scrum_v1.png differ diff --git a/Instructions/basic/media/dashboard_v1.png b/Instructions/basic/media/dashboard_v1.png new file mode 100644 index 0000000..22c0f12 Binary files /dev/null and b/Instructions/basic/media/dashboard_v1.png differ diff --git a/Instructions/basic/media/days_off_v1.png b/Instructions/basic/media/days_off_v1.png new file mode 100644 index 0000000..bf42e2f Binary files /dev/null and b/Instructions/basic/media/days_off_v1.png differ diff --git a/Instructions/basic/media/dd_v1.png b/Instructions/basic/media/dd_v1.png new file mode 100644 index 0000000..2985989 Binary files /dev/null and b/Instructions/basic/media/dd_v1.png differ diff --git a/Instructions/basic/media/delete-branch.png b/Instructions/basic/media/delete-branch.png new file mode 100644 index 0000000..58831c8 Binary files /dev/null and b/Instructions/basic/media/delete-branch.png differ diff --git a/Instructions/basic/media/demo_signin_v1.png b/Instructions/basic/media/demo_signin_v1.png new file mode 100644 index 0000000..dbfe812 Binary files /dev/null and b/Instructions/basic/media/demo_signin_v1.png differ diff --git a/Instructions/basic/media/doingdone_v1.png b/Instructions/basic/media/doingdone_v1.png new file mode 100644 index 0000000..7548ed9 Binary files /dev/null and b/Instructions/basic/media/doingdone_v1.png differ diff --git a/Instructions/basic/media/epic_details_v1.png b/Instructions/basic/media/epic_details_v1.png new file mode 100644 index 0000000..aeb2e79 Binary files /dev/null and b/Instructions/basic/media/epic_details_v1.png differ diff --git a/Instructions/basic/media/eshopweb-team_v1.png b/Instructions/basic/media/eshopweb-team_v1.png new file mode 100644 index 0000000..66ad896 Binary files /dev/null and b/Instructions/basic/media/eshopweb-team_v1.png differ diff --git a/Instructions/basic/media/feature_v1.png b/Instructions/basic/media/feature_v1.png new file mode 100644 index 0000000..2b1734a Binary files /dev/null and b/Instructions/basic/media/feature_v1.png differ diff --git a/Instructions/basic/media/file-comparison.png b/Instructions/basic/media/file-comparison.png new file mode 100644 index 0000000..0c350f1 Binary files /dev/null and b/Instructions/basic/media/file-comparison.png differ diff --git a/Instructions/basic/media/finished_dashboard_v1.png b/Instructions/basic/media/finished_dashboard_v1.png new file mode 100644 index 0000000..e8bc55a Binary files /dev/null and b/Instructions/basic/media/finished_dashboard_v1.png differ diff --git a/Instructions/basic/media/first-commit.png b/Instructions/basic/media/first-commit.png new file mode 100644 index 0000000..bde5664 Binary files /dev/null and b/Instructions/basic/media/first-commit.png differ diff --git a/Instructions/basic/media/import-repo.png b/Instructions/basic/media/import-repo.png new file mode 100644 index 0000000..6f77107 Binary files /dev/null and b/Instructions/basic/media/import-repo.png differ diff --git a/Instructions/basic/media/inherited_v1.png b/Instructions/basic/media/inherited_v1.png new file mode 100644 index 0000000..0a3e9ba Binary files /dev/null and b/Instructions/basic/media/inherited_v1.png differ diff --git a/Instructions/basic/media/iterationsareas_v1.png b/Instructions/basic/media/iterationsareas_v1.png new file mode 100644 index 0000000..7de42c9 Binary files /dev/null and b/Instructions/basic/media/iterationsareas_v1.png differ diff --git a/Instructions/basic/media/link-wit.png b/Instructions/basic/media/link-wit.png new file mode 100644 index 0000000..199dafe Binary files /dev/null and b/Instructions/basic/media/link-wit.png differ diff --git a/Instructions/basic/media/navigate_project_v1.png b/Instructions/basic/media/navigate_project_v1.png new file mode 100644 index 0000000..012a1a5 Binary files /dev/null and b/Instructions/basic/media/navigate_project_v1.png differ diff --git a/Instructions/basic/media/new-service-connection.png b/Instructions/basic/media/new-service-connection.png new file mode 100644 index 0000000..5d83b0e Binary files /dev/null and b/Instructions/basic/media/new-service-connection.png differ diff --git a/Instructions/basic/media/new2ap_v1.png b/Instructions/basic/media/new2ap_v1.png new file mode 100644 index 0000000..1059b6c Binary files /dev/null and b/Instructions/basic/media/new2ap_v1.png differ diff --git a/Instructions/basic/media/new_dashboard_v1.png b/Instructions/basic/media/new_dashboard_v1.png new file mode 100644 index 0000000..0727ae3 Binary files /dev/null and b/Instructions/basic/media/new_dashboard_v1.png differ diff --git a/Instructions/basic/media/new_task_v1.png b/Instructions/basic/media/new_task_v1.png new file mode 100644 index 0000000..ab56cfe Binary files /dev/null and b/Instructions/basic/media/new_task_v1.png differ diff --git a/Instructions/basic/media/new_team_v1.png b/Instructions/basic/media/new_team_v1.png new file mode 100644 index 0000000..d11378a Binary files /dev/null and b/Instructions/basic/media/new_team_v1.png differ diff --git a/Instructions/basic/media/org_settings_v1.png b/Instructions/basic/media/org_settings_v1.png new file mode 100644 index 0000000..f364b82 Binary files /dev/null and b/Instructions/basic/media/org_settings_v1.png differ diff --git a/Instructions/basic/media/over_capacity_v1.png b/Instructions/basic/media/over_capacity_v1.png new file mode 100644 index 0000000..7d9415a Binary files /dev/null and b/Instructions/basic/media/over_capacity_v1.png differ diff --git a/Instructions/basic/media/pbi_field_layout_v1.png b/Instructions/basic/media/pbi_field_layout_v1.png new file mode 100644 index 0000000..d0f51e9 Binary files /dev/null and b/Instructions/basic/media/pbi_field_layout_v1.png differ diff --git a/Instructions/basic/media/pbi_field_name_v1.png b/Instructions/basic/media/pbi_field_name_v1.png new file mode 100644 index 0000000..42866e6 Binary files /dev/null and b/Instructions/basic/media/pbi_field_name_v1.png differ diff --git a/Instructions/basic/media/pbi_v1.png b/Instructions/basic/media/pbi_v1.png new file mode 100644 index 0000000..fa62bf0 Binary files /dev/null and b/Instructions/basic/media/pbi_v1.png differ diff --git a/Instructions/basic/media/pbis_v1.png b/Instructions/basic/media/pbis_v1.png new file mode 100644 index 0000000..130e007 Binary files /dev/null and b/Instructions/basic/media/pbis_v1.png differ diff --git a/Instructions/basic/media/people_v1.png b/Instructions/basic/media/people_v1.png new file mode 100644 index 0000000..95d7b9f Binary files /dev/null and b/Instructions/basic/media/people_v1.png differ diff --git a/Instructions/basic/media/policy-denied.png b/Instructions/basic/media/policy-denied.png new file mode 100644 index 0000000..63ed306 Binary files /dev/null and b/Instructions/basic/media/policy-denied.png differ diff --git a/Instructions/basic/media/policy-settings.png b/Instructions/basic/media/policy-settings.png new file mode 100644 index 0000000..3ece634 Binary files /dev/null and b/Instructions/basic/media/policy-settings.png differ diff --git a/Instructions/basic/media/project_settings_v1.png b/Instructions/basic/media/project_settings_v1.png new file mode 100644 index 0000000..826dd66 Binary files /dev/null and b/Instructions/basic/media/project_settings_v1.png differ diff --git a/Instructions/basic/media/pu_template_v1.png b/Instructions/basic/media/pu_template_v1.png new file mode 100644 index 0000000..13ae8d5 Binary files /dev/null and b/Instructions/basic/media/pu_template_v1.png differ diff --git a/Instructions/basic/media/pulweb_boards_v1.png b/Instructions/basic/media/pulweb_boards_v1.png new file mode 100644 index 0000000..17ef17e Binary files /dev/null and b/Instructions/basic/media/pulweb_boards_v1.png differ diff --git a/Instructions/basic/media/pulweb_v1.png b/Instructions/basic/media/pulweb_v1.png new file mode 100644 index 0000000..92c144f Binary files /dev/null and b/Instructions/basic/media/pulweb_v1.png differ diff --git a/Instructions/basic/media/qa_column_v1.png b/Instructions/basic/media/qa_column_v1.png new file mode 100644 index 0000000..48a2b70 Binary files /dev/null and b/Instructions/basic/media/qa_column_v1.png differ diff --git a/Instructions/basic/media/query_v1.png b/Instructions/basic/media/query_v1.png new file mode 100644 index 0000000..dd267db Binary files /dev/null and b/Instructions/basic/media/query_v1.png differ diff --git a/Instructions/basic/media/restore-branch.png b/Instructions/basic/media/restore-branch.png new file mode 100644 index 0000000..34ee244 Binary files /dev/null and b/Instructions/basic/media/restore-branch.png differ diff --git a/Instructions/basic/media/save_task_v1.png b/Instructions/basic/media/save_task_v1.png new file mode 100644 index 0000000..9a1408c Binary files /dev/null and b/Instructions/basic/media/save_task_v1.png differ diff --git a/Instructions/basic/media/scrum_v1.png b/Instructions/basic/media/scrum_v1.png new file mode 100644 index 0000000..366b3bf Binary files /dev/null and b/Instructions/basic/media/scrum_v1.png differ diff --git a/Instructions/basic/media/select_iteration_v1.png b/Instructions/basic/media/select_iteration_v1.png new file mode 100644 index 0000000..e8accca Binary files /dev/null and b/Instructions/basic/media/select_iteration_v1.png differ diff --git a/Instructions/basic/media/staged-changes.png b/Instructions/basic/media/staged-changes.png new file mode 100644 index 0000000..5b7cdd5 Binary files /dev/null and b/Instructions/basic/media/staged-changes.png differ diff --git a/Instructions/basic/media/styles_v1.png b/Instructions/basic/media/styles_v1.png new file mode 100644 index 0000000..404fec2 Binary files /dev/null and b/Instructions/basic/media/styles_v1.png differ diff --git a/Instructions/basic/media/sub_areas_v1.png b/Instructions/basic/media/sub_areas_v1.png new file mode 100644 index 0000000..6569464 Binary files /dev/null and b/Instructions/basic/media/sub_areas_v1.png differ diff --git a/Instructions/basic/media/swimlane_v1.png b/Instructions/basic/media/swimlane_v1.png new file mode 100644 index 0000000..d51cb67 Binary files /dev/null and b/Instructions/basic/media/swimlane_v1.png differ diff --git a/Instructions/basic/media/tag_color_v1.png b/Instructions/basic/media/tag_color_v1.png new file mode 100644 index 0000000..6a86cc5 Binary files /dev/null and b/Instructions/basic/media/tag_color_v1.png differ diff --git a/Instructions/basic/media/tags_v1.png b/Instructions/basic/media/tags_v1.png new file mode 100644 index 0000000..93cf01d Binary files /dev/null and b/Instructions/basic/media/tags_v1.png differ diff --git a/Instructions/basic/media/verify_v1.png b/Instructions/basic/media/verify_v1.png new file mode 100644 index 0000000..b1da00c Binary files /dev/null and b/Instructions/basic/media/verify_v1.png differ diff --git a/Instructions/basic/media/view_backlog_v1.png b/Instructions/basic/media/view_backlog_v1.png new file mode 100644 index 0000000..df7463c Binary files /dev/null and b/Instructions/basic/media/view_backlog_v1.png differ diff --git a/Instructions/basic/media/vscode-command.png b/Instructions/basic/media/vscode-command.png new file mode 100644 index 0000000..6bc8c9b Binary files /dev/null and b/Instructions/basic/media/vscode-command.png differ diff --git a/Instructions/basic/media/wip_limit_v1.png b/Instructions/basic/media/wip_limit_v1.png new file mode 100644 index 0000000..1619013 Binary files /dev/null and b/Instructions/basic/media/wip_limit_v1.png differ diff --git a/Instructions/basic/media/work_details_v1.png b/Instructions/basic/media/work_details_v1.png new file mode 100644 index 0000000..96c454c Binary files /dev/null and b/Instructions/basic/media/work_details_v1.png differ diff --git a/Instructions/basic/media/work_details_window_v1.png b/Instructions/basic/media/work_details_window_v1.png new file mode 100644 index 0000000..6cb6b0c Binary files /dev/null and b/Instructions/basic/media/work_details_window_v1.png differ diff --git a/Instructions/intermediate/01-implement-ci-cd-with-github-actions-and-iac-with-bicep.md b/Instructions/intermediate/01-implement-ci-cd-with-github-actions-and-iac-with-bicep.md index ca186c2..8c5a4f8 100644 --- a/Instructions/intermediate/01-implement-ci-cd-with-github-actions-and-iac-with-bicep.md +++ b/Instructions/intermediate/01-implement-ci-cd-with-github-actions-and-iac-with-bicep.md @@ -257,6 +257,8 @@ Now that you finished the exercise, you should delete the cloud resources you cr 1. On the toolbar, select **Delete resource group**. 1. Enter the resource group name and confirm that you want to delete it. +> **CAUTION:** Deleting a resource group deletes all resources contained within it. If you chose an existing resource group for this exercise, any existing resources outside the scope of this exercise will also be deleted. + You don't need to clean up your GitHub repo or project, as they will remain available for you to use as a reference and portfolio item. If you want to delete the repo, you can do so by following this documentation: [Deleting a repository](https://docs.github.com/repositories/creating-and-managing-repositories/deleting-a-repository). diff --git a/Instructions/intermediate/02-implement-microsoft-dev-box.md b/Instructions/intermediate/02-implement-microsoft-dev-box.md new file mode 100644 index 0000000..ccebca5 --- /dev/null +++ b/Instructions/intermediate/02-implement-microsoft-dev-box.md @@ -0,0 +1,324 @@ +--- +lab: + topic: Intermediate + title: "Implement Microsoft Dev Box for Developer Self-Service" + description: "Learn how to implement basic Microsoft Dev Box environments for developer self-service scenarios." +--- + +# Implement Microsoft Dev Box for Developer Self-Service + +In this lab, you will learn how to implement Microsoft Dev Box environments to provide developer self-service capabilities. You will create a dev center, configure dev box definitions, set up projects and pools, and evaluate the developer experience using Microsoft-hosted networking. + +You will learn how to: + +- Create and configure a Microsoft Dev Box environment +- Set up dev box definitions and projects +- Configure dev box pools with Microsoft-hosted networking +- Manage permissions for different roles +- Evaluate the developer experience + +This lab takes approximately **30** minutes to complete. + +## Before you start + +To complete the lab, you need: + +- An Azure subscription to which you have at least the Contributor-level access. If you don't already have one, you can [sign up for one](https://azure.microsoft.com/). +- A Microsoft Entra tenant with appropriate permissions to create users and groups +- A Microsoft Intune subscription associated with the same Microsoft Entra tenant as the Azure subscription +- A GitHub user account. If you don't have one, you can [create a new account](https://github.com/join). If you need instructions on how to create a GitHub account, refer to the article [Creating an account on GitHub](https://docs.github.com/get-started/quickstart/creating-an-account-on-github). + +## Prepare the lab environment + +> **Note:** In this lab, you will create Microsoft Entra users and groups to simulate different roles in a Microsoft Dev Box deployment. If you already have suitable test users and groups in your tenant, you can skip the user creation steps and adapt the instructions accordingly. + +### Create Microsoft Entra users and groups + +1. Start a web browser and navigate to the Microsoft Entra admin center at `https://entra.microsoft.com`. +1. If prompted, sign in by using a Microsoft Entra account with Global Administrator permissions in your tenant. +1. In the Microsoft Entra admin center, in the left navigation pane, expand **Identity** and select **Groups**. +1. On the **Groups | All groups** page, select **+ New group**. +1. On the **New Group** page, specify the following settings and select **Create**: + + | Setting | Value | + | ----------- | ------------------------------------- | + | Group type | **Security** | + | Group name | **DevCenter_Platform_Engineers** | + | Description | **Platform Engineers for Dev Center** | + +1. Repeat the previous steps to create two additional groups: + + - **DevCenter_Dev_Leads** (Development team leads) + - **DevCenter_Dev_Users** (Developers) + +1. In the Microsoft Entra admin center, in the left navigation pane, expand **Identity** and select **Users**. +1. On the **Users | All users** page, select **+ New user** and then **Create new user**. +1. On the **New user** page, specify the following settings and select **Create**: + + | Setting | Value | + | ---------------------- | ------------------------------------ | + | User principal name | **platformegineer01@yourdomain.com** | + | Display name | **Platform Engineer 01** | + | Auto-generate password | **Enabled** | + | Account enabled | **Enabled** | + + > **Note:** Replace `yourdomain.com` with your actual domain name. Record the auto-generated password for later use. + +1. Repeat the previous steps to create two additional users: + + - **devlead01@yourdomain.com** (Development Lead 01) + - **devuser01@yourdomain.com** (Developer User 01) + +1. Add the users to their respective groups: + - Add **platformegineer01** to **DevCenter_Platform_Engineers** + - Add **devlead01** to **DevCenter_Dev_Leads** + - Add **devuser01** to **DevCenter_Dev_Users** + +### Fork required GitHub repository + +1. Open a new browser tab and navigate to [https://github.com/microsoft/devcenter-catalog](https://github.com/microsoft/devcenter-catalog). +1. Sign in to GitHub with your account if prompted. +1. Select **Fork** to create a fork of the repository in your GitHub account. + +## Implement a Microsoft Dev Box environment + +In this exercise, you will leverage a set of features provided by Microsoft to implement a Microsoft Dev Box environment. This approach focuses on minimizing the effort involved in building a functional developer self-service solution using Microsoft-hosted networking. + +The exercise consists of the following tasks: + +- Create a dev center +- Review the dev center settings +- Create a dev box definition +- Create a project +- Create a dev box pool +- Configure permissions +- Evaluate a dev box + +### Create a dev center + +In this task, you will create an Azure dev center that will be used throughout this lab. A dev center is a platform engineering service that centralizes the creation and management of scalable, pre-configured development and deployment environments, optimizing collaboration and resource utilization for software development teams. + +1. Start a web browser and navigate to the Azure portal at `https://portal.azure.com`. +1. When prompted to authenticate, sign in by using your Microsoft Entra user account. +1. In the Azure portal, in the **Search** text box, search for and select **`Dev centers`**. +1. On the **Dev centers** page, select **+ Create**. +1. On the **Basics** tab of the **Create a dev center** page, specify the following settings and then select **Next: Settings**: + + | Setting | Value | + | ----------------------------------------------------------------------- | ------------------------------------------------------------ | + | Subscription | The name of the Azure subscription you are using in this lab | + | Resource group | The name of a **new** resource group **rg-devcenter-basic** | + | Name | **devcenter-basic** | + | Location | **(US) East US** | + | Attach a quick start catalog - Azure deployment environment definitions | Enabled | + | Attach a quick start catalog - Dev box customization tasks | Disabled | + +1. On the **Settings** tab of the **Create a dev center** page, specify the following settings and then select **Review + Create**: + + | Setting | Value | + | ------------------------------------------ | ------- | + | Enable catalogs per project | Enabled | + | Allow Microsoft hosted network in projects | Enabled | + | Enable Azure Monitor agent installation | Enabled | + + > **Note:** By design, resources from catalogs attached to the dev center are available to all projects within it. The setting **Enable catalogs per project** makes it possible to attach additional catalogs to arbitrarily selected projects as well. + + > **Note:** Dev boxes can be connected either to a virtual network in your own Azure subscription or a Microsoft hosted one, depending on whether they need to communicate with resources in your environment. If there is no such need, by enabling the setting **Allow Microsoft hosted network in projects**, you introduce the option to connect dev boxes to a Microsoft hosted network, effectively minimizing the management and configuration overhead. + + > **Note:** The setting **Enable Azure Monitor agent installation** automatically triggers installation of the Azure Monitor agent on all dev boxes in the dev center. + +1. On the **Review + Create** tab, wait for the validation to complete and then select **Create**. + + > **Note:** Wait for the project to be provisioned. The project creation might take about 1 minute. + +1. On the **Deployment is completed** page, select **Go to resource**. + +### Review the dev center settings + +In this task, you will review the basic configuration settings of the dev center you created in the previous task. + +1. In the web browser displaying the Azure portal, on the **devcenter-basic** page, in the vertical navigation menu on the left side, expand the **Environment configuration** section and select **Catalogs**. +1. On the **devcenter-basic | Catalogs** page, notice that the dev center is configured with the **quickstart-environment-definitions** catalog, which points to the GitHub repository `https://github.com/microsoft/devcenter-catalog.git`. +1. Verify that the **Status** column contains the **Sync successful** entry. If that is not the case, use the following sequence of steps to re-create the catalog: + + 1. Select the checkbox next to the autogenerated catalog entry **quickstart-environment-definitions** and then, in the toolbar, select **Delete**. + 1. On the **devcenter-basic | Catalogs** page, select **+ Add**. + 1. In the **Add catalog** pane, in the **Name** text box, enter **`quickstart-environment-definitions-fix`**, in the **Catalog location** section, select **GitHub**, in the **Authentication type**, select **GitHub app**, leave the checkbox **Automatically sync this catalog** checkbox enabled, and then select **Sign in with GitHub**. + 1. In the **Sign in with GitHub** window, enter the GitHub credentials and select **Sign in**. + + > **Note:** These GitHub credentials provide you with access to a GitHub repo created as a fork of + + 1. When prompted, in the **Authorize Microsoft DevCenter** window, select **Authorize Microsoft DevCenter**. + 1. Back in the **Add catalog** pane, in the **Repo** drop-down list, select **devcenter-catalog**, in the **Branch** drop-down list, accept the **Default branch** entry, in the **Folder path**, enter **`Environment-Definitions`** and then select **Add**. + 1. Back on the **devcenter-basic | Catalogs** page, verify that the sync completes successfully by monitoring the entry in the **Status** column. + +1. On the **devcenter-basic | Catalogs** page, select the **quickstart-environment-definitions-fix** entry. +1. On the **quickstart-environment-definitions-fix** page, review the list of predefined environment definitions. + + > **Note:** Each entry represents a definition of an Azure deployment environment defined in a respective subfolder of the **Environment-Definitions** folder of the GitHub repository `https://github.com/microsoft/devcenter-catalog.git`. + + > **Note:** A deployment environment is a collection of Azure resources defined in a template referred to as an environment definition. Developers can use these definitions to deploy infrastructure that will serve to host their solutions. For more information regarding Azure deployment environments, refer to the Microsoft Learn article [What is Azure Deployment Environments?](https://learn.microsoft.com/azure/deployment-environments/overview-what-is-azure-deployment-environments) + +### Create a dev box definition + +In this task, you will create a dev box definition. Its purpose is to define the operating system, tools, settings, and resources that serve as a blueprint for creating consistent and tailored development environments (referred to as dev boxes). + +1. In the web browser displaying the Azure portal, on the **devcenter-basic** page, in the vertical navigation menu on the left side, expand the **Dev box configuration** section and select **Dev box definitions**. +1. On the **devcenter-basic | Dev box definitions** page, select **+ Create**. +1. On the **Create dev box definition** page, specify the following settings and then select **Create**: + + | Setting | Value | + | ------------------ | ----------------------------------------------------------------------------------------------------------- | + | Name | **devbox-definition-basic** | + | Image | **Visual Studio 2022 Enterprise on Windows 11 Enterprise + Microsoft 365 Apps 24H2 \| Hibernate supported** | + | Image version | **Latest** | + | Compute | **8 vCPU, 32 GB RAM** | + | Storage | **256 GB SSD** | + | Enable hibernation | Enabled | + + > **Note:** Wait for the dev box definition to be created. This should take less than 1 minute. + +### Create a dev center project + +In this task, you will create a dev center project. A dev center project typically corresponds with a development project within your organization. For example, you might create a project for the development of a line of business application, and another project for the development of the company website. All projects in a dev center share the same dev box definitions, network connection, catalogs, and compute galleries. You might consider creating multiple dev center projects if you have multiple development projects that have separate project administrators and access permissions requirements. + +1. In the web browser displaying the Azure portal, on the **devcenter-basic** page, in the vertical navigation menu on the left side, expand the **Manage** section and select **Projects**. +1. On the **devcenter-basic | Projects** page, select **+ Create**. +1. On the **Basics** tab of the **Create a project** page, specify the following settings and then select **Next: Dev box management**: + + | Setting | Value | + | -------------- | ------------------------------------------------------------ | + | Subscription | The name of the Azure subscription you are using in this lab | + | Resource group | **rg-devcenter-basic** | + | Dev center | **devcenter-basic** | + | Name | **devcenter-project-basic** | + | Description | **Basic Dev Center Project** | + +1. On the **Dev box management** tab of the **Create a project** page, specify the following settings and then select **Next: Catalogs**: + + | Setting | Value | + | ----------------------- | ----- | + | Enable dev box limits | Yes | + | Dev boxes per developer | **2** | + +1. On the **Catalogs** tab of the **Create a project** page, specify the following settings and then select **Review + Create**: + + | Setting | Value | + | ---------------------------------- | ------- | + | Deployment environment definitions | Enabled | + | Image definitions | Enabled | + +1. On the **Review + Create** tab of the **Create a project** page, select **Create**: + + > **Note:** Wait for the project to be created. This should take less than 1 minute. + +1. On the **Deployment is completed** page, select **Go to resource**. + +### Create a dev box pool + +In this task, you will create a dev box pool in the dev center project you created in the previous task. Dev box pools are used by dev box users to create dev boxes. A dev box pool links a dev box definition with a network connection. In this lab, you will use Microsoft-hosted connections, which minimize the management and configuration overhead. The network connection determines where a dev box is hosted and its access to other cloud and on-premises resources. In addition, to reduce the cost of running dev boxes, you can configure a dev box pool to shut them down daily at a predefined time. + +1. In the web browser displaying the Azure portal, on the **devcenter-project-basic** page, in the vertical navigation menu on the left side, expand the **Manage** section and select **Dev box pools**. +1. On the **devcenter-project-basic | Dev box pools** page, select **+ Create**. +1. On the **Basics** tab of the **Create a dev box pool** page, specify the following settings and then select **Create**: + + | Setting | Value | + | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | + | Name | **devbox-pool-basic** | + | Definition | **devbox-definition-basic** | + | Network connection | **Deploy to a Microsoft hosted network** | + | Region | **(US) East US** | + | Enable single sign-on | Enabled | + | Dev box Creator Privileges | **Local Administrator** | + | Enable auto-stop on schedule | Enabled | + | Stop time | **07:00 PM** | + | Time zone | Your current time zone | + | Enable hibernate on disconnect | Enabled | + | Grace period in minutes | **60** | + | I confirm that my organization has Azure Hybrid Benefits licenses, which will apply to all dev boxes in this pool | Enabled | + + > **Note:** Wait for the dev box pool to be created. This might take about 2 minutes. + +### Configure permissions + +In this task, you will assign suitable Microsoft dev box-related permissions to the three Microsoft Entra security principals which have been provisioned in your lab environment. These security principals correspond to typical roles in platform engineering scenarios: + +| User | Group | Role | +| ----------------- | ---------------------------- | --------------------- | +| platformegineer01 | DevCenter_Platform_Engineers | Platform engineer | +| devlead01 | DevCenter_Dev_Leads | Development team lead | +| devuser01 | DevCenter_Dev_Users | Developer | + +Microsoft dev box relies on Azure role-based access control (Azure RBAC) to control access to project-level functionality. Platform engineers should have full control to create and manage dev centers, their catalogs, and projects. This effectively requires the owner or contributor role, depending on whether they also need the ability to delegate permissions to others. Development team leads should be assigned the dev center Project Admin role, which grants the ability to perform administrative tasks on Microsoft Dev Box projects. Dev box users need the ability to create and manage their own dev boxes, which are associated with the Dev Box User role. + +> **Note:** You will start by assigning permissions to the Microsoft Entra group intended to contain platform engineer user accounts. + +1. In the web browser displaying the Azure portal, navigate to the **devcenter-basic** page and, in the vertical navigation menu on the left side, select **Access control (IAM)**. +1. On the **devcenter-basic | Access control (IAM)** page, select **+ Add** and, in the drop-down list, select **Add role assignment**. +1. On the **Role** tab of the **Add role assignment** page, select the **Privileged administrator role** tab, in the list of roles, select **Owner** and finally select **Next**. +1. On the **Members** tab of the **Add role assignment** page, ensure that the **User, group, or service principal** option is selected and click **+ Select members**. +1. In the **Select members** pane, search for and select **`DevCenter_Platform_Engineers`** and then click **Select**. +1. Back on the **Members** tab of the **Add role assignment** page, select **Next**. +1. On the **Conditions** tab of the **Add role assignment** page, in the **What user can do** section, select the option **Allow user to assign all roles (highly privileged)** and then select **Next**. +1. On the **Review + assign** tab of the **Add role assignment** page, select **Review + assign**. + + > **Note:** Next you will assign permissions to the Microsoft Entra group intended to contain development team lead user accounts. + +1. Back on the **devcenter-basic | Access control (IAM)** page, in the vertical navigation menu on the left side, expand the **Manage** section, select **Projects**, and, in the list of projects, select **devcenter-project-basic**. +1. On the **devcenter-project-basic** page, in the vertical navigation menu on the left side, select **Access control (IAM)**. +1. On the **devcenter-project-basic | Access control (IAM)** page, select **+ Add** and, in the drop-down list, select **Add role assignment**. +1. On the **Role** tab of the **Add role assignment** page, ensure that the **Job function roles** tab is selected, in the list of roles, select **DevCenter Project Admin** and select **Next**. +1. On the **Members** tab of the **Add role assignment** page, ensure that the **User, group, or service principal** option is selected and click **+ Select members**. +1. In the **Select members** pane, search for and select **`DevCenter_Dev_Leads`** and then click **Select**. +1. Back on the **Members** tab of the **Add role assignment** page, select **Next**. +1. On the **Review + assign** tab of the **Add role assignment** page, select **Review + assign**. + + > **Note:** Finally, you will assign permissions to the Microsoft Entra group intended to contain developer user accounts. + +1. Back on the **devcenter-project-basic | Access control (IAM)** page, select **+ Add** and, in the drop-down list, select **Add role assignment**. +1. On the **Role** tab of the **Add role assignment** page, ensure that the **Job function roles** tab is selected, in the list of roles, select **DevCenter Dev Box Users** and select **Next**. +1. On the **Members** tab of the **Add role assignment** page, ensure that the **User, group, or service principal** option is selected and click **+ Select members**. +1. In the **Select members** pane, search for and select **`DevCenter_Dev_Users`** and then click **Select**. +1. Back on the **Members** tab of the **Add role assignment** page, select **Next**. +1. On the **Review + assign** tab of the **Add role assignment** page, select **Review + assign**. + +### Evaluate a dev box + +In this task, you will evaluate a dev box functionality by using a Microsoft Entra developer user account. + +1. Start a web browser incognito/in-private and navigate to the Microsoft Dev Box developer portal at `https://aka.ms/devbox-portal`. +1. When prompted to sign in, provide the credentials of the **devuser01** user account. +1. On the **Welcome, devuser01** page of the Microsoft Dev Box developer portal, select **+ New dev box**. +1. In the **Add a dev box** pane, in the **Name** text box, enter **`devuser01box01`** +1. Review other information presented in the **Add a dev box** pane, including the project name, dev box pool specifications, hibernation support status, and the scheduled shutdown timing. In addition, note the option to apply customizations and the notification that dev box creation might take up to 65 minutes. + + > **Note:** Dev box names must be unique within a project. + +1. In the **Add a dev box** pane, select **Create**. + + > **Note:** Do not wait for the dev box to be created. You can continue with the cleanup section while the dev box is being provisioned in the background. + +1. Once the dev box is fully provisioned and running, connect to it by selecting the option to **Connect via app**. + + > **Note:** Connectivity to a dev box can be established by using a Remote Desktop Windows app, a Remote Desktop client (mstsc.exe), or directly within a web browser window. + +1. In the pop-up window titled **This site is trying to open Microsoft Remote Connection Center**, select **Open**. This will automatically initiate a Remote Desktop session to the dev box. +1. When prompted for credentials, authenticate by providing the user name and the password of the **devuser01** account. +1. Within the Remote Desktop session to the dev box, verify that its configuration includes an installation of Visual Studio 2022 and Microsoft 365 apps. + + > **Note:** You can shut down the dev box directly from the Microsoft Dev Box developer portal as a dev user by first selecting the ellipsis symbol in the **Your dev box** interface and then selecting **Shut down** from the cascading menu. Alternatively, as a platform engineer or development team leads, you can control dev box lifecycle from the **Dev box pools** section of the corresponding dev center project. + +## Clean up resources + +Now that you finished the exercise, you should delete the cloud resources you created to avoid unnecessary resource usage. + +1. In your browser navigate to the Azure portal [https://portal.azure.com](https://portal.azure.com); signing in with your Azure credentials if prompted. +1. Navigate to the resource group you created and view the contents of the resources used in this exercise. +1. On the toolbar, select **Delete resource group**. +1. Enter the resource group name and confirm that you want to delete it. + +> **CAUTION:** Deleting a resource group deletes all resources contained within it. If you chose an existing resource group for this exercise, any existing resources outside the scope of this exercise will also be deleted. + +You don't need to clean up your GitHub repo or project, as they will remain available for you to use as a reference and portfolio item. + +If you want to delete the repo, you can do so by following this documentation: [Deleting a repository](https://docs.github.com/repositories/creating-and-managing-repositories/deleting-a-repository). diff --git a/Instructions/intermediate/03-customize-microsoft-dev-box.md b/Instructions/intermediate/03-customize-microsoft-dev-box.md new file mode 100644 index 0000000..0242d7e --- /dev/null +++ b/Instructions/intermediate/03-customize-microsoft-dev-box.md @@ -0,0 +1,548 @@ +--- +lab: + topic: Intermediate + title: "Customize Microsoft Dev Box with Custom Images and Networking" + description: "Learn how to customize Microsoft Dev Box environments with custom images, private networking, and advanced configurations." +--- + +# Customize Microsoft Dev Box with Custom Images and Networking + +In this lab, you will learn how to customize Microsoft Dev Box environments using custom images built with Azure Image Builder, private networking configurations, and advanced customization features. You will create Azure compute galleries, build custom images, configure virtual network connections, and implement image definitions for team-specific development environments. + +You will learn how to: + +- Create and configure Azure compute galleries +- Build custom dev box images using Azure Image Builder +- Configure private networking for dev boxes +- Implement image definitions and customization catalogs +- Create customized dev box pools with private networking + +This lab takes approximately **30** minutes to complete. + +## Before you start + +To complete the lab, you need: + +- An Azure subscription to which you have at least the Contributor-level access. If you don't already have one, you can [sign up for one](https://azure.microsoft.com/). +- An Microsoft Dev Box environment set up in your Azure subscription. If you haven't set it up yet, refer to the lab [Implement Microsoft Dev Box for Developer Self-Service](../intermediate/02-implement-microsoft-dev-box.md) or follow the instructions [Configure Microsoft Dev Box using the Get Started template](https://learn.microsoft.com/azure/dev-box/quickstart-get-started-template). +- A Microsoft Entra tenant with 3 pre-created user accounts (and, optionally 3 pre-created Microsoft Entra groups) representing 3 different roles involved in Microsoft Dev Box deployments. For the sake of clarity, the user and group names in the lab instructions will be matching the information in the following table: + + | User | Group | Role | + | ----------------- | ---------------------------- | --------------------- | + | platformegineer01 | DevCenter_Platform_Engineers | Platform engineer | + | devlead01 | DevCenter_Dev_Leads | Development team lead | + | devuser01 | DevCenter_Dev_Users | Developer | + +- A Microsoft Entra tenant with appropriate permissions to create users and groups +- A Microsoft Intune subscription associated with the same Microsoft Entra tenant as the Azure subscription +- A GitHub user account. If you don't have one, you can [create a new account](https://github.com/join). If you need instructions on how to create a GitHub account, refer to the article [Creating an account on GitHub](https://docs.github.com/get-started/quickstart/creating-an-account-on-github). + +### Fork required GitHub repositories + +1. Open a new browser tab and navigate to [https://github.com/MicrosoftLearning/contoso-co-eShop](https://github.com/MicrosoftLearning/contoso-co-eShop). +1. Sign in to GitHub with your account if prompted. +1. Select **Fork** to create a fork of the repository in your GitHub account. + +> **Note:** This repository contains image definition files that will be used for customization. + +## Customize a Microsoft Dev Box environment + +In this exercise, you will customize the functionality of the Microsoft Dev Box environment. This approach focuses on the extent of changes you can apply when implementing a custom developer self-service solution using Azure compute galleries, custom images, and private networking. + +The exercise consists of the following tasks: + +- Create an Azure compute gallery and attach it to the dev center +- Configure authentication and authorization for Azure Image Builder +- Create a custom image by using Azure Image Builder +- Create an Azure dev center network connection +- Adding image definitions to an Azure dev center project +- Create a customized dev box pool +- Evaluate a customized dev box + +### Create an Azure compute gallery and attach it to the dev center + +In this task, you will create an Azure compute gallery and attach it to a dev center. A gallery is a repository residing in an Azure subscription, which helps you build structure and organization around custom images. After you attach a compute gallery to a dev center and populate it with images, you will be able to create dev box definitions based on images stored in the compute gallery. + +1. Start a web browser and navigate to the Azure portal at `https://portal.azure.com`. +1. When prompted to authenticate, sign in by using your Microsoft account. +1. In the Azure portal, in the **Search** text box, search for and select **`Azure compute galleries`**. +1. On the **Azure compute galleries** page, select **+ Create**. +1. On the **Basics** tab of the **Create Azure compute gallery** page, specify the following settings and then select **Next: Sharing method**: + + | Setting | Value | + | -------------- | ------------------------------------------------------------ | + | Subscription | The name of the Azure subscription you are using in this lab | + | Resource group | The name of a **new** resource group **rg-devcenter-custom** | + | Name | **compute_gallery_custom** | + | Region | **(US) East US** | + +1. On the **Sharing method** tab of the **Create Azure compute gallery** page, ensure that the **Role based access control (RBAC)** option is selected and then select **Review + Create**: +1. On the **Review + Create** tab, wait for the validation to complete and then select **Create**. + + > **Note:** Wait for the project to be provisioned. The Azure compute gallery creation should take less than 1 minute. + +1. In the Azure portal, search for and select **`Dev centers`**. +1. On the **Dev centers** page, select **+ Create**. +1. On the **Basics** tab of the **Create a dev center** page, specify the following settings and then select **Next: Settings**: + + | Setting | Value | + | ----------------------------------------------------------------------- | ------------------------------------------------------------ | + | Subscription | The name of the Azure subscription you are using in this lab | + | Resource group | **rg-devcenter-custom** | + | Name | **devcenter-custom** | + | Location | **(US) East US** | + | Attach a quick start catalog - Azure deployment environment definitions | Enabled | + | Attach a quick start catalog - Dev box customization tasks | Enabled | + +1. On the **Settings** tab of the **Create a dev center** page, specify the following settings and then select **Review + Create**: + + | Setting | Value | + | ------------------------------------------ | ------- | + | Enable catalogs per project | Enabled | + | Allow Microsoft hosted network in projects | Enabled | + | Enable Azure Monitor agent installation | Enabled | + +1. On the **Review + Create** tab, wait for the validation to complete and then select **Create**. + + > **Note:** Wait for the dev center to be provisioned. This might take about 1 minute. + +1. On the **Deployment is completed** page, select **Go to resource**. +1. On the **devcenter-custom** page, in the vertical navigation menu on the left side, expand the **Dev box configuration** section and select **Azure compute galleries**. +1. On the **devcenter-custom | Azure compute galleries** page, select **+ Add**. +1. In the **Add Azure compute gallery** pane, in the **Gallery** drop-down list, select **compute_gallery_custom** and then select **Add**. + + > **Note:** If you receive an error message: "_This dev center does not have a system assigned or user assigned identity. Galleries cannot be added until an identity has been assigned._" you will need to assign a system assigned identity to the dev center. + > To do so, in the Azure portal, on the **devcenter-custom** page, in the vertical navigation menu on the left side, select **Identity** under Settings, in the **System assigned** tab, set the **Status** switch to **On**, and then select **Save**. + +### Configure authentication and authorization for Azure Image Builder + +In this task, you will create a user-assigned managed identity that will be used by Azure Image Builder to add images to the Azure compute gallery you created in the previous task. You will also configure the required permissions by creating a custom role based access control (RBAC) role and assigning it to the managed identity. This will allow you to use Azure Image Builder in the next task to build a custom image. + +1. In the Azure portal, select the **Cloud Shell** toolbar icon to open the Cloud Shell pane and, if needed, select **Switch to PowerShell** to start a PowerShell session and, in the **Switch to PowerShell in Cloud Shell** dialog box, select **Confirm**. + + > **Note:** If this is the first time you are opening Cloud Shell, in the **Welcome to Azure Cloud Shell** dialog box, select **PowerShell**, in the **Getting Started** pane, select the option **No Storage Account required** and, in the **Subscription** drop-down list, select the name of the Azure subscription you are using in this lab. + +1. In the PowerShell session of the Cloud Shell pane, run the following commands to ensure that all required resource providers are registered: + + ```powershell + Register-AzResourceProvider -ProviderNamespace Microsoft.VirtualMachineImages + Register-AzResourceProvider -ProviderNamespace Microsoft.Storage + Register-AzResourceProvider -ProviderNamespace Microsoft.Compute + Register-AzResourceProvider -ProviderNamespace Microsoft.KeyVault + Register-AzResourceProvider -ProviderNamespace Microsoft.Network + ``` + +1. Run the following command to install the required PowerShell modules (when prompted, type **A** and press the **Enter** key): + + ```powershell + 'Az.ImageBuilder', 'Az.ManagedServiceIdentity' | ForEach-Object {Install-Module -Name $_ -AllowPrerelease} + ``` + +1. Run the following commands to set up variables that will be referenced throughout the image build process: + + ```powershell + $currentAzContext = Get-AzContext + # the target Azure subscription ID + $subscriptionID=$currentAzContext.Subscription.Id + # the target Azure resource group name + $imageResourceGroup='rg-devcenter-custom' + # the target Azure region + $location='eastus' + # the reference name assigned to the image created by using the Azure Image Builder service + $runOutputName="aibWinImgCustom" + # image template name + $imageTemplateName="templateWinVSCodeCustom" + # the Azure compute gallery name + $computeGallery = 'compute_gallery_custom' + ``` + +1. Run the following commands to create a user-assigned managed identity (VM Image Builder uses the user identity you provide to store images in the target Azure Compute Gallery): + + ```powershell + # Install the Azure PowerShell module to support AzUserAssignedIdentity + Install-Module -Name Az.ManagedServiceIdentity + # Generate a pseudo-random integer to be used for resource names + $timeInt=$(get-date -UFormat "%s") + + # Create an identity + $identityName='identityAIBCustom' + $timeInt + New-AzUserAssignedIdentity -ResourceGroupName $imageResourceGroup -Name $identityName -Location $location + $identityNameResourceId=$(Get-AzUserAssignedIdentity -ResourceGroupName $imageResourceGroup -Name $identityName).Id + $identityNamePrincipalId=$(Get-AzUserAssignedIdentity -ResourceGroupName $imageResourceGroup -Name $identityName).PrincipalId + ``` + +1. Run the following commands to grant the newly created user-assigned managed identity the permissions required to store images in the **rg-devcenter-custom** resource group: + + ```powershell + # Set variables + $imageRoleDefName = 'Custom Azure Image Builder Image Def Custom' + $timeInt + $aibRoleImageCreationUrl = 'https://raw.githubusercontent.com/azure/azvmimagebuilder/master/solutions/12_Creating_AIB_Security_Roles/aibRoleImageCreation.json' + $aibRoleImageCreationPath = 'aibRoleImageCreation.json' + + # Customize the role definition file + Invoke-WebRequest -Uri $aibRoleImageCreationUrl -OutFile $aibRoleImageCreationPath -UseBasicParsing + ((Get-Content -path $aibRoleImageCreationPath -Raw) -Replace '', $subscriptionID) | Set-Content -Path $aibRoleImageCreationPath + ((Get-Content -path $aibRoleImageCreationPath -Raw) -Replace '', $imageResourceGroup) | Set-Content -Path $aibRoleImageCreationPath + ((Get-Content -path $aibRoleImageCreationPath -Raw) -Replace 'Azure Image Builder Service Image Creation Role', $imageRoleDefName) | Set-Content -Path $aibRoleImageCreationPath + + # Create a role definition + New-AzRoleDefinition -InputFile ./aibRoleImageCreation.json + + # Assign the role to the VM Image Builder user-assigned managed identity within the scope of the **rg-devcenter-custom** resource group + New-AzRoleAssignment -ObjectId $identityNamePrincipalId -RoleDefinitionName $imageRoleDefName -Scope "/subscriptions/$subscriptionID/resourceGroups/$imageResourceGroup" + ``` + +### Create a custom image by using Azure Image Builder + +In this task, you will use Azure Image Builder to create a custom image based on an existing Azure Resource Manager (ARM) template that defines a Windows 11 Enterprise image with automatically installed Chocolatey and Visual Studio Code. Azure VM Image Builder considerably simplifies the process of defining and provisioning VM images. It relies on an image configuration that you specify to configure an automated imaging pipeline. Subsequently, developers will be able to use such images to provision their dev boxes. + +1. In the PowerShell session of the Cloud Shell pane, run the following commands to create an image definition to be added to the Azure compute gallery you created in the first task of this exercise: + + ```powershell + # ensure that the image definition security type property is set to 'TrustedLaunch' + $securityType = @{Name='SecurityType';Value='TrustedLaunch'} + $features = @($securityType) + # Image definition name + $imageDefName = 'imageDefDevBoxVSCodeCustom' + + # Create the image definition + New-AzGalleryImageDefinition -GalleryName $computeGallery -ResourceGroupName $imageResourceGroup -Location $location -Name $imageDefName -OsState generalized -OsType Windows -Publisher 'Contoso' -Offer 'vscodedevbox' -Sku '1-0-0' -Feature $features -HyperVGeneration 'V2' + ``` + + > **Note:** A dev box image must satisfy a number of requirements including the use of Generation 2, Hyper-V v2, and Windows 10 or 11 Enterprise version 20H2 or later. For their full list, refer to the Microsoft Learn article [Configure Azure Compute Gallery for Microsoft Dev Box](https://learn.microsoft.com/azure/dev-box/how-to-configure-azure-compute-gallery). + +1. Run the following commands to create an empty file named template.json that will contain an ARM template defining a Windows 11 Enterprise image with automatically installed Chocolatey and Visual Studio Code: + + ```powershell + Set-Location -Path ~ + $templateFile = 'template.json' + Set-Content -Path $templateFile -Value '' + ``` + +1. In the PowerShell session of Cloud Shell, use the nano text editor to add the following content to the newly created file: + + > **Note:** To open the nano text editor, run the command `nano ./template.json`. To save changes and exit the nano text editor, press **Ctrl+X**, then **Y**, and finally **Enter**. + + ```json + { + "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "imageTemplateName": { + "type": "string" + }, + "api-version": { + "type": "string" + }, + "svclocation": { + "type": "string" + } + }, + "variables": {}, + "resources": [ + { + "name": "[parameters('imageTemplateName')]", + "type": "Microsoft.VirtualMachineImages/imageTemplates", + "apiVersion": "[parameters('api-version')]", + "location": "[parameters('svclocation')]", + "dependsOn": [], + "tags": { + "imagebuilderTemplate": "win11multi", + "userIdentity": "enabled" + }, + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "": {} + } + }, + "properties": { + "buildTimeoutInMinutes": 100, + "vmProfile": { + "vmSize": "Standard_D2s_v3", + "osDiskSizeGB": 127 + }, + "source": { + "type": "PlatformImage", + "publisher": "MicrosoftWindowsDesktop", + "offer": "Windows-11", + "sku": "win11-21h2-ent", + "version": "latest" + }, + "customize": [ + { + "type": "PowerShell", + "name": "CreateBuildPath", + "inline": [ + "mkdir c:\\buildArtifacts", + "echo Azure-Image-Builder-Was-Here > c:\\buildArtifacts\\azureImageBuilder.txt" + ] + }, + { + "type": "PowerShell", + "name": "InstallChocolatey", + "inline": [ + "Set-ExecutionPolicy Bypass -Scope Process -Force", + "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072", + "iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))" + ] + }, + { + "type": "PowerShell", + "name": "InstallVSCode", + "inline": ["choco install vscode -y"] + } + ], + "distribute": [ + { + "type": "SharedImage", + "galleryImageId": "/subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images/", + "runOutputName": "", + "artifactTags": { + "source": "azureVmImageBuilder", + "baseosimg": "windows11" + }, + "replicationRegions": ["", ""] + } + ] + } + } + ] + } + ``` + +1. Run the following commands to replace placeholders in the template.json with the values specific to your Azure environment: + + ```powershell + $replRegion2 = 'eastus2' + $templateFilePath = '.\template.json' + (Get-Content -Path $templateFilePath -Raw ) -Replace '', $subscriptionID | Set-Content -Path $templateFilePath + (Get-Content -Path $templateFilePath -Raw ) -Replace '', $imageResourceGroup | Set-Content -Path $templateFilePath + (Get-Content -Path $templateFilePath -Raw ) -Replace '', $runOutputName | Set-Content -Path $templateFilePath + (Get-Content -Path $templateFilePath -Raw ) -Replace '', $imageDefName | Set-Content -Path $templateFilePath + (Get-Content -Path $templateFilePath -Raw ) -Replace '', $computeGallery | Set-Content -Path $templateFilePath + (Get-Content -Path $templateFilePath -Raw ) -Replace '', $location | Set-Content -Path $templateFilePath + (Get-Content -Path $templateFilePath -Raw ) -Replace '', $replRegion2 | Set-Content -Path $templateFilePath + ((Get-Content -Path $templateFilePath -Raw) -Replace '', $identityNameResourceId) | Set-Content -Path $templateFilePath + ``` + +1. Run the following command to submit the template to the Azure Image Builder service (the service processes the submitted template by downloading any dependent artifacts, such as scripts, and storing them in a staging resource group, which name includes the **IT\_** prefix, for building a custom virtual machine image): + + ```powershell + New-AzResourceGroupDeployment -ResourceGroupName $imageResourceGroup -TemplateFile $templateFilePath -Api-Version "2020-02-14" -imageTemplateName $imageTemplateName -svclocation $location + ``` + +1. Run the following command to invoke the image build process: + + ```powershell + Invoke-AzResourceAction -ResourceName $imageTemplateName -ResourceGroupName $imageResourceGroup -ResourceType Microsoft.VirtualMachineImages/imageTemplates -ApiVersion "2020-02-14" -Action Run -Force + ``` + +1. Run the following command to determine the image provisioning state: + + ```powershell + Get-AzImageBuilderTemplate -ImageTemplateName $imageTemplateName -ResourceGroupName $imageResourceGroup | Select-Object -Property Name, LastRunStatusRunState, LastRunStatusMessage, ProvisioningState + ``` + + > **Note:** The following output will indicate that the build process has completed successfully: + + ```powershell + Name LastRunStatusRunState LastRunStatusMessage ProvisioningState + ---- --------------------- -------------------- ----------------- + templateWinVSCodeCustom Succeeded Succeeded + ``` + +1. Alternatively, to monitor the build progress, use the following procedure: + + 1. In the Azure portal, search for and select **`Image templates`**. + 1. On the **Image templates** page, select **templateWinVSCodeCustom**. + 1. On the **templateWinVSCodeCustom** page, in the **Essentials** section, note the value of the **Build run state** entry. + + > **Note:** The build process might take about 30 minutes. For the sake of time, you can proceed to the next task while the build is in progress and return to verify the completion later. + +1. Once the build completes, in the Azure portal, search for and select **`Azure compute galleries`**. +1. On the **Azure compute galleries** page, select **compute_gallery_custom**. +1. On the **compute_gallery_custom** page, ensure that the **Definitions** tab is selected and, in the list of definitions, select **imageDefDevBoxVSCodeCustom**. +1. On the **imageDefDevBoxVSCodeCustom** page, select the **Versions** tab and verify that the **1.0.0 (latest version)** entry appears on the list with the **Provisioning State** set to **Succeeded**. +1. Select the **1.0.0 (latest version)** entry. +1. On the **1.0.0 (compute_gallery_custom/imageDefDevBoxVSCodeCustom/1.0.0)** page, review the VM image version settings. + +### Create an Azure dev center network connection + +In this task, you will configure Azure dev center networking to be used in a scenario that requires private connectivity to resources hosted within an Azure virtual network. Unlike Microsoft hosted network that you might have used in previous labs, virtual network connections also support hybrid scenarios (providing connectivity to on-premises resources) and Microsoft Entra hybrid join of Azure dev boxes (in addition to support for Microsoft Entra join). + +1. In the web browser displaying the Azure portal, in the **Search** text box, search for and select **`Virtual networks`**. +1. On the **Virtual networks** page, select **+ Create**. +1. On the **Basics** tab of the **Create virtual network** page, specify the following settings and then select **Next**: + + | Setting | Value | + | -------------- | ------------------------------------------------------------ | + | Subscription | The name of the Azure subscription you are using in this lab | + | Resource group | **rg-devcenter-custom** | + | Name | **vnet-custom** | + | Location | **(US) East US** | + +1. On the **Security** tab of the **Create virtual network** page, review the existing settings without changing their default values and then select **Next**. +1. On the **IP addresses** tab of the **Create virtual network** page, review the existing settings without changing their default values and then select **Review + Create**. +1. On the **Review + Create** tab of the **Create virtual network** page, select **Create**. + + > **Note:** Wait for the virtual network to be created. This should take less than 1 minute. + +1. In the Azure portal, in the **Search** text box, search for and select **`Network connections`**. +1. On the **Network connections** page, select **+ Create**. +1. On the **Basics** tab of the **Create a network connection** page, specify the following settings and then select **Review + Create**: + + | Setting | Value | + | --------------- | ------------------------------------------------------------ | + | Subscription | The name of the Azure subscription you are using in this lab | + | Resource group | **rg-devcenter-custom** | + | Name | **network-connection-vnet-custom** | + | Virtual network | **vnet-custom** | + | Subnet | **default** | + +1. On the **Review + Create** tab of the **Create virtual network** page, select **Create**. + + > **Note:** Wait for the network connection to be created. This might take about 1 minute. + +1. In the Azure portal, search for and select **`Dev centers`** and, on the **Dev centers** page, select **devcenter-custom**. +1. On the **devcenter-custom** page, in the vertical navigation menu on the left side, expand the **Dev box configuration** section and select **Networking**. +1. On the **devcenter-custom | Networking** page, select **+ Add**. +1. In the **Add network connection** pane, in the **Network connection** drop-down list, select **network-connection-vnet-custom** and then select **Add**. + + > **Note:** Do not wait for network connection to be added, but instead proceed to the next task. Adding a network connection might take about 1 minute. + +### Adding image definitions to an Azure dev center project + +In this task, you will add image definitions to an Azure dev center project. Image definitions combine an Azure Marketplace or a custom image with configurable tasks that define additional modifications to be applied to the underlying image. An image definition can be used to build a new image (containing all changes, including those applied by tasks) or to create dev box pools directly. Creating a reusable image minimizes time required for dev box provisioning. + +To configure imaging for Microsoft Dev Box team customizations, project-level catalogs must be enabled (which you configured when creating the dev center). In this task, you will configure catalog sync settings for the project. This will involve attaching a catalog that contains image definition files. + +1. In the web browser displaying the Azure portal, on the **devcenter-custom** page, in the vertical navigation menu on the left side, expand the **Manage** section and select **Projects**. +1. On the **devcenter-custom | Projects** page, select **+ Create**. +1. On the **Basics** tab of the **Create a project** page, specify the following settings and then select **Next: Dev box management**: + + | Setting | Value | + | -------------- | ------------------------------------------------------------ | + | Subscription | The name of the Azure subscription you are using in this lab | + | Resource group | **rg-devcenter-custom** | + | Dev center | **devcenter-custom** | + | Name | **devcenter-project-custom** | + | Description | **Custom Dev Center Project** | + +1. On the **Dev box management** tab of the **Create a project** page, specify the following settings and then select **Next: Catalogs**: + + | Setting | Value | + | ----------------------- | ----- | + | Enable dev box limits | Yes | + | Dev boxes per developer | **2** | + +1. On the **Catalogs** tab of the **Create a project** page, specify the following settings and then select **Review + Create**: + + | Setting | Value | + | ---------------------------------- | ------- | + | Deployment environment definitions | Enabled | + | Image definitions | Enabled | + +1. On the **Review + Create** tab of the **Create a project** page, select **Create**: + + > **Note:** Wait for the project to be created. This should take less than 1 minute. + +1. On the **Deployment is completed** page, select **Go to resource**. +1. On the **devcenter-project-custom** page, in the vertical navigation menu on the left side, expand the **Settings** section and select **Catalogs**. +1. On the **devcenter-project-custom | Catalogs** page, select **+ Add**. +1. In the **Add catalog** pane, in the **Name** text box, enter **`image-definitions-custom`**, in the **Catalog source** section, select **GitHub**, in the **Authentication type**, select **GitHub app**, leave the checkbox **Automatically sync this catalog** checkbox enabled, and then select **Sign in with GitHub**. +1. If prompted, in the **Sign in with GitHub** window, enter your GitHub credentials and select **Sign in**. +1. If you see a message stating "We could not find any GitHub repos associated with the account" with a link to **configure your repositories**, complete the following additional steps to set up the Microsoft DevCenter GitHub App: + + 1. Select the **configure your repositories** link. This will open a new browser tab or window directed to GitHub. + 1. On the GitHub **Install Microsoft DevCenter** page, you will be prompted to install the app on your personal account. + 1. In the **Install on your personal account** section, choose one of the following options: + - Select **All repositories** to grant access to all current and future repositories in your account. + - Select **Only select repositories** to choose specific repositories. If you choose this option, use the **Select repositories** dropdown to select the **contoso-co-eShop** repository (or any other repositories you want to make available to Azure DevCenter). + 1. Review the permissions that will be granted under **with these permissions** section, which typically includes "Read access to code and metadata". + 1. Select **Install** to complete the GitHub App installation. + 1. You will be redirected back to the Azure portal. If the redirect doesn't happen automatically, close the GitHub tab and return to the Azure portal. + 1. Back in the Azure portal, on the **Add catalog** page, select **Refresh** or refresh the page to reload the repository list. + + > **Note:** You need to fork the https://github.com/MicrosoftLearning/contoso-co-eShop repository to your GitHub account before you can complete this step. + +1. If prompted, in the **Authorize Microsoft DevCenter** window, select **Authorize Microsoft DevCenter**. +1. Back in the **Add catalog** pane, in the **Repo** drop-down list, select **contoso-co-eShop**, in the **Branch** drop-down list, accept the **Default branch** entry, in the **Folder path**, enter **`.devcenter/catalog/image-definitions`** and then select **Add**. +1. Back on the **devcenter-project-custom | Catalogs** page, verify that the sync completes successfully by monitoring the entry in the **Status** column. +1. Select the **Sync successful** link in the **Status** column, review the resulting notification pane, verify 3 items were added to the catalog, and close the pane by selecting the **x** symbol in the upper right corner. +1. Back on the **devcenter-project-custom | Catalogs** page, select **image-definitions-custom** and verify that it contains three entries named **ContosoBaseImageDefinition**, **backend-eng**, and **frontend-eng**. +1. In the Azure portal, navigate back to the **devcenter-project-custom** page, in the vertical navigation menu on the left side, expand the **Manage** section, select **Image definitions**, and verify that the page displays the same 3 image definitions you identified earlier in this task. + +### Create a customized dev box pool + +In this task, you will use the newly provisioned image definitions to create a dev box pool. The pool will also utilize the network connection you set up earlier in this exercise. + +1. In the Azure portal displaying the **devcenter-project-custom | Image definitions** page, in the vertical navigation menu on the left side, in the **Manage** section, select **Dev box pools**. +1. On the **devcenter-project-custom | Dev box pools** page, select **+ Create**. +1. On the **Basics** tab of the **Create a dev box pool** page, specify the following settings and then select **Create**: + + | Setting | Value | + | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------- | + | Name | **devbox-pool-custom** | + | Definition | **frontend-eng** | + | Network connection | **network-connection-vnet-custom** | + | Enable single sign-on | Enabled | + | Dev box Creator Privileges | **Local Administrator** | + | Enable auto-stop on schedule | Enabled | + | Stop time | **07:00 PM** | + | Time zone | Your current time zone | + | Enable hibernate on disconnect | Enabled | + | Grace period in minutes | **60** | + | I confirm that my organization has Azure Hybrid Benefits licenses, which will apply to all dev boxes in this pool | Enabled | + + > **Note:** Wait for the dev box pool to be created. This might take about 2 minutes. + +### Evaluate a customized dev box + +In this task, you will evaluate the customized dev box functionality by creating a dev box using the custom image and private networking. + +> **Note:** For this task, you will need the developer user account credentials from the previous lab or you can create a new user account following the steps in the prepare lab environment section. + +1. First, you need to assign permissions to allow developers to use the new dev box pool. In the Azure portal, navigate to the **devcenter-project-custom** page. +1. In the vertical navigation menu on the left side, select **Access control (IAM)**. +1. On the **devcenter-project-custom | Access control (IAM)** page, select **+ Add** and, in the drop-down list, select **Add role assignment**. +1. On the **Role** tab of the **Add role assignment** page, ensure that the **Job function roles** tab is selected, in the list of roles, select **DevCenter Dev Box Users** and select **Next**. +1. On the **Members** tab of the **Add role assignment** page, ensure that the **User, group, or service principal** option is selected and click **+ Select members**. +1. In the **Select members** pane, search for and select **`DevCenter_Dev_Users`** and then click **Select**. +1. Back on the **Members** tab of the **Add role assignment** page, select **Next**. +1. On the **Review + assign** tab of the **Add role assignment** page, select **Review + assign**. +1. Start a web browser incognito/in-private and navigate to the Microsoft Dev Box developer portal at `https://aka.ms/devbox-portal`. +1. When prompted to sign in, provide the credentials of the **devuser01** user account. +1. On the **Welcome, devuser01** page of the Microsoft Dev Box developer portal, select **+ New dev box**. +1. In the **Add a dev box** pane, specify the following settings: + + | Setting | Value | + | ------- | ---------------------------- | + | Name | **devuser01custombox01** | + | Project | **devcenter-project-custom** | + | Pool | **devbox-pool-custom** | + +1. Review other information presented in the **Add a dev box** pane, including the pool specifications, hibernation support status, and the scheduled shutdown timing. Note the customization options available. +1. In the **Add a dev box** pane, select **Create**. + + > **Note:** The dev box creation process may take 30-65 minutes. For the sake of time in this lab, you don't need to wait for the complete provisioning, but you should observe that the dev box is being created with the custom image and private network configuration. + +1. Observe the provisioning status and note that the dev box is being created using: + - The custom **frontend-eng** image definition + - Private network connection **network-connection-vnet-custom** + - Custom configurations from the image definition catalog + +## Clean up resources + +Now that you finished the exercise, you should delete the cloud resources you created to avoid unnecessary resource usage. + +1. In your browser navigate to the Azure portal [https://portal.azure.com](https://portal.azure.com); signing in with your Azure credentials if prompted. +1. Navigate to the resource group you created and view the contents of the resources used in this exercise. +1. On the toolbar, select **Delete resource group**. +1. Enter the resource group name and confirm that you want to delete it. + +> **CAUTION:** Deleting a resource group deletes all resources contained within it. If you chose an existing resource group for this exercise, any existing resources outside the scope of this exercise will also be deleted. + +You don't need to clean up your GitHub repo or project, as they will remain available for you to use as a reference and portfolio item. + +If you want to delete the repo, you can do so by following this documentation: [Deleting a repository](https://docs.github.com/repositories/creating-and-managing-repositories/deleting-a-repository). diff --git a/Instructions/intermediate/04-configure-agent-pools-pipeline-styles.md b/Instructions/intermediate/04-configure-agent-pools-pipeline-styles.md new file mode 100644 index 0000000..53dd959 --- /dev/null +++ b/Instructions/intermediate/04-configure-agent-pools-pipeline-styles.md @@ -0,0 +1,367 @@ +--- +lab: + topic: Intermediate + title: "Configure Agent Pools and Understand Pipeline Styles" + description: "Learn how to implement and use self-hosted agents with YAML pipelines in Azure DevOps. You'll create Azure VMs as agents and configure agent pools for CI/CD processes." +--- + +# Configure Agent Pools and Understand Pipeline Styles + +In this lab, you'll learn how to implement and use self-hosted agents with YAML pipelines. YAML-based pipelines allow you to fully implement CI/CD as code, in which pipeline definitions reside in the same repository as the code that is part of your Azure DevOps project. + +Regardless of the choice of the pipeline style, to build your code or deploy your solution by using Azure Pipelines, you need an agent. An agent hosts compute resources that run one job at a time. Jobs can be run directly on the host machine of the agent or in a container. You have an option to run your jobs using Microsoft-hosted agents, which are managed for you, or implementing a self-hosted agent that you set up and manage on your own. + +You will learn how to: + +- Create and configure Azure VMs as build agents. +- Implement YAML-based pipelines. +- Implement self-hosted agents. +- Configure agent pools for pipelines. + +This lab takes approximately **30** minutes to complete. + +## Before you start + +To complete the lab, you need: + +- **Microsoft Edge** or an [Azure DevOps supported browser.](https://docs.microsoft.com/azure/devops/server/compatibility) +- An Azure DevOps organization. If you don't already have one, create one by following the instructions at [Create an organization or project collection](https://docs.microsoft.com/azure/devops/organizations/accounts/create-organization). +- **An Azure subscription**: If you don't already have an Azure subscription, sign up for a free account at [Azure Free Account](https://azure.microsoft.com/free). +- **Git 2.47.0 or later**: If you don't have Git installed yet, navigate to the [Git for Windows download page](https://gitforwindows.org/) download it, and install it. +- **Visual Studio Code**: If you don't have Visual Studio Code installed yet, navigate to the [Visual Studio Code download page](https://code.visualstudio.com/), download it, and install it. + +### Set up Azure DevOps organization (if needed) + +If you don't already have an Azure DevOps organization, follow these steps: + +1. Use a private browser session to get a new **personal Microsoft Account (MSA)** at `https://account.microsoft.com` (skip if you already have one). +1. Using the same browser session, sign up for a free Azure subscription at `https://azure.microsoft.com/free` (skip if you already have one). +1. Open a browser and navigate to Azure portal at `https://portal.azure.com`, then search at the top of the Azure portal screen for **Azure DevOps**. In the resulting page, click **Azure DevOps organizations**. +1. Next, click on the link labelled **My Azure DevOps Organizations** or navigate directly to `https://aex.dev.azure.com`. +1. On the **We need a few more details** page, select **Continue**. +1. In the drop-down box on the left, choose **Default Directory**, instead of **Microsoft Account**. +1. If prompted (_"We need a few more details"_), provide your name, e-mail address, and location and click **Continue**. +1. Back at `https://aex.dev.azure.com` with **Default Directory** selected click the blue button **Create new organization**. +1. Accept the _Terms of Service_ by clicking **Continue**. +1. If prompted (_"Almost done"_), leave the name for the Azure DevOps organization at default (it needs to be a globally unique name) and pick a hosting location close to you from the list. +1. Once the newly created organization opens in **Azure DevOps**, select **Organization settings** in the bottom left corner. +1. At the **Organization settings** screen select **Billing** (opening this screen takes a few seconds). +1. Select **Setup billing** and on the right-hand side of the screen, select your **Azure Subscription** and then select **Save** to link the subscription with the organization. +1. Once the screen shows the linked Azure Subscription ID at the top, change the number of **Paid parallel jobs** for **MS Hosted CI/CD** from 0 to **1**. Then select **SAVE** button at the bottom. + + > **Note**: You may **wait a couple of minutes before using the CI/CD capabilities** so that the new settings are reflected in the backend. Otherwise, you will still see the message _"No hosted parallelism has been purchased or granted"_. + +1. In **Organization Settings**, go to section **Pipelines** and click **Settings**. +1. Toggle the switch to **Off** for **Disable creation of classic build pipelines** and **Disable creation of classic release pipelines**. +1. In **Organization Settings**, go to section **Security** and click **Policies**. +1. Toggle the switch to **On** for **Allow public projects**. + +### Create and configure the Azure DevOps project (if needed) + +1. Open your browser and navigate to your Azure DevOps organization. +1. Select the **New Project** option and use the following settings: + - name: **eShopOnWeb** + - visibility: **Private** + - Advanced: Version Control: **Git** + - Advanced: Work Item Process: **Scrum** +1. Select **Create**. + +### Import eShopOnWeb git repository (if needed) + +1. Open the previously created **eShopOnWeb** project. +1. Select the **Repos > Files**, **Import a Repository** and then select **Import**. +1. On the **Import a Git Repository** window, paste the following URL `https://github.com/MicrosoftLearning/eShopOnWeb.git` and select **Import**: + +1. The repository is organized the following way: + + - **.ado** folder contains Azure DevOps YAML pipelines. + - **.devcontainer** folder container setup to develop using containers (either locally in VS Code or GitHub Codespaces). + - **infra** folder contains Bicep & ARM infrastructure as code templates used in some lab scenarios. + - **.github** folder contains YAML GitHub workflow definitions. + - **src** folder contains the .NET 8 website used in the lab scenarios. + +1. Leave the web browser window open. +1. Go to **Repos > Branches**. +1. Hover on the **main** branch then click the ellipsis on the right of the column. +1. Click on **Set as default branch**. + +## Create agents and configure agent pools + +In this section, you will create an Azure virtual machine (VM) and use it to create an agent and configure agent pools. + +### Create and connect to an Azure VM + +1. In your browser, open the Azure Portal at `https://portal.azure.com`. If prompted, sign in by using an account with the Owner role in your Azure subscription. + +1. In the **Search resources, services and docs (G+/)** box, type **`Virtual Machines`** and select it from the dropdown list. + +1. Select the **Create** button. + +1. Select the **Presets**. + + ![Screenshot of the create virtual machine with preset configuration.](media/create-virtual-machine-preset.png) + +1. Select the **Dev/Test** as the workload environment and the **General purpose** as the workload type. + +1. Select the **Continue to create a VM** button, on the **Basics** tab perform the following actions and then select **Management**: + + | Setting | Action | + | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | **Subscription** drop-down list | Select your Azure subscription. | + | **Resource group** section | Create a new resource group named **rg-eshoponweb-agentpool**. | + | **Virtual machine name** text box | Enter name of your preference, for example, **`eshoponweb-vm`**. | + | **Region** drop-down list | You can choose your closest [azure](https://azure.microsoft.com/explore/global-infrastructure/geographies) region. For example, "eastus", "eastasia", "westus", etc. | + | **Availability options** drop-down list | Select **No infrastructure redundancy required**. | + | **Security type** drop-down list | Select with the **Trusted launch virtual machines** option. | + | **Image** drop-down list | Select the **Windows Server 2022 Datacenter: Azure Edition - x64 Gen2** image. | + | **Size** drop-down list | Select the cheapest **Standard** size for testing purposes. | + | **Username** text box | Enter the username of your preference | + | **Password** text box | Enter the password of your preference | + | **Public inbound ports** section | Select **Allow selected ports**. | + | **Select inbound ports** drop-down list | Select **RDP (3389)**. | + +1. On the **Management** tab, in the **Identity** section, select the **Enable system assigned managed identity** checkbox and then select **Review + create**: + +1. On the **Review + create** tab, select **Create**. + + > **Note**: Wait for the provisioning process to complete. This should take about 2 minutes. + +1. In the Azure portal, navigate to the page displaying configuration of the newly created Azure VM. + +1. On the Azure VM page, select **Connect**, in the drop-down menu, select **Connect**, then select **Download RDP file**. + +1. Use the downloaded RDP file to establish a Remote Desktop session to the operating system running in the Azure VM. + +### Create an agent pool + +1. In the Remote Desktop session to the Azure VM, start Microsoft Edge web browser. + +1. In the web browser, navigate to the Azure DevOps portal at `https://aex.dev.azure.com` and sign in to access your organization. + + > **Note**: If it is your first time accessing the Azure DevOps portal, you may need to create your profile. + +1. Open the **eShopOnWeb** project, and select **Project settings** from the left-side bottom menu. + +1. From **Pipelines > Agent Pools**, select the **Add pool** button. + +1. Choose the **Self-hosted** pool type. + +1. Provide a name for the agent pool, such as **eShopOnWebSelfPool**, and add an optional description. + +1. Leave the **Grant access permission to all pipelines** option unchecked. + + ![Screenshot showing add agent pool options with self-hosted type.](media/create-new-agent-pool-self-hosted-agent.png) + + > **Note**: Granting access permission to all pipelines is not recommended for production environments. It is only used in this lab to simplify the configuration of the pipeline. + +1. Select **Create** button to create the agent pool. + +### Download and extract the agent installation files + +1. In the Azure DevOps portal, select the newly created agent pool and then select the **Agents** tab. + +1. Select the **New agent** button and then **Download** button from the **Download agent** in the new pop-up window. + + > **Note**: Follow the installation instructions to install the agent. + + > **Note**: The name of the zip file you downloaded with the **Download** button should be similar to the following `vsts-agent-win-x64-X.YYY.Z.zip` (at the time of writing this lab the file name is `vsts-agent-win-x64-4.255.0.zip`). The file name will be used later in one of the agent installation commands. + +1. Start a PowerShell session and run the following commands to create a folder named **agent**. + + ```powershell + mkdir agent ; cd agent + ``` + + > **Note**: Make sure you are in the folder where you want to install the agent, for example, C:\agent. + +1. Run the following command to extract the content of the downloaded agent installer files: + + ```powershell + Add-Type -AssemblyName System.IO.Compression.FileSystem ; [System.IO.Compression.ZipFile]::ExtractToDirectory("$HOME\Downloads\vsts-agent-win-x64-4.255.0.zip", "$PWD") + ``` + + > **Note**: If you downloaded the agent to a different location (or the downloaded version differs), adjust the above command accordingly. + + > **Note**: Make sure that the zip file name specified inside the `ExtractToDirectory` command is the same as the zip file name you previously downloaded. + +### Create a PAT token + +> **Note**: Before configuring the agent, you need to create a PAT token (unless you have an existing one). To create a PAT token, follow the steps below: + +1. Within the Remote Desktop session to the Azure VM, open another browser window, navigate to the Azure DevOps portal at `https://aex.dev.azure.com`, and access your organization and project. + +1. Select **User settings** from the right-side top menu (directly to the left of your user's avatar icon). + +1. Select the **Personal access tokens** menu item. + + ![Screenshot showing the personal access tokens menu.](media/personal-access-token-menu.png) + +1. Select the **New Token** button. + +1. Provide a name for the token, such as **eShopOnWebToken**. + +1. Select the Azure DevOps organization for you want to use the token. + +1. Set the expiration date for the token (only used to configure the agent). + +1. Select the custom defined scope. + +1. Select to show all scopes. + +1. Select the **Agent Pools (Read & Manage)** scope. + +1. Select the **Create** button to create the token. + +1. Copy the token value and save it in a safe place (you will not be able to see it again. You can only regenerate the token). + + ![Screenshot showing the personal access token configuration.](media/personal-access-token-configuration.png) + + > [!IMPORTANT] + > Use the least privilege option, **Agent Pools (Read & Manage)**, only for the agent configuration. Also, make sure you set the minimum expiration date for the token if that is the only purpose of the token. You can create another token with the same privileges if you need to configure the agent again. + +### Configure the agent + +1. Within the Remote Desktop session to the Azure VM, switch back to the PowerShell window. If necessary, change the current directory to the one into which you extracted the agent installation files earlier in this exercise. + +1. To configure your agent to run unattended, invoke the following command: + + ```powershell + .\config.cmd + ``` + + > **Note**: If you want to run the agent interactively, use `.\run.cmd` instead. + +1. To configure the agent, perform the following actions when prompted: + + - Enter the URL of the Azure DevOps organization (**server URL**) in the format `https://dev.azure.com/{your organization name}`. + - Accept the default authentication type (**`PAT`**). + - Enter the value of the PAT token you created in the previous step. + - Enter the agent pool name **`eShopOnWebSelfPool`** you created earlier in this exercise. + - Enter the agent name **`eShopOnWebSelfAgent`**. + - Accept the default agent work folder (\_work). + - Enter **Y** to configure the agent to run as service. + - Enter **Y** to enable SERVICE_SID_TYPE_UNRESTRICTED for the agent service. + - Enter **`NT AUTHORITY\SYSTEM`** to set the security context for the service. + + > [!IMPORTANT] + > In general, you should follow the principle of least privilege when configuring the service security context. + + - Accept the default option (**N**) to allow the service to start immediately after configuration is finished. + + ![Screenshot showing the agent configuration.](media/agent-configuration.png) + + > **Note**: The agent configuration process will take a few minutes to complete. Once it is done, you will see a message indicating that the agent is running as a service. + + > [!IMPORTANT] If you see an error message indicating that the agent is not running, you may need to start the service manually. To do this, open the **Services** applet in the Windows Control Panel, locate the service named **Azure DevOps Agent (eShopOnWebSelfAgent)**, and start it. + + > [!IMPORTANT] If your agent fails to start, you may need to choose a different folder for the agent work directory. To do this, re-run the agent configuration script and choose a different folder. + +1. Check the agent status by switching to the web browser displaying the Azure DevOps portal, navigating to the agent pool and clicking on the **Agents** tab. You should see the new agent in the list. + + ![Screenshot showing the agent status.](media/agent-status.png) + + > **Note**: For more details on Windows agents, see: [Self-hosted Windows agents](https://learn.microsoft.com/azure/devops/pipelines/agents/windows-agent) + + > [!IMPORTANT] + > In order for the agent to be able to build and deploy Azure resources from the Azure DevOps pipelines (which you will step through in the upcoming labs), you need to install Azure CLI within the operating system of the Azure VM that is hosting the agent. + +1. Start a web browser and navigate to the page `https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-windows?tabs=azure-cli&pivots=msi#install-or-update`. + +1. Download and install Azure CLI. + +1. (Optional) If you prefer, run the following PowerShell command to install Azure CLI: + + ```powershell + $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi; Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet'; Remove-Item .\AzureCLI.msi + ``` + + > **Note**: If you are using a different version of the Azure CLI, you may need to adjust the above command accordingly. + +1. In the web browser navigate to the page Microsoft .NET 8.0 SDK installer page at `https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/sdk-8.0.403-windows-x64-installer`. + + > [!IMPORTANT] + > You need to install the .NET 8.0 SDK (or higher) on the Azure VM that is hosting the agent. This is required to build the eShopOnWeb application in the upcoming labs. Any other tools or SDKs required for the application build should also be installed on the Azure VM. + +1. Download and install Microsoft .NET 8.0 SDK. + +## Author YAML-based Azure Pipelines + +In this section, you will create an application lifecycle build pipeline, using a YAML-based template. + +### Create an Azure DevOps YAML pipeline + +In this task, you will create a YAML-based pipeline for the **eShopOnWeb** project. + +1. From the web browser displaying the Azure DevOps portal with the **eShopOnWeb** project open, in the vertical navigational pane on the left side, click **Pipelines**. +1. Click the **Create Pipeline** button - if you don't have any other pipelines created yet or click **New pipeline** to create an additional new one. + +1. On the **Where is your code?** pane, click **Azure Repos Git**. +1. On the **Select a repository** pane, click **eShopOnWeb**. +1. On the **Configure your pipeline** pane, click **Existing Azure Pipelines YAML File**. +1. On the **Select an existing YAML file**, select **main** for the Branch, and **/.ado/eshoponweb-ci-pr.yml** for the Path. +1. Click **Continue**. +1. On the **Review your pipeline YAML** pane, review the sample pipeline. This is a rather straight-forward .NET application Build pipeline, which does the following: + + - A single Stage: Build + - A single Job: Build + - 4 tasks within the Build Job: + - Dotnet Restore + - Dotnet Build + - Dotnet Test + - Dotnet Publish + +1. On the **Review your pipeline YAML** pane, click the down-facing caret symbol next to the **Run** button, click **Save**. + + > **Note**: We are just creating the pipeline definition for now, without running it. You will first set up an Azure DevOps agent pool and run the pipeline in a later exercise. + +### Update the YAML pipeline with the self-hosted agent pool + +1. In the Azure DevOps portal, navigate to the **eShopOnWeb** project, and select **Pipelines** from the left-side menu. +1. Click on the **Edit** button for the pipeline you created in the previous task. +1. On the **eShopOnWeb** edit pane, in the existing YAML-based pipeline, remove line 13 which says **vmImage: ubuntu-latest** designating the target agent pool the following content, designating the newly created self-hosted agent pool: + + ```yaml + pool: + name: eShopOnWebSelfPool + demands: Agent.Name -equals eShopOnWebSelfAgent + ``` + + > **WARNING**: Be careful with copy/paste, make sure you have the same indentation shown above. + + ![Screenshot showing the YAML pool syntax.](media/eshoponweb-ci-pr-agent-pool.png) + +1. On the **eShopOnWeb** edit pane, in the upper right corner of the pane, click **Validate and save**. Then click **Save**. +1. On the **eShopOnWeb** edit pane, in the upper right corner of the pane, click **Run**. + + > **Note**: The pipeline will run on the self-hosted agent pool you created in the previous exercise. + +1. Open the pipeline run and monitor the job until its successful completion. + + > **Note**: If you receive a permissions prompt, click **Permit** to allow the pipeline to run. + +1. Once the pipeline run is complete, review the output and verify that the pipeline ran successfully. + +## Clean up resources + +When you complete the lab, it's important to clean up your Azure resources to avoid unnecessary charges: + +### Delete the Azure resources + +1. In the Azure Portal at `https://portal.azure.com`, navigate to the **Resource groups** section. +1. Find and select the **rg-eshoponweb-agentpool** resource group. +1. On the resource group page, click **Delete resource group**. +1. Type the resource group name to confirm deletion and click **Delete**. +1. Wait for the deletion process to complete. + +### Clean up Azure DevOps resources + +You don't need to clean up your Azure DevOps organization or project, as they will remain available for you to use as a reference and portfolio item. Azure DevOps provides free tier usage that includes basic features for small teams. + +If you want to remove the agent pool and clean up the project: + +1. In your Azure DevOps project, go to **Project Settings**. +1. Navigate to **Pipelines > Agent pools**. +1. Find the **eShopOnWebSelfPool** agent pool and delete it. +1. If you want to delete the entire project, go to the project **Overview** page and click **Delete** at the bottom. + +> **CAUTION:** Deleting a project deletes all work items, repositories, builds, and other project artifacts. If you used an existing project for this exercise, any existing resources outside the scope of this exercise will also be deleted. diff --git a/Instructions/intermediate/05-enable-continuous-integration-azure-pipelines.md b/Instructions/intermediate/05-enable-continuous-integration-azure-pipelines.md new file mode 100644 index 0000000..30d260a --- /dev/null +++ b/Instructions/intermediate/05-enable-continuous-integration-azure-pipelines.md @@ -0,0 +1,242 @@ +--- +lab: + topic: Intermediate + title: "Enable Continuous Integration with Azure Pipelines" + description: "Learn how to define build pipelines in Azure DevOps using YAML for Pull Request validation and Continuous Integration implementation." +--- + +# Enable Continuous Integration with Azure Pipelines + +In this lab, you'll learn how to define build pipelines in Azure DevOps using YAML. The pipelines will be used in two scenarios: as part of Pull Request validation process and as part of the Continuous Integration implementation. + +You will learn how to: + +- Include build validation as part of a Pull Request. +- Configure CI pipeline as code with YAML. +- Set up branch policies for code protection. +- Work with Pull Requests and automated builds. + +This lab takes approximately **30** minutes to complete. + +## Before you start + +To complete the lab, you need: + +- **Microsoft Edge** or an [Azure DevOps supported browser.](https://docs.microsoft.com/azure/devops/server/compatibility) +- An Azure DevOps organization. If you don't already have one, create one by following the instructions at [Create an organization or project collection](https://docs.microsoft.com/azure/devops/organizations/accounts/create-organization). + +### Set up Azure DevOps organization (if needed) + +If you don't already have an Azure DevOps organization, follow these steps: + +1. Use a private browser session to get a new **personal Microsoft Account (MSA)** at `https://account.microsoft.com` (skip if you already have one). +1. Using the same browser session, sign up for a free Azure subscription at `https://azure.microsoft.com/free` (skip if you already have one). +1. Open a browser and navigate to Azure portal at `https://portal.azure.com`, then search at the top of the Azure portal screen for **Azure DevOps**. In the resulting page, click **Azure DevOps organizations**. +1. Next, click on the link labelled **My Azure DevOps Organizations** or navigate directly to `https://aex.dev.azure.com`. +1. On the **We need a few more details** page, select **Continue**. +1. In the drop-down box on the left, choose **Default Directory**, instead of **Microsoft Account**. +1. If prompted (_"We need a few more details"_), provide your name, e-mail address, and location and click **Continue**. +1. Back at `https://aex.dev.azure.com` with **Default Directory** selected click the blue button **Create new organization**. +1. Accept the _Terms of Service_ by clicking **Continue**. +1. If prompted (_"Almost done"_), leave the name for the Azure DevOps organization at default (it needs to be a globally unique name) and pick a hosting location close to you from the list. +1. Once the newly created organization opens in **Azure DevOps**, select **Organization settings** in the bottom left corner. +1. At the **Organization settings** screen select **Billing** (opening this screen takes a few seconds). +1. Select **Setup billing** and on the right-hand side of the screen, select your **Azure Subscription** and then select **Save** to link the subscription with the organization. +1. Once the screen shows the linked Azure Subscription ID at the top, change the number of **Paid parallel jobs** for **MS Hosted CI/CD** from 0 to **1**. Then select **SAVE** button at the bottom. + + > **Note**: You may **wait a couple of minutes before using the CI/CD capabilities** so that the new settings are reflected in the backend. Otherwise, you will still see the message _"No hosted parallelism has been purchased or granted"_. + +1. In **Organization Settings**, go to section **Pipelines** and click **Settings**. +1. Toggle the switch to **Off** for **Disable creation of classic build pipelines** and **Disable creation of classic release pipelines**. +1. In **Organization Settings**, go to section **Security** and click **Policies**. +1. Toggle the switch to **On** for **Allow public projects**. + +### Create and configure the Azure DevOps project (if needed) + +1. Open your browser and navigate to your Azure DevOps organization. +1. Select the **New Project** option and use the following settings: + - name: **eShopOnWeb** + - visibility: **Private** + - Advanced: Version Control: **Git** + - Advanced: Work Item Process: **Scrum** +1. Select **Create**. + +### Import eShopOnWeb git repository (if needed) + +1. Open the previously created **eShopOnWeb** project. +1. Select the **Repos > Files**, **Import a Repository** and then select **Import**. +1. On the **Import a Git Repository** window, paste the following URL `https://github.com/MicrosoftLearning/eShopOnWeb.git` and select **Import**: + +1. The repository is organized the following way: + + - **.ado** folder contains Azure DevOps YAML pipelines. + - **.devcontainer** folder container setup to develop using containers (either locally in VS Code or GitHub Codespaces). + - **infra** folder contains Bicep & ARM infrastructure as code templates used in some lab scenarios. + - **.github** folder contains YAML GitHub workflow definitions. + - **src** folder contains the .NET website used in the lab scenarios. + +1. Leave the web browser window open. +1. Go to **Repos > Branches**. +1. Hover on the **main** branch then click the ellipsis on the right of the column. +1. Click on **Set as default branch**. + +## Include build validation as part of a Pull Request + +In this section, you will include build validation to validate a Pull Request. + +### Import the YAML build definition + +In this task, you will import the YAML build definition that will be used as a Branch Policy to validate the pull requests. + +Let's start by importing the build pipeline named [eshoponweb-ci-pr.yml](https://github.com/MicrosoftLearning/eShopOnWeb/blob/main/.ado/eshoponweb-ci-pr.yml). + +1. Go to **Pipelines > Pipelines** +1. Click on **Create Pipeline** or **New Pipeline** button +1. Select **Azure Repos Git (YAML)** +1. Select the **eShopOnWeb** repository +1. Select **Existing Azure Pipelines YAML File** +1. Select the **main** branch and the **/.ado/eshoponweb-ci-pr.yml** file, then click on **Continue** + + The build definition consists of the following tasks: + + - **DotNet Restore**: With NuGet Package Restore you can install all your project's dependency without having to store them in source control. + - **DotNet Build**: Builds a project and all of its dependencies. + - **DotNet Test**: .Net test driver used to execute unit tests. + - **DotNet Publish**: Publishes the application and its dependencies to a folder for deployment to a hosting system. In this case, it's **Build.ArtifactStagingDirectory**. + +1. On the **Review your pipeline YAML** pane, click the down-facing caret symbol next to the **Run** button, click **Save**. +1. Your pipeline will take a name based on the project name. Let's **rename** it for identifying the pipeline better. Go to **Pipelines > Pipelines** and click on the recently created pipeline. Click on the ellipsis and **Rename/Move** option. Name it **eshoponweb-ci-pr** and click on **Save**. + +### Branch Policies + +In this task, you will add policies to the main branch and only allow changes using Pull Requests that comply with the defined policies. You want to ensure that changes in a branch are reviewed before they are merged. + +1. Go to **Repos > Branches** section. +1. On the **Mine** tab of the **Branches** pane, hover the mouse pointer over the **main** branch entry to reveal the ellipsis symbol on the right side. +1. Click the ellipsis and, in the pop-up menu, select **Branch Policies**. +1. On the **main** tab of the repository settings, enable the option for **Require minimum number of reviewers**. Add **1** reviewer and check the box **Allow requestors to approve their own changes**(as you are the only user in your project for the lab) +1. On the **main** tab of the repository settings, in the **Build Validation** section, click **+** (Add a new build policy) and in the **Build pipeline** list, select **eshoponweb-ci-pr** then click **Save**. + +### Working with Pull Requests + +In this task, you will use the Azure DevOps portal to create a Pull Request, using a new branch to merge a change into the protected **main** branch. + +1. Navigate to the **Repos** section in the eShopOnWeb navigation and click **Branches**. +1. Create a new branch named **Feature01** based on the **main** branch. +1. Click **Feature01** and navigate to the **/eShopOnWeb/src/Web/Program.cs** file as part of the **Feature01** branch +1. Click the **Edit** button in the top-right +1. Make the following change on the first line: + + ```csharp + // Testing my PR + ``` + +1. Click on **Commit > Commit** (leave default commit message). +1. A message will pop-up, proposing to create a Pull Request (as your **Feature01** branch is now ahead in changes, compared to **main**). Click on **Create a Pull Request**. +1. In the **New pull request** tab, leave defaults and click on **Create**. +1. The Pull Request will show some pending requirements, based on the policies applied to the target **main** branch. + + - At least 1 user should review and approve the changes. + - Build validation, you will see that the build **eshoponweb-ci-pr** was triggered automatically + +1. After all validations are successful, on the top-right click on **Approve**. Now from the **Set auto-complete** dropdown you can click on **Complete**. +1. On the **Complete Pull Request** tab, click on **Complete Merge** + +## Configure CI Pipeline as Code with YAML + +In this section, you will configure CI Pipeline as code with YAML. + +### Import the YAML build definition for CI + +In this task, you will add the YAML build definition that will be used to implement the Continuous Integration. + +Let's start by importing the CI pipeline named [eshoponweb-ci.yml](https://github.com/MicrosoftLearning/eShopOnWeb/blob/main/.ado/eshoponweb-ci.yml). + +1. Go to **Pipelines > Pipelines**. +1. Click on **New Pipeline** button. +1. Select **Azure Repos Git (YAML)**. +1. Select the **eShopOnWeb** repository. +1. Select **Existing Azure Pipelines YAML File**. +1. Select the **main** branch and the **/.ado/eshoponweb-ci.yml** file, then click on **Continue**. + + The CI definition consists of the following tasks: + + - **DotNet Restore**: With NuGet Package Restore you can install all your project's dependency without having to store them in source control. + - **DotNet Build**: Builds a project and all of its dependencies. + - **DotNet Test**: .Net test driver used to execute unit tests. + - **DotNet Publish**: Publishes the application and its dependencies to a folder for deployment to a hosting system. In this case, it's **Build.ArtifactStagingDirectory**. + - **Publish Artifact - Website**: Publish the app artifact (created in the previous step) and make it available as a pipeline artifact. + - **Publish Artifact - Bicep**: Publish the infrastructure artifact (Bicep file) and make it available as a pipeline artifact. + +1. Click on **Run** and wait for the pipeline to execute successfully. + +### Enable Continuous Integration + +The default build pipeline definition doesn't enable Continuous Integration. + +1. Click on the **Edit pipeline** option under the ellipsis menu near **Run new** button in the top-right +1. Now, you need to replace the **# trigger:** and **# - main** lines with the following code: + + ```YAML + trigger: + branches: + include: + - main + paths: + include: + - src/web/* + ``` + + This will automatically trigger the build pipeline if any change is made to the main branch and the web application code (the src/web folder). + + Since you enabled Branch Policies, you need to pass by a Pull Request in order to update your code. + +1. Click the **Validate and save** button to validate and save the pipeline definition. +1. Select **Create a new branch for this commit**. +1. Keep the default branch name and **Start a pull request** checked. +1. Click on **Save**. +1. Your pipeline will take a name based on the project name. Let's **rename** it for identifying the pipeline better. Go to **Pipelines > Pipelines** and click on the recently created pipeline. Click on the ellipsis and **Rename/Move** option. Name it **eshoponweb-ci** and click on **Save**. +1. Go to **Repos > Pull Requests**. +1. Click on the **"Update eshoponweb-ci.yml for Azure Pipelines"** pull request. +1. After all validations are successful, on the top-right click on **Approve**. Now you can click on **Complete**. +1. On the **Complete Pull Request** tab, Click on **Complete Merge** + +### Test the CI pipeline + +In this task, you will create a Pull Request, using a new branch to merge a change into the protected **main** branch and automatically trigger the CI pipeline. + +1. Navigate to the **Repos** section, and click on **Branches**. +1. Create a new branch named **Feature02** based on the **main** branch. +1. Click the new **Feature02** branch. +1. Navigate to the **/eShopOnWeb/src/Web/Program.cs** file and click **Edit** in the top-right. +1. Remove the first line: + + ```csharp + // Testing my PR + ``` + +1. Click on **Commit > Commit** (leave default commit message). +1. A message will pop-up, proposing to create a Pull Request (as your **Feature02** branch is now ahead in changes, compared to **main**). +1. Click on **Create a Pull Request**. +1. In the **New pull request** tab, leave defaults and click on **Create**. +1. The Pull Request will show some pending requirements, based on the policies applied to the target **main** branch. +1. After all validations are successful, on the top-right click on **Approve**. Now from the **Set auto-complete** dropdown you can click on **Complete**. +1. On the **Complete Pull Request** tab, Click on **Complete Merge** +1. Go back to **Pipelines > Pipelines**, you will notice that the build **eshoponweb-ci** was triggered automatically after the code was merged. +1. Click on the **eshoponweb-ci** build then select the last run. +1. After its successful execution, click on **Related > Published** to check the published artifacts: + - Bicep: the infrastructure artifact. + - Website: the app artifact. + +## Clean up resources + +You don't need to clean up your Azure DevOps organization or project, as they will remain available for you to use as a reference and portfolio item. Azure DevOps provides free tier usage that includes basic features for small teams. + +If you want to delete the project, you can do so by following these steps: + +1. In your browser navigate to the Azure DevOps portal at `https://aex.dev.azure.com`. +1. Navigate to the **eShopOnWeb** project you created. +1. On the project settings page, go to **Overview** and click **Delete** at the bottom of the page. +1. Type the project name to confirm deletion and click **Delete**. + +> **CAUTION:** Deleting a project deletes all work items, repositories, builds, and other project artifacts. If you used an existing project for this exercise, any existing resources outside the scope of this exercise will also be deleted. diff --git a/Instructions/intermediate/06-implement-github-actions-ci-cd.md b/Instructions/intermediate/06-implement-github-actions-ci-cd.md new file mode 100644 index 0000000..efbf1b7 --- /dev/null +++ b/Instructions/intermediate/06-implement-github-actions-ci-cd.md @@ -0,0 +1,202 @@ +--- +lab: + topic: Intermediate + title: "Implement GitHub Actions for CI/CD" + description: "Learn how to implement a GitHub Action workflow that deploys an Azure web app using Service Principal authentication and GitHub environments." +--- + +# Implement GitHub Actions for CI/CD + +In this lab, you'll learn how to implement a GitHub Action workflow that deploys an Azure web app. You'll work with GitHub repositories, Azure Service Principals, and automated deployment workflows. + +You will learn how to: + +- Import and configure a GitHub repository for CI/CD. +- Create an Azure Service Principal for GitHub Actions authentication. +- Implement a GitHub Action workflow for CI/CD. +- Set up GitHub environments with manual approval gates. +- Deploy applications to Azure using GitHub Actions. + +This lab takes approximately **40** minutes to complete. + +## Before you start + +To complete the lab, you need: + +- **Microsoft Edge** or an [Azure DevOps supported browser.](https://docs.microsoft.com/azure/devops/server/compatibility) +- **An Azure subscription**: If you don't already have an Azure subscription, sign up for a free account at [Azure Free Account](https://azure.microsoft.com/free). +- Verify that you have a Microsoft account or a Microsoft Entra account with the **Contributor** or the **Owner** role in the Azure subscription. For details, refer to [List Azure role assignments using the Azure portal](https://docs.microsoft.com/azure/role-based-access-control/role-assignments-list-portal) and [View and assign administrator roles in Azure Active Directory](https://docs.microsoft.com/azure/active-directory/roles/manage-roles-portal). +- **A GitHub account**: If you don't already have a GitHub account that you can use for this lab, follow instructions available at [Signing up for a new GitHub account](https://github.com/join) to create one. + +## Import eShopOnWeb to your GitHub Repository + +In this section, you will import the existing [eShopOnWeb](https://github.com/MicrosoftLearning/eShopOnWeb) repository code to your own GitHub public repository. + +The repository is organized the following way: + +- **.ado** folder contains Azure DevOps YAML pipelines. +- **.devcontainer** folder container setup to develop using containers (either locally in VS Code or GitHub Codespaces). +- **infra** folder contains Bicep & ARM infrastructure as code templates used in some lab scenarios. +- **.github** folder container YAML GitHub workflow definitions. +- **src** folder contains the .NET 8 website used on the lab scenarios. + +### Create a public repository in GitHub and import eShopOnWeb + +In this task, you will create an empty public GitHub repository and import the existing [eShopOnWeb](https://github.com/MicrosoftLearning/eShopOnWeb) repository. + +1. From the lab computer, start a web browser, navigate to the [GitHub website](https://github.com/), sign in using your account and click on **New** to create new repository. + + ![Screenshot of the create new repository button.](media/github-new.png) + +1. On the **Create a new repository** page, click on **Import a repository** link (below the page title). + + > **Note**: you can also open the import website directly at + +1. On the **Import your project to GitHub** page: + + | Field | Value | + | ---------------------------------- | ------------------------------------------------- | + | The URL for your source repository | | + | Owner | Your account alias | + | Repository Name | eShopOnWeb | + | Privacy | **Public** | + +1. Click on **Begin Import** and wait for your repository to be ready. + +1. On the repository page, go to **Settings**, click on **Actions > General** and choose the option **Allow all actions and reusable workflows**. Click on **Save**. + + ![Screenshot of the enable GitHub Actions option.](media/enable-actions.png) + +## Setup your GitHub Repository and Azure access + +In this section, you will create an Azure Service Principal to authorize GitHub accessing your Azure subscription from GitHub Actions. You will also setup the GitHub workflow that will build, test and deploy your website to Azure. + +### Create an Azure Service Principal and save it as GitHub secret + +In this task, you will create the Azure Service Principal used by GitHub to deploy the desired resources. As an alternative, you could also use [OpenID connect in Azure](https://docs.github.com/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure), as a secretless authentication mechanism. + +1. On your lab computer, in a browser window, open the Azure Portal at `https://portal.azure.com`. +1. In the portal, look for **Resource Groups** and click on it. +1. Click on **+ Create** to create a new Resource Group for the exercise. +1. On the **Create a resource group** tab, give the following name to your Resource Group: **rg-eshoponweb-NAME** (replace NAME for some unique alias). Click on **Review + Create > Create**. +1. In the Azure Portal, open the **Cloud Shell** (next to the search bar). + + > **Note**: if the Azure portal asks you to create a storage, you can choose **No storage account required** options, select your subscription and click on **Apply** button + +1. Make sure the terminal is running in **Bash** mode and execute the following command, replacing **SUBSCRIPTION-ID** and **RESOURCE-GROUP** with your own identifiers (both can be found on the **Overview** page of the Resource Group): + + ```bash + az ad sp create-for-rbac --name GH-Action-eshoponweb --role contributor --scopes /subscriptions/SUBSCRIPTION-ID/resourceGroups/RESOURCE-GROUP --sdk-auth + ``` + + > **Note**: Make sure this is typed or pasted as a single line! + + > **Note**: this command will create a Service Principal with Contributor access to the Resource Group created before. This way we make sure GitHub Actions will only have the permissions needed to interact only with this Resource Group (not the rest of the subscription) + +1. The command will output a JSON object, you will later use it as a GitHub secret for the workflow. Copy the JSON. The JSON contains the identifiers used to authenticate against Azure in the name of a Microsoft Entra identity (service principal). + + ```JSON + { + "clientId": "", + "clientSecret": "", + "subscriptionId": "", + "tenantId": "", + (...) + } + ``` + +1. (Skip if already registered) You also need to run the following command to register the resource provider for the **Azure App Service** you will deploy later: + + ```bash + az provider register --namespace Microsoft.Web + ``` + +1. In a browser window, go back to your **eShopOnWeb** GitHub repository. +1. On the repository page, go to **Settings**, click on **Secrets and variables > Actions**. Click on **New repository secret** + + - Name : **`AZURE_CREDENTIALS`** + - Secret: **paste the previously copied JSON object** (GitHub is able to keep multiple secrets under same name, used by [azure/login](https://github.com/Azure/login) action ) + +1. Click on **Add secret**. Now GitHub Actions will be able to reference the service principal, using the repository secret. + +### Modify and execute the GitHub workflow + +In this task, you will modify the given GitHub workflow and execute it to deploy the solution in your own subscription. + +1. In a browser window, go back to your **eShopOnWeb** GitHub repository. +1. On the repository page, go to **Code** and open the following file: **eShopOnWeb/.github/workflows/eshoponweb-cicd.yml**. This workflow defines the CI/CD process for the given .NET 8 website code. +1. Uncomment the **on** section (delete "#"). The workflow triggers with every push to the main branch and also offers manual triggering ("workflow_dispatch"). +1. In the **env** section, make the following changes: + - Replace **NAME** in **RESOURCE-GROUP** variable. It should be the same resource group created in previous steps. + - (Optional) You can choose your closest [azure region](https://azure.microsoft.com/explore/global-infrastructure/geographies) for **LOCATION**. For example, "eastus", "eastasia", "westus", etc. + - Replace **YOUR-SUBS-ID** in **SUBSCRIPTION-ID**. + - Replace **NAME** in **WEBAPP-NAME** with some unique alias. It will be used to create a globally unique website using Azure App Service. +1. Read the workflow carefully, comments are provided to help understand. + +1. Click on **Commit changes...** on top right and **Commit changes** leaving defaults (changing the main branch). The workflow will get automatically executed. + +### Review GitHub Workflow execution + +In this task, you will review the GitHub workflow execution: + +1. In a browser window, go back to your **eShopOnWeb** GitHub repository. +1. On the repository page, go to **Actions**, you will see the workflow setup before executing. Click on it. + + ![Screenshot of the GitHub workflow in progress.](media/gh-actions.png) + +1. Wait for the workflow to finish. From the **Summary** you can see the two workflow jobs, the status and Artifacts retained from the execution. You can click in each job to review logs. + + ![Screenshot of the successful workflow.](media/gh-action-success.png) + +1. In a browser window, go back to the Azure Portal at `https://portal.azure.com`. Open the resource group created before. You will see that the GitHub Action, using a bicep template, has created an Azure App Service Plan + App Service. You can see the published website opening the App Service and clicking **Browse**. + + ![Screenshot of the browse WebApp.](media/browse-webapp.png) + +### (OPTIONAL) Add manual approval pre-deploy using GitHub Environments + +In this task, you will use GitHub environments to ask for manual approval before executing the actions defined on the deploy job of your workflow. + +1. On the repository page, go to **Code** and open the following file: **eShopOnWeb/.github/workflows/eshoponweb-cicd.yml**. +1. In the **deploy** job section, you can find a reference to an **environment** called **Development**. GitHub used [environments](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment) add protection rules (and secrets) for your targets. + +1. On the repository page, go to **Settings**, open **Environments** and click **New environment**. +1. Give it **`Development`** name and click on **Configure Environment**. + + > **Note**: If an environment called **Development** already exists in the **Environments** list, open its configuration by clicking on the environment name. + +1. In the **Configure Development** tab, check the option **Required Reviewers** and your GitHub account as a reviewer. Click on **Save protection rules**. +1. Now lets test the protection rule. On the repository page, go to **Actions**, click on **eShopOnWeb Build and Test** workflow and click on **Run workflow > Run workflow** to execute manually. + + ![Screenshot of the manual trigger workflow.](media/gh-manual-run.png) + +1. Click on the started execution of the workflow and wait for **buildandtest** job to finish. You will see a review request when **deploy** job is reached. + +1. Click on **Review deployments**, check **Development** and click on **Approve and deploy**. + + ![Screenshot of the Actions approval.](media/gh-approve.png) + +1. Workflow will follow the **deploy** job execution and finish. + +## Clean up resources + +When you complete the lab, it's important to clean up your Azure resources to avoid unnecessary charges: + +### Delete the Azure resources + +1. In the Azure Portal at `https://portal.azure.com`, navigate to the **Resource groups** section. +1. Find and select the **rg-eshoponweb-NAME** resource group you created. +1. On the resource group page, click **Delete resource group**. +1. Type the resource group name to confirm deletion and click **Delete**. +1. Wait for the deletion process to complete. + +### Clean up GitHub resources (optional) + +If you want to clean up your GitHub repository: + +1. Navigate to your **eShopOnWeb** repository on GitHub. +1. Go to **Settings** and scroll down to the **Danger Zone**. +1. Click **Delete this repository** and follow the prompts to confirm deletion. + +> **CAUTION:** Deleting the repository will permanently remove all code, issues, pull requests, and other repository data. + +> **IMPORTANT**: Remember to delete the Azure resources to avoid unnecessary charges. The GitHub repository can remain as part of your portfolio. diff --git a/Instructions/intermediate/07-deploy-docker-containers-azure-app-service.md b/Instructions/intermediate/07-deploy-docker-containers-azure-app-service.md new file mode 100644 index 0000000..5694eb0 --- /dev/null +++ b/Instructions/intermediate/07-deploy-docker-containers-azure-app-service.md @@ -0,0 +1,197 @@ +--- +lab: + topic: Intermediate + title: "Deploy Docker containers to Azure App Service web apps" + description: "Learn how to use an Azure DevOps CI/CD pipeline to build a custom Docker image, push it to Azure Container Registry, and deploy it as a container to Azure App Service." +--- + +# Deploy Docker containers to Azure App Service web apps + +In this lab, you'll learn how to use an Azure DevOps CI/CD pipeline to build a custom Docker image, push it to Azure Container Registry, and deploy it as a container to Azure App Service. You'll work with containerization, Azure Container Registry, and automated deployments. + +You will learn how to: + +- Build a custom Docker image using a Microsoft hosted Linux agent. +- Push an image to Azure Container Registry. +- Deploy a Docker image as a container to Azure App Service using Azure DevOps. +- Configure CI/CD pipelines for containerized applications. +- Test containerized web applications in Azure. + +This lab takes approximately **20** minutes to complete. + +## Before you start + +To complete the lab, you need: + +- **Microsoft Edge** or an [Azure DevOps supported browser.](https://learn.microsoft.com/azure/devops/server/compatibility) +- An Azure DevOps organization. If you don't already have one, create one by following the instructions at [Create an organization or project collection](https://learn.microsoft.com/azure/devops/organizations/accounts/create-organization). +- **An Azure subscription**: If you don't already have an Azure subscription, sign up for a free account at [Azure Free Account](https://azure.microsoft.com/free). +- Verify that you have a Microsoft account or a Microsoft Entra account with the **Contributor** or the **Owner** role in the Azure subscription. For details, refer to [List Azure role assignments using the Azure portal](https://learn.microsoft.com/azure/role-based-access-control/role-assignments-list-portal) and [View and assign administrator roles in Azure Active Directory](https://learn.microsoft.com/azure/active-directory/roles/manage-roles-portal). + +### Set up Azure DevOps organization (if needed) + +If you don't already have an Azure DevOps organization, follow these steps: + +1. Use a private browser session to get a new **personal Microsoft Account (MSA)** at `https://account.microsoft.com` (skip if you already have one). +1. Using the same browser session, sign up for a free Azure subscription at `https://azure.microsoft.com/free` (skip if you already have one). +1. Open a browser and navigate to Azure portal at `https://portal.azure.com`, then search at the top of the Azure portal screen for **Azure DevOps**. In the resulting page, click **Azure DevOps organizations**. +1. Next, click on the link labelled **My Azure DevOps Organizations** or navigate directly to `https://aex.dev.azure.com`. +1. On the **We need a few more details** page, select **Continue**. +1. In the drop-down box on the left, choose **Default Directory**, instead of **Microsoft Account**. +1. If prompted (_"We need a few more details"_), provide your name, e-mail address, and location and click **Continue**. +1. Back at `https://aex.dev.azure.com` with **Default Directory** selected click the blue button **Create new organization**. +1. Accept the _Terms of Service_ by clicking **Continue**. +1. If prompted (_"Almost done"_), leave the name for the Azure DevOps organization at default (it needs to be a globally unique name) and pick a hosting location close to you from the list. +1. Once the newly created organization opens in **Azure DevOps**, select **Organization settings** in the bottom left corner. +1. At the **Organization settings** screen select **Billing** (opening this screen takes a few seconds). +1. Select **Setup billing** and on the right-hand side of the screen, select your **Azure Subscription** and then select **Save** to link the subscription with the organization. +1. Once the screen shows the linked Azure Subscription ID at the top, change the number of **Paid parallel jobs** for **MS Hosted CI/CD** from 0 to **1**. Then select **SAVE** button at the bottom. + + > **Note**: You may **wait a couple of minutes before using the CI/CD capabilities** so that the new settings are reflected in the backend. Otherwise, you will still see the message _"No hosted parallelism has been purchased or granted"_. + +1. In **Organization Settings**, go to section **Pipelines** and click **Settings**. +1. Toggle the switch to **Off** for **Disable creation of classic build pipelines** and **Disable creation of classic release pipelines**. +1. In **Organization Settings**, go to section **Security** and click **Policies**. +1. Toggle the switch to **On** for **Allow public projects**. + +### Create and configure the Azure DevOps project (if needed) + +1. Open your browser and navigate to your Azure DevOps organization. +1. Select the **New Project** option and use the following settings: + - name: **eShopOnWeb** + - visibility: **Private** + - Advanced: Version Control: **Git** + - Advanced: Work Item Process: **Scrum** +1. Select **Create**. + +### Import eShopOnWeb git repository (if needed) + +1. Open the previously created **eShopOnWeb** project. +1. Select the **Repos > Files**, **Import a Repository** and then select **Import**. +1. On the **Import a Git Repository** window, paste the following URL `https://github.com/MicrosoftLearning/eShopOnWeb.git` and select **Import**: + +1. The repository is organized the following way: + + - **.ado** folder contains Azure DevOps YAML pipelines. + - **.devcontainer** folder container setup to develop using containers (either locally in VS Code or GitHub Codespaces). + - **infra** folder contains Bicep & ARM infrastructure as code templates used in some lab scenarios. + - **.github** folder contains YAML GitHub workflow definitions. + - **src** folder contains the .NET 8 website used in the lab scenarios. + +1. Leave the web browser window open. +1. Go to **Repos > Branches**. +1. Hover on the **main** branch then click the ellipsis on the right of the column. +1. Click on **Set as default branch**. + +## Import and run the CI pipeline + +In this section, you will configure the service connection with your Azure Subscription then import and run the CI pipeline. + +### Import and run the CI pipeline + +1. Go to **Pipelines > Pipelines** +1. Click on **New pipeline** button (or **Create Pipeline** if you don't have other pipelines previously created) +1. Select **Azure Repos Git (YAML)** +1. Select the **eShopOnWeb** repository +1. Select **Existing Azure Pipelines YAML file** +1. Select the **main** branch and the **/.ado/eshoponweb-ci-docker.yml** file, then click on **Continue** +1. In the YAML pipeline definition, customize: + + - **YOUR-SUBSCRIPTION-ID** with your Azure subscription ID. + - Replace the **resourceGroup** with the resource group name you want to use, for example, **AZ400-RG1**. + +1. Review the pipeline definition. The CI definition consists of the following tasks: + + - **Resources**: It downloads the repository files that will be used in the following tasks. + - **AzureResourceManagerTemplateDeployment**: Deploys the Azure Container Registry using bicep template. + - **PowerShell**: Retrieve the **ACR Login Server** value from the previous task's output and create a new parameter **acrLoginServer** + - [**Docker**](https://learn.microsoft.com/azure/devops/pipelines/tasks/reference/docker-v0?view=azure-pipelines) **- Build**: Build the Docker image and create two tags (Latest and current BuildID) + - **Docker - Push**: Push the images to Azure Container Registry + +1. Click on **Save and Run**. + +1. Open the pipeline execution. If you see a warning message "This pipeline needs permission to access a resource before this run can continue to Build", click on **View** and then **Permit** and **Permit** again. This will allow the pipeline to access the Azure subscription. + + > **Note**: The deployment may take a few minutes to complete. + +1. Your pipeline will take a name based on the project name. Let's **rename** it for identifying the pipeline better. Go to **Pipelines > Pipelines** and click on the recently created pipeline. Click on the ellipsis and **Rename/move** option. Name it **eshoponweb-ci-docker** and click on **Save**. + +1. Navigate to the [**Azure Portal**](https://portal.azure.com), search for the Azure Container Registry in the recently created Resource Group (it should be named **AZ400-RG1**). On the left-hand side click **Repositories** under **Services** and make sure that the repository **eshoponweb/web** was created. When you click the repository link, you should see two tags (one of them is **latest**), these are the pushed images. If you don't see this, check the status of your pipeline. + +## Import and run the CD pipeline + +In this section, you will configure the service connection with your Azure Subscription then import and run the CD pipeline. + +### Import and run the CD pipeline + +In this task, you will import and run the CD pipeline. + +1. Go to **Pipelines > Pipelines** +1. Click on **New pipeline** button +1. Select **Azure Repos Git (YAML)** +1. Select the **eShopOnWeb** repository +1. Select **Existing Azure Pipelines YAML File** +1. Select the **main** branch and the **/.ado/eshoponweb-cd-webapp-docker.yml** file, then click on **Continue** +1. In the YAML pipeline definition, customize: + + - **YOUR-SUBSCRIPTION-ID** with your Azure subscription ID. + - Replace the **resourceGroup** with the resource group name used during the service connection creation, for example, **AZ400-RG1**. + - Replace **location** with the Azure region where the resources will be deployed. + +1. Review the pipeline definition. The CD definition consists of the following tasks: + + - **Resources**: It downloads the repository files that will be used in the following tasks. + - **AzureResourceManagerTemplateDeployment**: Deploys the Azure App Service using bicep template. + - **AzureResourceManagerTemplateDeployment**: Add role assignment using Bicep + +1. Click on **Save and Run**. + +1. Open the pipeline execution. If you see a warning message "This pipeline needs permission to access a resource before this run can continue to Deploy", click on **View** and then **Permit** and **Permit** again. This will allow the pipeline to access the Azure subscription. + + > **Important**: If you do not authorize the pipeline when configuring, you will encounter permission errors during execution. Common error messages include "This pipeline needs permission to access a resource" or "Pipeline run failed due to insufficient permissions". To resolve this, navigate to the pipeline run, click **View** next to the permission request, then click **Permit** to grant the necessary access to your Azure subscription and resources. + + > **Note**: The deployment may take a few minutes to complete. + + > [!IMPORTANT] + > If you receive the error message "TF402455: Pushes to this branch are not permitted; you must use a pull request to update this branch.", you need to uncheck the "Require a minimum number of reviewers" Branch protection rule enabled in the previous labs. + +1. Your pipeline will take a name based on the project name. Let's **rename** it for identifying the pipeline better. Go to **Pipelines > Pipelines** and hover on the recently created pipeline. Click on the ellipsis and **Rename/move** option. Name it **eshoponweb-cd-webapp-docker** and click on **Save**. + + > **Note 1**: The use of the **/infra/webapp-docker.bicep** template creates an app service plan, a web app with system assigned managed identity enabled, and references the Docker image pushed previously: **${acr.properties.loginServer}/eshoponweb/web:latest**. + + > **Note 2**: The use of the **/infra/webapp-to-acr-roleassignment.bicep** template creates a new role assignment for the web app with AcrPull role to be able to retrieve the Docker image. This could be done in the first template, but since the role assignment can take some time to propagate, it's a good idea to do both tasks separately. + +### Test the solution + +1. In the Azure Portal, navigate to the recently created Resource Group, you should now see three resources (App Service, App Service Plan and Container Registry). + +1. Navigate to the App Service, then click **Browse**, this will take you to the website. + +1. Verify that the eShopOnWeb application is running successfully. Once confirmed, you have completed the lab successfully. + +## Clean up resources + +When you complete the lab, it's important to clean up your Azure resources to avoid unnecessary charges: + +### Delete the Azure resources + +1. In the Azure Portal at `https://portal.azure.com`, navigate to the **Resource groups** section. +1. Find and select the **AZ400-RG1** resource group (or the name you used). +1. On the resource group page, click **Delete resource group**. +1. Type the resource group name to confirm deletion and click **Delete**. +1. Wait for the deletion process to complete. + +### Clean up Azure DevOps resources + +You don't need to clean up your Azure DevOps organization or project, as they will remain available for you to use as a reference and portfolio item. Azure DevOps provides free tier usage that includes basic features for small teams. + +If you want to delete the project, you can do so by following these steps: + +1. In your browser navigate to the Azure DevOps portal at `https://aex.dev.azure.com`. +1. Navigate to the **eShopOnWeb** project you created. +1. On the project settings page, go to **Overview** and click **Delete** at the bottom of the page. +1. Type the project name to confirm deletion and click **Delete**. + +> **CAUTION:** Deleting a project deletes all work items, repositories, builds, and other project artifacts. If you used an existing project for this exercise, any existing resources outside the scope of this exercise will also be deleted. + +> **IMPORTANT**: Remember to delete the Azure resources to avoid unnecessary charges. The Azure DevOps project can remain as part of your portfolio. diff --git a/Instructions/intermediate/08-set-up-run-functional-tests.md b/Instructions/intermediate/08-set-up-run-functional-tests.md new file mode 100644 index 0000000..a79bccb --- /dev/null +++ b/Instructions/intermediate/08-set-up-run-functional-tests.md @@ -0,0 +1,154 @@ +--- +lab: + topic: Intermediate + title: "Set up and run functional tests" + description: "Learn how to configure a CI pipeline for a .NET application that includes unit tests, integration tests, and functional tests." +--- + +# Set up and run functional tests + +**Estimated time:** 20 minutes + +You will learn how to configure a CI pipeline for a .NET application that includes unit tests, integration tests, and functional tests. You'll understand the different types of automated testing and how they fit into a continuous integration strategy. + +## Before you start + +You need: + +- **Microsoft Edge** or an [Azure DevOps supported browser](https://docs.microsoft.com/azure/devops/server/compatibility) +- **Azure DevOps organization:** Create one at [Create an organization or project collection](https://docs.microsoft.com/azure/devops/organizations/accounts/create-organization) if you don't have one + +## About software testing + +Software of any complexity can fail in unexpected ways in response to changes. Testing after making changes is required for all but the most trivial applications. Manual testing is the slowest, least reliable, most expensive way to test software. + +There are many kinds of automated tests for software applications: + +- **Unit Tests:** Test a single part of your application's logic. They don't test how your code works with dependencies or infrastructure +- **Integration Tests:** Test how your code works with dependencies or infrastructure. They verify that your code's layers interact as expected when dependencies are fully resolved +- **Functional Tests:** Written from the user's perspective, they verify the correctness of the system based on its requirements + +For more information about testing types, see [Test ASP.NET Core MVC apps](https://learn.microsoft.com/dotnet/architecture/modern-web-apps-azure/test-asp-net-core-mvc-apps). + +## Create and configure the team project + +First, you'll create an Azure DevOps project for this lab. + +1. In your browser, open your Azure DevOps organization +2. Click **New Project** +3. Give your project the name **eShopOnWeb** +4. Leave other fields with defaults +5. Click **Create** + +## Import the eShopOnWeb Git Repository + +Next, you'll import the sample repository that contains the application code and test projects. + +1. In your Azure DevOps organization, open the **eShopOnWeb** project +2. Click **Repos > Files** +3. Click **Import a Repository** +4. Select **Import** +5. In the **Import a Git Repository** window, paste this URL: `https://github.com/MicrosoftLearning/eShopOnWeb.git` +6. Click **Import** + +The repository is organized this way: + +- **.ado** folder contains Azure DevOps YAML pipelines +- **.devcontainer** folder contains setup to develop using containers +- **infra** folder contains Bicep & ARM infrastructure as code templates +- **.github** folder contains YAML GitHub workflow definitions +- **src** folder contains the .NET website used in the lab scenarios +- **tests** folder contains the different test projects (Unit, Integration, Functional) + +7. Go to **Repos > Branches** +8. Hover on the **main** branch then click the ellipsis on the right +9. Click **Set as default branch** + +## Setup Tests in CI pipeline + +You'll configure a CI pipeline that includes different types of tests. + +### Import the YAML build definition for CI + +You'll add the YAML build definition that implements Continuous Integration. + +1. Go to **Pipelines > Pipelines** +2. Click **New Pipeline** (or **Create Pipeline** if you don't have any pipelines) +3. Select **Azure Repos Git (YAML)** +4. Select the **eShopOnWeb** repository +5. Select **Existing Azure Pipelines YAML File** +6. Select the **main** branch and the **/.ado/eshoponweb-ci.yml** file +7. Click **Continue** + +The CI definition consists of these tasks: + +- **DotNet Restore:** Installs all project dependencies without storing them in source control +- **DotNet Build:** Builds a project and all its dependencies +- **DotNet Test:** .Net test driver used to execute unit tests +- **DotNet Publish:** Publishes the application and its dependencies to a folder for deployment +- **Publish Artifact - Website:** Publishes the app artifact and makes it available as a pipeline artifact +- **Publish Artifact - Bicep:** Publishes the infrastructure artifact and makes it available as a pipeline artifact + +8. Click the **Save** button (not **Save and run**) to save the pipeline definition + +You can find the **Save** button by clicking on the arrow to the right of the **Save and Run** button. + +### Add tests to the CI pipeline + +You'll add integration and functional tests to the CI Pipeline. Notice that Unit Tests are already part of the pipeline. + +1. Edit the pipeline by pressing the **Edit** button +2. Add the Integration Tests task after the Unit Tests task: + + ```yaml + - task: DotNetCoreCLI@2 + displayName: Integration Tests + inputs: + command: "test" + projects: "tests/IntegrationTests/*.csproj" + ``` + + **Integration Tests** test how your code works with dependencies or infrastructure. Although it's good to encapsulate code that interacts with infrastructure like databases and file systems, you'll still have some of that code, and you'll want to test it. Additionally, you should verify that your code's layers interact as expected when your application's dependencies are fully resolved. + +3. Add the Functional tests task after the Integration Tests task: + + ```yaml + - task: DotNetCoreCLI@2 + displayName: Functional Tests + inputs: + command: "test" + projects: "tests/FunctionalTests/*.csproj" + ``` + + **Functional Tests** are written from the user's perspective and verify the correctness of the system based on its requirements. Unlike integration tests that are written from the developer's perspective to verify that components work correctly together, functional tests validate the system's behavior from an end-user standpoint. + +4. Click **Validate and Save** +5. If validation is successful, click **Save** again to commit the changes directly to the main branch + +### Check the tests summary + +Now you'll run the pipeline and examine the test results. + +1. Click **Run** +2. From the **Run pipeline** tab, click **Run** again +3. Wait for the pipeline to start and complete the Build Stage successfully +4. Once completed, the **Tests** tab will appear as part of the pipeline run +5. Click on it to check the summary + + ![Screenshot of the tests summary](media/AZ400_M05_L09_Tests_Summary.png) + +6. For more details, at the bottom of the page, the table shows a list of the different run tests + + ![Screenshot of the tests table](media/AZ400_M05_L09_Tests_Table.png) + +> **Note**: If the table is empty, you need to reset the filters to see all the details about the tests run. + +## Summary + +In this lab, you learned how to set up and run different test types using Azure Pipelines and .NET. You configured a CI pipeline that includes: + +- **Unit Tests:** Testing individual components in isolation +- **Integration Tests:** Testing how components work together with dependencies +- **Functional Tests:** Testing the system from the user's perspective + +These different testing layers provide comprehensive coverage and help ensure code quality throughout your development lifecycle. Automated testing in CI/CD pipelines helps catch issues early and maintains confidence in your deployments. diff --git a/Instructions/intermediate/09-package-management-azure-artifacts.md b/Instructions/intermediate/09-package-management-azure-artifacts.md new file mode 100644 index 0000000..8d5c5cf --- /dev/null +++ b/Instructions/intermediate/09-package-management-azure-artifacts.md @@ -0,0 +1,271 @@ +--- +lab: + topic: Intermediate + title: "Package Management with Azure Artifacts" + description: "Learn how to work with Azure Artifacts for package management, including creating and connecting to feeds, and publishing NuGet packages." +--- + +# Package Management with Azure Artifacts + +**Estimated time:** 35 minutes + +You will learn how to work with Azure Artifacts for package management, including creating and connecting to a feed, creating and publishing a NuGet package, importing a NuGet package, and updating a NuGet package. Azure Artifacts facilitates discovery, installation, and publishing of NuGet, npm, and Maven packages in Azure DevOps. + +## Before you start + +You need: + +- **Microsoft Edge** or an [Azure DevOps supported browser](https://docs.microsoft.com/azure/devops/server/compatibility) +- **Azure DevOps organization:** Create one if you don't have one +- **Visual Studio 2022 Community Edition** available from [Visual Studio Downloads page](https://visualstudio.microsoft.com/downloads/). Installation should include: + - **ASP.NET and web development** workload + - **Azure development** workload + - **.NET Core cross-platform development** workload +- **.NET Core SDK:** [Download and install the .NET Core SDK (2.1.400+)](https://go.microsoft.com/fwlink/?linkid=2103972) +- **Azure Artifacts credential provider:** [Download and install the credential provider](https://go.microsoft.com/fwlink/?linkid=2099625) + +## About Azure Artifacts + +Azure Artifacts facilitate discovery, installation, and publishing NuGet, npm, and Maven packages in Azure DevOps. It's deeply integrated with other Azure DevOps features such as Build, making package management a seamless part of your existing workflows. + +Key benefits: + +- **Centralized package management** across your organization +- **Integration with CI/CD pipelines** for automatic package publishing +- **Support for multiple package types** (NuGet, npm, Maven, Python, Universal packages) +- **Upstream sources** to proxy and cache packages from public repositories +- **Security and permissions** to control access to your packages + +## Create and configure the team project + +First, you'll create an Azure DevOps project for this lab. + +1. In your browser, open your Azure DevOps organization +2. Click **New Project** +3. Give your project the name **eShopOnWeb** +4. Leave other fields with defaults +5. Click **Create** + +## Import the eShopOnWeb Git Repository + +Next, you'll import the sample repository that contains the application code. + +1. In your Azure DevOps organization, open the **eShopOnWeb** project +2. Click **Repos > Files** +3. Click **Import a Repository** +4. Select **Import** +5. In the **Import a Git Repository** window, paste this URL: `https://github.com/MicrosoftLearning/eShopOnWeb.git` +6. Click **Import** + +The repository is organized this way: + +- **.ado** folder contains Azure DevOps YAML pipelines +- **src** folder contains the .NET 8 website used in the lab scenarios + +7. Go to **Repos > Branches** +8. Hover on the **main** branch then click the ellipsis on the right +9. Click **Set as default branch** + +## Configure the eShopOnWeb solution in Visual Studio + +You'll configure Visual Studio to prepare for the lab. + +1. Ensure that you are viewing the **eShopOnWeb** team project on the Azure DevOps portal + +> **Note**: You can access the project page directly by navigating to the https://dev.azure.com/``/eShopOnWeb URL, where the `` placeholder represents your Azure DevOps Organization name. + +2. In the vertical menu on the left side of the **eShopOnWeb** pane, click **Repos** +3. On the **Files** pane, click **Clone** +4. Select the drop-down arrow next to **Clone in VS Code**, and in the dropdown menu, select **Visual Studio** +5. If prompted whether to proceed, click **Open** +6. If prompted, sign in with the user account you used to set up your Azure DevOps organization +7. Within the Visual Studio interface, in the **Azure DevOps** pop-up window, accept the default local path (C:\eShopOnWeb) and click **Clone** +8. This will automatically import the project into Visual Studio +9. Leave Visual Studio window open for use in your lab + +## Working with Azure Artifacts + +You'll learn how to work with Azure Artifacts using the following steps: + +- Create and connect to a feed +- Create and publish a NuGet package +- Import a NuGet package +- Update a NuGet package + +### Create and connect to a feed + +A feed is a collection of packages. You'll create a feed to store your NuGet packages. + +1. In the web browser window displaying your Azure DevOps project, in the vertical navigational pane, select the **Artifacts** icon +2. With the **Artifacts** hub displayed, click **+ Create Feed** at the top of the pane + +> **Note**: This feed will be a collection of NuGet packages available to users within the organization and will sit alongside the public NuGet feed as a peer. + +3. On the **Create new feed** pane, in the **Name** textbox, type **eShopOnWebShared** +4. In the **Visibility** section, select **People in my organization** +5. In the **Upstream sources** section, select **Include packages from common public sources** +6. Click **Create** + +> **Note**: Any user who wants to connect to this NuGet feed must configure their environment. + +7. Back on the **Artifacts** hub, click **Connect to Feed** +8. On the **Connect to feed** pane, in the **NuGet** section, select **Visual Studio** and copy the **Source** URL +9. Switch back to the **Visual Studio** window +10. In the Visual Studio window, click **Tools** menu header and, in the dropdown menu, select **NuGet Package Manager** and, in the cascading menu, select **Package Manager Settings** +11. In the **Options** dialog box, click **Package Sources** and click the plus sign to add a new package source +12. At the bottom of the dialog box, in the **Name** textbox, replace **Package source** with **eShopOnWebShared** +13. In the **Source** textbox, paste the URL you copied from Azure DevOps +14. Click **Update** and then **OK** + +> **Note**: Visual Studio is now connected to the new feed. + +### Create and publish a NuGet package + +You'll create a custom NuGet package and publish it to the feed. + +1. In the Visual Studio window you used to configure the new package source, in the main menu, click **File**, in the dropdown menu, click **New** and then, in the cascading menu, click **Project** +2. On the **Create a new project** pane of the **New Project** dialog box, in the list of project templates, locate the **Class Library** template, select the **Class Library (.NET Standard)** template, and click **Next** +3. On the **Configure your new project** pane of the **New Project** dialog box, specify the following settings and click **Create**: + + - Project name: **eShopOnWeb.Shared** + - Location: accept the default value + - Solution: **Create new solution** + - Solution name: **eShopOnWeb.Shared** + +4. Click **Create** +5. In the Visual Studio interface, in the **Solution Explorer** pane, right-click **Class1.cs**, in the right-click menu, select **Delete**, and, when prompted for confirmation, click **OK** +6. Press **Ctrl+Shift+B** or **right-click on the eShopOnWeb.Shared project** and select **Build** to build the project + +> **Note**: Next you'll use MSBuild to generate a NuGet package directly from the project. This approach is common for shared libraries to include all the metadata and dependencies in the package. + +7. Switch to the Azure DevOps web portal and navigate to the **Artifacts** section +8. Click on the **eShopOnWebShared** feed +9. Click **Connect to Feed** and select **NuGet.exe** under the **NuGet** section +10. Copy the **Project setup** commands for later use + +### Publish the package using dotnet CLI + +You'll use the .NET CLI to pack and publish your package. + +1. In Visual Studio, right-click on the **eShopOnWeb.Shared** project and select **Open Folder in File Explorer** +2. In the File Explorer window, note the path to the project folder +3. Open a **Command Prompt** or **PowerShell** window as Administrator +4. Navigate to the project folder using the `cd` command +5. Run the following command to create a NuGet package: + + ``` + dotnet pack --configuration Release + ``` + +6. This will create a `.nupkg` file in the `bin\Release` folder +7. Navigate to the `bin\Release` folder: + + ``` + cd bin\Release + ``` + +8. You need to publish the package to your Azure Artifacts feed. First, add the package source: + + ``` + dotnet nuget add source "YOUR_FEED_URL" --name "eShopOnWebShared" --username "YOUR_USERNAME" --password "YOUR_PAT" + ``` + +> **Note**: Replace YOUR_FEED_URL with the feed URL from Azure DevOps, YOUR_USERNAME with your Azure DevOps username, and YOUR_PAT with a Personal Access Token with package read/write permissions. + +9. Publish the package: + + ``` + dotnet nuget push *.nupkg --source "eShopOnWebShared" + ``` + +10. Switch back to the Azure DevOps web portal displaying the **Artifacts** tab +11. Click **Refresh** +12. In the list of packages, click the **eShopOnWeb.Shared** package +13. On the **eShopOnWeb.Shared** pane, review its metadata + +### Import a NuGet package + +You'll now import the package you created into another project. + +1. Switch back to Visual Studio +2. In the **Solution Explorer**, right-click on the **eShopOnWeb.Shared** solution and select **Add > New Project** +3. Select **Console App (.NET Core)** template and click **Next** +4. Configure the project: + - Project name: **eShopOnWeb.Shared.Client** + - Location: accept the default value + - Solution: **Add to solution** +5. Click **Create** +6. In the **Solution Explorer**, right-click on the **eShopOnWeb.Shared.Client** project and select **Manage NuGet Packages** +7. In the **NuGet Package Manager** pane, click the **Browse** tab +8. In the **Package source** dropdown, select **eShopOnWebShared** +9. In the search box, type **eShopOnWeb.Shared** and press Enter +10. Select the **eShopOnWeb.Shared** package and click **Install** +11. Accept any license agreements if prompted +12. The package is now installed and can be used in your project + +### Update a NuGet package + +You'll update the package by modifying the original project and publishing a new version. + +1. In the **Solution Explorer**, right-click on the **eShopOnWeb.Shared** project and select **Add > Class** +2. Name the class **ProductHelper** and click **Add** +3. Add some sample code to the class: + + ```csharp + using System; + + namespace eShopOnWeb.Shared + { + public class ProductHelper + { + public static string GetProductDisplayName(string name, decimal price) + { + return $"{name} - ${price:F2}"; + } + } + } + ``` + +4. Save the file +5. Right-click on the **eShopOnWeb.Shared** project and select **Properties** +6. On the **Package** tab, increment the **Package version** from 1.0.0 to 1.1.0 +7. Save and close the Properties window +8. Build the project by pressing **Ctrl+Shift+B** +9. Open Command Prompt or PowerShell as Administrator again +10. Navigate to the **eShopOnWeb.Shared** project folder +11. Pack the new version: + + ``` + dotnet pack --configuration Release + ``` + +12. Navigate to the `bin\Release` folder and publish the new version: + + ``` + dotnet nuget push *.nupkg --source "eShopOnWebShared" + ``` + +13. Switch back to the Azure DevOps web portal +14. Refresh the **eShopOnWeb.Shared** package page +15. You should now see version 1.1.0 available + +### Update the package in the client project + +1. Switch back to Visual Studio +2. In the **Solution Explorer**, right-click on the **eShopOnWeb.Shared.Client** project and select **Manage NuGet Packages** +3. Click the **Updates** tab +4. You should see that **eShopOnWeb.Shared** has an update available +5. Select the package and click **Update** +6. Accept any license agreements if prompted +7. The package is now updated to the latest version + +## Summary + +In this lab, you learned how to work with Azure Artifacts by: + +- **Creating and connecting to a feed** for centralized package management +- **Creating and publishing a NuGet package** using the .NET CLI +- **Importing a NuGet package** into another project +- **Updating a NuGet package** with new versions and functionality + +Azure Artifacts provides a powerful platform for managing packages across your organization, integrating seamlessly with your CI/CD pipelines and development workflows. It supports multiple package formats and provides upstream source capabilities to proxy external package repositories. diff --git a/Instructions/intermediate/media/AZ400_M05_L09_Tests_Summary.png b/Instructions/intermediate/media/AZ400_M05_L09_Tests_Summary.png new file mode 100644 index 0000000..e9e46aa Binary files /dev/null and b/Instructions/intermediate/media/AZ400_M05_L09_Tests_Summary.png differ diff --git a/Instructions/intermediate/media/AZ400_M05_L09_Tests_Table.png b/Instructions/intermediate/media/AZ400_M05_L09_Tests_Table.png new file mode 100644 index 0000000..28bf5b5 Binary files /dev/null and b/Instructions/intermediate/media/AZ400_M05_L09_Tests_Table.png differ diff --git a/Instructions/intermediate/media/agent-configuration.png b/Instructions/intermediate/media/agent-configuration.png new file mode 100644 index 0000000..594b48d Binary files /dev/null and b/Instructions/intermediate/media/agent-configuration.png differ diff --git a/Instructions/intermediate/media/agent-status.png b/Instructions/intermediate/media/agent-status.png new file mode 100644 index 0000000..ba9d2ca Binary files /dev/null and b/Instructions/intermediate/media/agent-status.png differ diff --git a/Instructions/intermediate/media/browse-webapp.png b/Instructions/intermediate/media/browse-webapp.png new file mode 100644 index 0000000..3b3a093 Binary files /dev/null and b/Instructions/intermediate/media/browse-webapp.png differ diff --git a/Instructions/intermediate/media/create-new-agent-pool-self-hosted-agent.png b/Instructions/intermediate/media/create-new-agent-pool-self-hosted-agent.png new file mode 100644 index 0000000..e0e5271 Binary files /dev/null and b/Instructions/intermediate/media/create-new-agent-pool-self-hosted-agent.png differ diff --git a/Instructions/intermediate/media/create-virtual-machine-preset.png b/Instructions/intermediate/media/create-virtual-machine-preset.png new file mode 100644 index 0000000..98708e8 Binary files /dev/null and b/Instructions/intermediate/media/create-virtual-machine-preset.png differ diff --git a/Instructions/intermediate/media/enable-actions.png b/Instructions/intermediate/media/enable-actions.png new file mode 100644 index 0000000..35766d8 Binary files /dev/null and b/Instructions/intermediate/media/enable-actions.png differ diff --git a/Instructions/intermediate/media/eshoponweb-ci-pr-agent-pool.png b/Instructions/intermediate/media/eshoponweb-ci-pr-agent-pool.png new file mode 100644 index 0000000..06da845 Binary files /dev/null and b/Instructions/intermediate/media/eshoponweb-ci-pr-agent-pool.png differ diff --git a/Instructions/intermediate/media/gh-action-success.png b/Instructions/intermediate/media/gh-action-success.png new file mode 100644 index 0000000..041e40d Binary files /dev/null and b/Instructions/intermediate/media/gh-action-success.png differ diff --git a/Instructions/intermediate/media/gh-actions.png b/Instructions/intermediate/media/gh-actions.png new file mode 100644 index 0000000..fe306d6 Binary files /dev/null and b/Instructions/intermediate/media/gh-actions.png differ diff --git a/Instructions/intermediate/media/gh-approve.png b/Instructions/intermediate/media/gh-approve.png new file mode 100644 index 0000000..1ed01d7 Binary files /dev/null and b/Instructions/intermediate/media/gh-approve.png differ diff --git a/Instructions/intermediate/media/gh-manual-run.png b/Instructions/intermediate/media/gh-manual-run.png new file mode 100644 index 0000000..0398d3d Binary files /dev/null and b/Instructions/intermediate/media/gh-manual-run.png differ diff --git a/Instructions/intermediate/media/github-new.png b/Instructions/intermediate/media/github-new.png new file mode 100644 index 0000000..eb81a18 Binary files /dev/null and b/Instructions/intermediate/media/github-new.png differ diff --git a/Instructions/intermediate/media/personal-access-token-configuration.png b/Instructions/intermediate/media/personal-access-token-configuration.png new file mode 100644 index 0000000..dce2a5f Binary files /dev/null and b/Instructions/intermediate/media/personal-access-token-configuration.png differ diff --git a/Instructions/intermediate/media/personal-access-token-menu.png b/Instructions/intermediate/media/personal-access-token-menu.png new file mode 100644 index 0000000..f37e9f4 Binary files /dev/null and b/Instructions/intermediate/media/personal-access-token-menu.png differ diff --git a/index.md b/index.md index f50902b..4dc7359 100644 --- a/index.md +++ b/index.md @@ -16,14 +16,15 @@ Some exercises may have additional, or different, requirements. Those will conta ## Topic levels {% assign exercises = site.pages | where_exp:"page", "page.url contains '/Instructions'" %} +{% assign exercises = exercises | where_exp:"page", "page.lab.topic != null" %} {% assign grouped_exercises = exercises | group_by: "lab.topic" %} -{% assign topic_order = "Basic,Intermediate,Advanced,Expert" | split: "," %} +{% assign topic_order = "Basic,Intermediate,Advanced" | split: "," %} {% assign sorted_groups = "" | split: "" %} {% for topic in topic_order %} - {% assign matching_group = grouped_exercises | where: "name", topic | first %} - {% if matching_group %} - {% assign sorted_groups = sorted_groups | push: matching_group %} - {% endif %} +{% assign matching_group = grouped_exercises | where: "name", topic | first %} +{% if matching_group %} +{% assign sorted_groups = sorted_groups | push: matching_group %} +{% endif %} {% endfor %}