Cvičení – počáteční nastavení účtu úložiště a databáze

Dokončeno

Aktualizovali jste své potrubí, aby sestavovalo a nasazovalo aplikaci vaší webové stránky do aplikace Azure App Service definované v souboru Bicep. Fáze orientačního testu se selhává, protože databáze ještě nefunguje. V této lekci nasadíte nový logický server a databázi Azure SQL a nakonfigurujete kanál pro sestavení a nasazení schématu databáze. Také aktualizujete svůj procesní řetězec, aby se přidala ukázková data produktů pro testovací prostředí, aby váš tým mohl web vyzkoušet.

V procesu provedete následující úlohy:

  • Přidejte kontejner objektů blob do účtu úložiště Azure.
  • Přidejte logický server a databázi Azure SQL.
  • Aktualizujte fázi sestavení tak, aby se projekt databáze sestavil do souboru DACPAC.
  • Přidejte do skupiny proměnných nové proměnné pro logický server a databázi Azure SQL.
  • Aktualizujte fáze nasazení tak, aby jako hodnoty parametrů používaly nové proměnné.
  • Přidejte nové kroky sestavení pro nasazení souboru DACPAC.
  • Spusťte potrubí a zobrazte web.

Přidejte kontejner úložiště

Váš soubor Bicep už definuje účet úložiště, ale nedefinuje kontejner objektů blob. Tady přidáte kontejner objektů blob do souboru Bicep. Do aplikace také zadáte název účtu úložiště a kontejneru objektů blob pomocí jeho konfiguračního nastavení. Aplikace tak ví, ke kterému účtu úložiště se má přistupovat.

  1. Ve Visual Studio Code otevřete soubor main.bicep ve složce deploy.

  2. Pod proměnné definující názvy prostředků (u řádku 27) přidejte novou definici proměnné pro název kontejneru úložiště objektů blob:

    var storageAccountImagesBlobContainerName = 'toyimages'
    
  3. Aktualizujte prostředek storageAccount pro definování blob kontejneru:

    resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
      name: storageAccountName
      location: location
      kind: 'StorageV2'
      sku: environmentConfigurationMap[environmentType].storageAccount.sku
    
      resource blobService 'blobServices' = {
        name: 'default'
    
        resource storageAccountImagesBlobContainer 'containers' = {
          name: storageAccountImagesBlobContainerName
    
          properties: {
            publicAccess: 'Blob'
          }
        }
      }
    }
    
  4. Aktualizujte vlastnost appSettings aplikace a přidejte tři nová nastavení aplikace pro název účtu úložiště, koncový bod blobu a název kontejneru blobu:

    resource appServiceApp 'Microsoft.Web/sites@2022-03-01' = {
      name: appServiceAppName
      location: location
      properties: {
        serverFarmId: appServicePlan.id
        httpsOnly: true
        siteConfig: {
          appSettings: [
            {
              name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
              value: applicationInsights.properties.InstrumentationKey
            }
            {
              name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
              value: applicationInsights.properties.ConnectionString
            }
            {
              name: 'ReviewApiUrl'
              value: reviewApiUrl
            }
            {
              name: 'ReviewApiKey'
              value: reviewApiKey
            }
            {
              name: 'StorageAccountName'
              value: storageAccount.name
            }
            {
              name: 'StorageAccountBlobEndpoint'
              value: storageAccount.properties.primaryEndpoints.blob
            }
            {
              name: 'StorageAccountImagesContainerName'
              value: storageAccount::blobService::storageAccountImagesBlobContainer.name
            }
          ]
        }
      }
    }
    
  5. Na konec souboru přidejte nové výstupy, abyste zpřístupnili názvy účtu úložiště a kontejneru objektů blob:

    output storageAccountName string = storageAccount.name
    output storageAccountImagesBlobContainerName string = storageAccount::blobService::storageAccountImagesBlobContainer.name
    
  6. Uložte změny souboru.

  7. Potvrďte změny do úložiště Git, ale zatím je nenasdílejte. V terminálu editoru Visual Studio Code spusťte následující příkazy:

    git add .
    git commit -m "Add storage container"
    

Přidání logického serveru a databáze Azure SQL

Váš soubor Bicep momentálně nenasazuje logický server nebo databázi Azure SQL. V této části přidáte tyto prostředky do souboru Bicep.

  1. Do souboru main.bicep přidejte dva nové parametry pod parametr reviewApiKey:

    @description('The administrator login username for the SQL server.')
    param sqlServerAdministratorLogin string
    
    @secure()
    @description('The administrator login password for the SQL server.')
    param sqlServerAdministratorLoginPassword string
    
  2. Pod proměnné, které definují názvy prostředků, přidejte nové proměnné, které definují názvy logického serveru a databáze Azure SQL:

    var sqlServerName = 'toy-website-${resourceNameSuffix}'
    var sqlDatabaseName = 'Toys'
    
  3. Pod právě přidanou proměnnou definujte novou proměnnou, která vytvoří připojovací řetězec pro aplikaci pro přístup k databázi:

    // Define the connection string to access Azure SQL.
    var sqlDatabaseConnectionString = 'Server=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Initial Catalog=${sqlDatabase.name};Persist Security Info=False;User ID=${sqlServerAdministratorLogin};Password=${sqlServerAdministratorLoginPassword};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;'
    

    Poznámka

    Pro zjednodušení aplikace používá pro přístup k databázi přihlašovací jméno a heslo správce. To ale není vhodný postup pro produkční řešení. Pro přístup k databázi je lepší použít spravovanou identitu služby App Service a udělit spravované identitě minimální oprávnění potřebná aplikací. Odkazujeme na další informace na stránce Souhrn.

  4. Na konec souboru nad výstupy přidejte logický server a databázové prostředky Azure SQL:

    resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {
      name: sqlServerName
      location: location
      properties: {
        administratorLogin: sqlServerAdministratorLogin
        administratorLoginPassword: sqlServerAdministratorLoginPassword
      }
    }
    
    resource sqlServerFirewallRule 'Microsoft.Sql/servers/firewallRules@2022-05-01-preview' = {
      parent: sqlServer
      name: 'AllowAllWindowsAzureIps'
      properties: {
        endIpAddress: '0.0.0.0'
        startIpAddress: '0.0.0.0'
      }
    }
    
    resource sqlDatabase 'Microsoft.Sql/servers/databases@2022-05-01-preview' = {
      parent: sqlServer
      name: sqlDatabaseName
      location: location
      sku: environmentConfigurationMap[environmentType].sqlDatabase.sku
    }
    
  5. Aktualizujte proměnnou environmentConfigurationMap tak, aby definovala hodnoty sku, které se mají použít pro vaši databázi pro každé prostředí:

    var environmentConfigurationMap = {
      Production: {
        appServicePlan: {
          sku: {
            name: 'S1'
            capacity: 1
          }
        }
        storageAccount: {
          sku: {
            name: 'Standard_LRS'
          }
        }
        sqlDatabase: {
          sku: {
            name: 'Standard'
            tier: 'Standard'
          }
        }
      }
      Test: {
        appServicePlan: {
          sku: {
            name: 'F1'
          }
        }
        storageAccount: {
          sku: {
            name: 'Standard_GRS'
          }
        }
        sqlDatabase: {
          sku: {
            name: 'Standard'
            tier: 'Standard'
          }
        }
      }
    }
    
  6. Do vaší aplikace App Service přidejte další nastavení pro připojovací řetězec k databázi.

    resource appServiceApp 'Microsoft.Web/sites@2022-03-01' = {
      name: appServiceAppName
      location: location
      properties: {
        serverFarmId: appServicePlan.id
        httpsOnly: true
        siteConfig: {
          appSettings: [
            {
              name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
              value: applicationInsights.properties.InstrumentationKey
            }
            {
              name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
              value: applicationInsights.properties.ConnectionString
            }
            {
              name: 'ReviewApiUrl'
              value: reviewApiUrl
            }
            {
              name: 'ReviewApiKey'
              value: reviewApiKey
            }
            {
              name: 'StorageAccountName'
              value: storageAccount.name
            }
            {
              name: 'StorageAccountBlobEndpoint'
              value: storageAccount.properties.primaryEndpoints.blob
            }
            {
              name: 'StorageAccountImagesContainerName'
              value: storageAccount::blobService::storageAccountImagesBlobContainer.name
            }
            {
              name: 'SqlDatabaseConnectionString'
              value: sqlDatabaseConnectionString
            }
          ]
        }
      }
    }
    
  7. V dolní části souboru přidejte výstupy pro zveřejnění názvu hostitele logického serveru Azure SQL a názvu databáze:

    output appServiceAppName string = appServiceApp.name
    output appServiceAppHostName string = appServiceApp.properties.defaultHostName
    output storageAccountName string = storageAccount.name
    output storageAccountImagesBlobContainerName string = storageAccount::blobService::storageAccountImagesBlobContainer.name
    output sqlServerFullyQualifiedDomainName string = sqlServer.properties.fullyQualifiedDomainName
    output sqlDatabaseName string = sqlDatabase.name
    
  8. Uložte změny souboru.

Přidání nových kroků sestavení pro databázový projekt

Vývojáři webu připravili databázový projekt sady Visual Studio, který nasadí a nakonfiguruje tabulku databáze webu. V této části aktualizujete fázi sestavení v kanálu, aby se projekt databáze sestavil do souboru DACPAC a publikoval ho jako artefakt kanálu.

  1. Otevřete soubor build.yml ve složce deploy/pipeline-templates.

  2. Pokud chcete sestavit projekt databáze sady Visual Studio, zkopírujte vygenerovaný soubor DACPAC do přípravné složky a publikujte ho jako artefakt v kanálu. Přidejte následující kroky:

    jobs:
    - job: Build
      displayName: Build application and database
      pool:
        vmImage: windows-latest
    
      steps:
    
      # Build, copy, and publish the website.
      - task: DotNetCoreCLI@2
        displayName: Build publishable website
        inputs:
          command: 'publish'
          publishWebProjects: true
    
      - task: CopyFiles@2
        displayName: Copy publishable website
        inputs:
          sourceFolder: '$(Build.SourcesDirectory)/src/ToyCompany/ToyCompany.Website/bin'
          contents: '**/publish.zip'
          targetFolder: '$(Build.ArtifactStagingDirectory)/website'
          flattenFolders: true
    
      - task: PublishBuildArtifacts@1
        displayName: Publish website as pipeline artifact
        inputs:
          pathToPublish: '$(Build.ArtifactStagingDirectory)/website'
          artifactName: 'website'
    
      # Build, copy, and publish the DACPAC file.
      - task: VSBuild@1
        displayName: Build Visual Studio solution
        inputs:
          solution: '$(Build.SourcesDirectory)/src/ToyCompany/ToyCompany.Database/ToyCompany.Database.sqlproj'
    
      - task: CopyFiles@2
        displayName: Copy DACPAC
        inputs:
          sourceFolder: '$(Build.SourcesDirectory)/src/ToyCompany/ToyCompany.Database/bin'
          contents: '**/*.dacpac'
          targetFolder: '$(Build.ArtifactStagingDirectory)/database'
          flattenFolders: true
    
      - task: PublishBuildArtifacts@1
        displayName: Publish DACPAC as pipeline artifact
        inputs:
          pathToPublish: '$(Build.ArtifactStagingDirectory)/database'
          artifactName: 'database'
    
  3. Uložte změny souboru.

Přidání hodnot do skupin proměnných

  1. V prohlížeči přejděte na Pipelines>Library.

  2. Vyberte skupinu proměnných ToyWebsiteProduction.

    Snímek obrazovky Azure DevOps zobrazující seznam skupin proměnných, se zvýrazněnou skupinou proměnných ToyWebsiteProduction.

  3. Do skupiny proměnných přidejte následující proměnné:

    Jméno Hodnota
    SqlServerAdministratorLogin ToyCompanyAdmin
    SqlServerAdministratorLoginPassword SecurePassword!111
  4. Vyberte ikonu visacího zámku vedle proměnné SqlServerAdministratorLoginPassword. Tato funkce říká službě Azure Pipelines, aby bezpečně zacházeli s hodnotou proměnné.

    Snímek obrazovky se skupinou produkčních proměnných se zvýrazněným tlačítkem tajné proměnné

  5. Uložte skupinu proměnných.

    Snímek obrazovky se skupinou produkčních proměnných se zvýrazněným tlačítkem Uložit

  6. Opakováním procesu přidejte následující proměnné do skupiny proměnných ToyWebsiteTest:

    Jméno Hodnota
    SqlServerAdministratorLogin TestToyCompanyAdmin
    SqlServerAdministratorLoginPassword SecurePassword!999

    Nezapomeňte vybrat ikonu visacího zámku vedle SqlServerAdministratorLoginPassword proměnnou a uložit skupinu proměnných.

Přidání hodnot parametrů do fází Ověření a Náhled

Soubor Bicep má nyní dva nové povinné parametry: sqlServerAdministratorLogin a sqlServerAdministratorLoginPassword. Tady přenesete tyto hodnoty parametrů ze skupiny proměnných pro fáze Validate i Preview.

  1. V editoru Visual Studio Code otevřete soubor deploy.yml ve složce deploy/pipeline-templates.

  2. Aktualizujte krok RunPreflightValidation ve fázi Validate přidáním nových parametrů.

    - task: AzureResourceManagerTemplateDeployment@3
      name: RunPreflightValidation
      displayName: Run preflight validation
      inputs:
        connectedServiceName: ToyWebsite${{parameters.environmentType}}
        location: ${{parameters.deploymentDefaultLocation}}
        deploymentMode: Validation
        resourceGroupName: $(ResourceGroupName)
        csmFile: deploy/main.bicep
        overrideParameters: >
          -environmentType $(EnvironmentType)
          -reviewApiUrl $(ReviewApiUrl)
          -reviewApiKey $(ReviewApiKey)
          -sqlServerAdministratorLogin $(SqlServerAdministratorLogin)
          -sqlServerAdministratorLoginPassword $(SqlServerAdministratorLoginPassword)
    
  3. Aktualizujte fázi Preview kroku RunWhatIf přidáním nových parametrů.

    inlineScript: |
      az deployment group what-if \
        --resource-group $(ResourceGroupName) \
        --template-file deploy/main.bicep \
        --parameters environmentType=$(EnvironmentType) \
                     reviewApiUrl=$(ReviewApiUrl) \
                     reviewApiKey=$(ReviewApiKey) \
                     sqlServerAdministratorLogin=$(SqlServerAdministratorLogin) \
                     sqlServerAdministratorLoginPassword=$(SqlServerAdministratorLoginPassword)
    

    Důležitý

    Nezapomeňte na konec řádku přidat znak zpětného lomítka (\), který nastaví hodnotu parametru reviewApiKey a na další řádek. Znak \ označuje, že existují další řádky, které jsou součástí stejného příkazu Azure CLI.

Přidání hodnot parametrů do fáze Nasazení

  1. Aktualizujte krok fáze Nasazení s názvem DeployBicepFile tak, že přidáte nové parametry:

    - task: AzureResourceManagerTemplateDeployment@3
      name: DeployBicepFile
      displayName: Deploy Bicep file
      inputs:
        connectedServiceName: ToyWebsite${{parameters.environmentType}}
        deploymentName: $(Build.BuildNumber)
        location: ${{parameters.deploymentDefaultLocation}}
        resourceGroupName: $(ResourceGroupName)
        csmFile: deploy/main.bicep
        overrideParameters: >
          -environmentType $(EnvironmentType)
          -reviewApiUrl $(ReviewApiUrl)
          -reviewApiKey $(ReviewApiKey)
          -sqlServerAdministratorLogin $(SqlServerAdministratorLogin)
          -sqlServerAdministratorLoginPassword $(SqlServerAdministratorLoginPassword)
        deploymentOutputs: deploymentOutputs
    
  2. Vytvořte proměnné pro pipeline, které obsahují hodnoty výstupů z Bicep, jež jste nedávno přidali pro úložiště a zdroje Azure SQL:

    - bash: |
        echo "##vso[task.setvariable variable=appServiceAppName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppName.value')"
        echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')"
        echo "##vso[task.setvariable variable=storageAccountName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.storageAccountName.value')"
        echo "##vso[task.setvariable variable=storageAccountImagesBlobContainerName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.storageAccountImagesBlobContainerName.value')"
        echo "##vso[task.setvariable variable=sqlServerFullyQualifiedDomainName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.sqlServerFullyQualifiedDomainName.value')"
        echo "##vso[task.setvariable variable=sqlDatabaseName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.sqlDatabaseName.value')"
      name: SaveDeploymentOutputs
      displayName: Save deployment outputs into variables
      env:
        DEPLOYMENT_OUTPUTS: $(deploymentOutputs)
    

Přidání kroků nasazení databáze

V této části definujete kroky potřebné k nasazení databázových komponent vašeho webu. Nejprve přidáte krok pro nasazení souboru DACPAC, který kanál vytvořil dříve. Pak do databáze a účtu úložiště přidáte ukázková data, ale jenom pro neprodukční prostředí.

  1. Pod krokem DeployWebsiteApp ve fázi Nasazení přidejte nový krok pro nasazení souboru DACPAC:

    - task: SqlAzureDacpacDeployment@1
      name: DeploySqlDatabaseDacpac
      displayName: Deploy DACPAC to database
      inputs:
        ConnectedServiceNameARM: ToyWebsite${{parameters.environmentType}}
        authenticationType: 'server'
        serverName: $(sqlServerFullyQualifiedDomainName)
        databaseName: $(sqlDatabaseName)
        sqlUsername: $(SqlServerAdministratorLogin)
        sqlPassword: $(SqlServerAdministratorLoginPassword)
        deployType: 'DacpacTask'
        deploymentAction: 'Publish'
        dacpacFile: '$(Pipeline.Workspace)/database/ToyCompany.Database.dacpac'
    
  2. Pod právě přidaným krokem definujte krok pro vytvoření databáze s ukázkovými daty.

    - ${{ if ne(parameters.environmentType, 'Production') }}:
      - task: SqlAzureDacpacDeployment@1
        name: AddTestDataToDatabase
        displayName: Add test data to database
        inputs:
          ConnectedServiceNameARM: ToyWebsite${{parameters.environmentType}}
          authenticationType: 'server'
          serverName: $(sqlServerFullyQualifiedDomainName)
          databaseName: $(sqlDatabaseName)
          sqlUsername: $(SqlServerAdministratorLogin)
          sqlPassword: $(SqlServerAdministratorLoginPassword)
          deployType: 'sqlTask'
          sqlFile: 'deploy/sample-data/Toys.sql'
    

    Všimněte si, že tento krok má nastavenou podmínku, aby se spustil pouze v neprodukčním prostředí.

  3. Pod právě přidaným krokem a stále v rozsahu podmínky přidejte krok pro nahrání některých ukázkových obrázků hraček do kontejneru objektů blob pomocí Azure CLI.

    - task: AzureCLI@2
      name: UploadSampleImages
      displayName: Upload sample images
      inputs:
        azureSubscription: ToyWebsite${{parameters.environmentType}}
        scriptType: 'bash'
        scriptLocation: 'inlineScript'
        inlineScript: |
          az storage blob upload-batch \
            --account-name $(storageAccountName) \
            --destination $(storageAccountImagesBlobContainerName) \
            --source 'deploy/sample-data/toyimages'
    

Ověření souborů a potvrzení změn

  1. Ověřte, že soubor main.bicep vypadá takto:

    @description('The location into which your Azure resources should be deployed.')
    param location string = resourceGroup().location
    
    @description('Select the type of environment you want to provision. Allowed values are Production and Test.')
    @allowed([
      'Production'
      'Test'
    ])
    param environmentType string
    
    @description('A unique suffix to add to resource names that need to be globally unique.')
    @maxLength(13)
    param resourceNameSuffix string = uniqueString(resourceGroup().id)
    
    @description('The URL to the product review API.')
    param reviewApiUrl string
    
    @secure()
    @description('The API key to use when accessing the product review API.')
    param reviewApiKey string
    
    @description('The administrator login username for the SQL server.')
    param sqlServerAdministratorLogin string
    
    @secure()
    @description('The administrator login password for the SQL server.')
    param sqlServerAdministratorLoginPassword string
    
    // Define the names for resources.
    var appServiceAppName = 'toy-website-${resourceNameSuffix}'
    var appServicePlanName = 'toy-website'
    var logAnalyticsWorkspaceName = 'workspace-${resourceNameSuffix}'
    var applicationInsightsName = 'toywebsite'
    var storageAccountName = 'mystorage${resourceNameSuffix}'
    var storageAccountImagesBlobContainerName = 'toyimages'
    var sqlServerName = 'toy-website-${resourceNameSuffix}'
    var sqlDatabaseName = 'Toys'
    
    // Define the connection string to access Azure SQL.
    var sqlDatabaseConnectionString = 'Server=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Initial Catalog=${sqlDatabase.name};Persist Security Info=False;User ID=${sqlServerAdministratorLogin};Password=${sqlServerAdministratorLoginPassword};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;'
    
    // Define the SKUs for each component based on the environment type.
    var environmentConfigurationMap = {
      Production: {
        appServicePlan: {
          sku: {
            name: 'S1'
            capacity: 1
          }
        }
        storageAccount: {
          sku: {
            name: 'Standard_LRS'
          }
        }
        sqlDatabase: {
          sku: {
            name: 'Standard'
            tier: 'Standard'
          }
        }
      }
      Test: {
        appServicePlan: {
          sku: {
            name: 'F1'
          }
        }
        storageAccount: {
          sku: {
            name: 'Standard_GRS'
          }
        }
        sqlDatabase: {
          sku: {
            name: 'Standard'
            tier: 'Standard'
          }
        }
      }
    }
    
    resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
      name: appServicePlanName
      location: location
      sku: environmentConfigurationMap[environmentType].appServicePlan.sku
    }
    
    resource appServiceApp 'Microsoft.Web/sites@2022-03-01' = {
      name: appServiceAppName
      location: location
      properties: {
        serverFarmId: appServicePlan.id
        httpsOnly: true
        siteConfig: {
          appSettings: [
            {
              name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
              value: applicationInsights.properties.InstrumentationKey
            }
            {
              name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
              value: applicationInsights.properties.ConnectionString
            }
            {
              name: 'ReviewApiUrl'
              value: reviewApiUrl
            }
            {
              name: 'ReviewApiKey'
              value: reviewApiKey
            }
            {
              name: 'StorageAccountName'
              value: storageAccount.name
            }
            {
              name: 'StorageAccountBlobEndpoint'
              value: storageAccount.properties.primaryEndpoints.blob
            }
            {
              name: 'StorageAccountImagesContainerName'
              value: storageAccount::blobService::storageAccountImagesBlobContainer.name
            }
            {
              name: 'SqlDatabaseConnectionString'
              value: sqlDatabaseConnectionString
            }
          ]
        }
      }
    }
    
    resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
      name: logAnalyticsWorkspaceName
      location: location
    }
    
    resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
      name: applicationInsightsName
      location: location
      kind: 'web'
      properties: {
        Application_Type: 'web'
        Request_Source: 'rest'
        Flow_Type: 'Bluefield'
        WorkspaceResourceId: logAnalyticsWorkspace.id
      }
    }
    
    resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
      name: storageAccountName
      location: location
      kind: 'StorageV2'
      sku: environmentConfigurationMap[environmentType].storageAccount.sku
    
      resource blobService 'blobServices' = {
        name: 'default'
    
        resource storageAccountImagesBlobContainer 'containers' = {
          name: storageAccountImagesBlobContainerName
    
          properties: {
            publicAccess: 'Blob'
          }
        }
      }
    }
    
    resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {
      name: sqlServerName
      location: location
      properties: {
        administratorLogin: sqlServerAdministratorLogin
        administratorLoginPassword: sqlServerAdministratorLoginPassword
      }
    }
    
    resource sqlServerFirewallRule 'Microsoft.Sql/servers/firewallRules@2022-05-01-preview' = {
      parent: sqlServer
      name: 'AllowAllWindowsAzureIps'
      properties: {
        endIpAddress: '0.0.0.0'
        startIpAddress: '0.0.0.0'
      }
    }
    
    resource sqlDatabase 'Microsoft.Sql/servers/databases@2022-05-01-preview' = {
      parent: sqlServer
      name: sqlDatabaseName
      location: location
      sku: environmentConfigurationMap[environmentType].sqlDatabase.sku
    }
    
    output appServiceAppName string = appServiceApp.name
    output appServiceAppHostName string = appServiceApp.properties.defaultHostName
    output storageAccountName string = storageAccount.name
    output storageAccountImagesBlobContainerName string = storageAccount::blobService::storageAccountImagesBlobContainer.name
    output sqlServerFullyQualifiedDomainName string = sqlServer.properties.fullyQualifiedDomainName
    output sqlDatabaseName string = sqlDatabase.name
    

    Pokud ne, aktualizujte ho tak, aby odpovídal obsahu souboru.

  2. Ověřte, že váš soubor deploy.yml vypadá takto:

    parameters:
    - name: environmentType
      type: string
    - name: deploymentDefaultLocation
      type: string
      default: westus3
    
    stages:
    
    - ${{ if ne(parameters.environmentType, 'Production') }}:
      - stage: Validate_${{parameters.environmentType}}
        displayName: Validate (${{parameters.environmentType}} Environment)
        jobs:
        - job: ValidateBicepCode
          displayName: Validate Bicep code
          variables:
          - group: ToyWebsite${{parameters.environmentType}}
          steps:
            - task: AzureResourceManagerTemplateDeployment@3
              name: RunPreflightValidation
              displayName: Run preflight validation
              inputs:
                connectedServiceName: ToyWebsite${{parameters.environmentType}}
                location: ${{parameters.deploymentDefaultLocation}}
                deploymentMode: Validation
                resourceGroupName: $(ResourceGroupName)
                csmFile: deploy/main.bicep
                overrideParameters: >
                  -environmentType $(EnvironmentType)
                  -reviewApiUrl $(ReviewApiUrl)
                  -reviewApiKey $(ReviewApiKey)
                  -sqlServerAdministratorLogin $(SqlServerAdministratorLogin)
                  -sqlServerAdministratorLoginPassword $(SqlServerAdministratorLoginPassword)
    
    - ${{ if eq(parameters.environmentType, 'Production') }}:
      - stage: Preview_${{parameters.environmentType}}
        displayName: Preview (${{parameters.environmentType}} Environment)
        jobs:
        - job: PreviewAzureChanges
          displayName: Preview Azure changes
          variables:
          - group: ToyWebsite${{parameters.environmentType}}
          steps:
            - task: AzureCLI@2
              name: RunWhatIf
              displayName: Run what-if
              inputs:
                azureSubscription: ToyWebsite${{parameters.environmentType}}
                scriptType: 'bash'
                scriptLocation: 'inlineScript'
                inlineScript: |
                  az deployment group what-if \
                    --resource-group $(ResourceGroupName) \
                    --template-file deploy/main.bicep \
                    --parameters environmentType=$(EnvironmentType) \
                                 reviewApiUrl=$(ReviewApiUrl) \
                                 reviewApiKey=$(ReviewApiKey) \
                                 sqlServerAdministratorLogin=$(SqlServerAdministratorLogin) \
                                 sqlServerAdministratorLoginPassword=$(SqlServerAdministratorLoginPassword)
    
    - stage: Deploy_${{parameters.environmentType}}
      displayName: Deploy (${{parameters.environmentType}} Environment)
      jobs:
      - deployment: DeployWebsite
        displayName: Deploy website
        pool:
          vmImage: windows-latest
        variables:
        - group: ToyWebsite${{parameters.environmentType}}
        environment: ${{parameters.environmentType}}
        strategy:
          runOnce:
            deploy:
              steps:
                - checkout: self
    
                - task: AzureResourceManagerTemplateDeployment@3
                  name: DeployBicepFile
                  displayName: Deploy Bicep file
                  inputs:
                    connectedServiceName: ToyWebsite${{parameters.environmentType}}
                    deploymentName: $(Build.BuildNumber)
                    location: ${{parameters.deploymentDefaultLocation}}
                    resourceGroupName: $(ResourceGroupName)
                    csmFile: deploy/main.bicep
                    overrideParameters: >
                      -environmentType $(EnvironmentType)
                      -reviewApiUrl $(ReviewApiUrl)
                      -reviewApiKey $(ReviewApiKey)
                      -sqlServerAdministratorLogin $(SqlServerAdministratorLogin)
                      -sqlServerAdministratorLoginPassword $(SqlServerAdministratorLoginPassword)
                    deploymentOutputs: deploymentOutputs
    
                - bash: |
                    echo "##vso[task.setvariable variable=appServiceAppName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppName.value')"
                    echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')"
                    echo "##vso[task.setvariable variable=storageAccountName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.storageAccountName.value')"
                    echo "##vso[task.setvariable variable=storageAccountImagesBlobContainerName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.storageAccountImagesBlobContainerName.value')"
                    echo "##vso[task.setvariable variable=sqlServerFullyQualifiedDomainName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.sqlServerFullyQualifiedDomainName.value')"
                    echo "##vso[task.setvariable variable=sqlDatabaseName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.sqlDatabaseName.value')"
                  name: SaveDeploymentOutputs
                  displayName: Save deployment outputs into variables
                  env:
                    DEPLOYMENT_OUTPUTS: $(deploymentOutputs)
    
                - task: AzureRmWebAppDeployment@4
                  name: DeployWebsiteApp
                  displayName: Deploy website
                  inputs:
                    appType: webApp
                    ConnectionType: AzureRM
                    azureSubscription: ToyWebsite${{parameters.environmentType}}
                    ResourceGroupName: $(ResourceGroupName)
                    WebAppName: $(appServiceAppName)
                    Package: '$(Pipeline.Workspace)/website/publish.zip'
    
                - task: SqlAzureDacpacDeployment@1
                  name: DeploySqlDatabaseDacpac
                  displayName: Deploy DACPAC to database
                  inputs:
                    ConnectedServiceNameARM: ToyWebsite${{parameters.environmentType}}
                    authenticationType: 'server'
                    serverName: $(sqlServerFullyQualifiedDomainName)
                    databaseName: $(sqlDatabaseName)
                    sqlUsername: $(SqlServerAdministratorLogin)
                    sqlPassword: $(SqlServerAdministratorLoginPassword)
                    deployType: 'DacpacTask'
                    deploymentAction: 'Publish'
                    dacpacFile: '$(Pipeline.Workspace)/database/ToyCompany.Database.dacpac'
    
                - ${{ if ne(parameters.environmentType, 'Production') }}:
                  - task: SqlAzureDacpacDeployment@1
                    name: AddTestDataToDatabase
                    displayName: Add test data to database
                    inputs:
                      ConnectedServiceNameARM: ToyWebsite${{parameters.environmentType}}
                      authenticationType: 'server'
                      serverName: $(sqlServerFullyQualifiedDomainName)
                      databaseName: $(sqlDatabaseName)
                      sqlUsername: $(SqlServerAdministratorLogin)
                      sqlPassword: $(SqlServerAdministratorLoginPassword)
                      deployType: 'sqlTask'
                      sqlFile: 'deploy/sample-data/Toys.sql'
    
                  - task: AzureCLI@2
                    name: UploadSampleImages
                    displayName: Upload sample images
                    inputs:
                      azureSubscription: ToyWebsite${{parameters.environmentType}}
                      scriptType: 'bash'
                      scriptLocation: 'inlineScript'
                      inlineScript: |
                        az storage blob upload-batch \
                          --account-name $(storageAccountName) \
                          --destination $(storageAccountImagesBlobContainerName) \
                          --source 'deploy/sample-data/toyimages'
    
    - stage: SmokeTest_${{parameters.environmentType}}
      displayName: Smoke Test (${{parameters.environmentType}} Environment)
      jobs:
      - job: SmokeTest
        displayName: Smoke test
        variables:
          appServiceAppHostName: $[ stageDependencies.Deploy_${{parameters.environmentType}}.DeployWebsite.outputs['DeployWebsite.SaveDeploymentOutputs.appServiceAppHostName'] ]
        steps:
          - task: PowerShell@2
            name: RunSmokeTests
            displayName: Run smoke tests
            inputs:
              targetType: inline
              script: |
                $container = New-PesterContainer `
                  -Path 'deploy/Website.Tests.ps1' `
                  -Data @{ HostName = '$(appServiceAppHostName)' }
                Invoke-Pester `
                  -Container $container `
                  -CI
    
          - task: PublishTestResults@2
            name: PublishTestResults
            displayName: Publish test results
            condition: always()
            inputs:
              testResultsFormat: NUnit
              testResultsFiles: 'testResults.xml'
    

    Pokud ne, aktualizujte ho tak, aby odpovídal obsahu souboru.

  3. Uložte změny souboru.

  4. Potvrďte a nasdílejte změny do úložiště Git. V terminálu editoru Visual Studio Code spusťte následující příkazy:

    git add .
    git commit -m "Add SQL database"
    git push
    

Spustit potrubí

  1. V prohlížeči přejděte na Pipelines.

  2. Vyberte nejnovější spuštění potrubí.

    Počkejte, až se všechny fáze testovacího prostředí úspěšně dokončí. Všimněte si, že smoke test teď také probíhá úspěšně.

    snímek obrazovky Azure DevOps znázorňující fázi Smoke Test pipeline pro testovací prostředí. Stav ukazuje, že fáze proběhla úspěšně.

  3. Počkejte, až se kanál znovu pozastaví před fází Preview (produkční prostředí), protože tentokrát potřebuje oprávnění k jiné skupině proměnných.

    snímek obrazovky Azure DevOps zobrazující spuštění kanálu pozastavené ve fázi nasazení. K pokračování se vyžaduje oprávnění. Tlačítko zobrazení je zvýrazněné.

  4. Vyberte Zobrazita pak vyberte Povolení>Povolení.

    Fáze Preview (produkční prostředí) se úspěšně dokončuje.

  5. Sledujte proces, jak dokončuje závěrečné fáze.

    Fáze Deploy (Production Environment) se úspěšně dokončí a fáze Smoke Test (Production Environment) se také úspěšně dokončí.

    Snímek obrazovky Azure DevOps zobrazující spuštění potrubí se všemi fázemi, které ukazují úspěšné dokončení

Zobrazení webu

  1. Vyberte fázi Nasadit (testovací prostředí) a otevřete protokol pipeline.

  2. Vyberte krok pro nasazení webu.

    Podržte stisknutou klávesu Ctrl ( v macOS) a vyberte adresu URL aplikace App Service a otevřete ji na nové kartě prohlížeče.

    snímek obrazovky Azure DevOps zobrazující protokol spuštění kanálu pro fázi nasazení testovacího prostředí Adresa URL aplikace App Service je zvýrazněná.

  3. Vyberte Hračky.

    Snímek obrazovky domovské stránky webu hračkářské společnosti, se zvýrazněným odkazem Hračky

    Všimněte si, že ukázková data se zobrazují v testovacím prostředí.

    snímek obrazovky se stránkou hračky testovacího webu se zobrazenými ukázkovými hračkami.

  4. Opakujte předchozí proces pro fázi aplikace v produkčním prostředí Deploy.

    Všimněte si, že v produkčním prostředí se nezobrazují žádná ukázková data.

    snímek obrazovky stránky s hračkami na produkčním webu bez zobrazení hraček

Vyčištění prostředků

Po dokončení cvičení byste měli odebrat prostředky, abyste za ně nebyli účtováni.

V terminálu editoru Visual Studio Code spusťte následující příkazy:

az group delete --resource-group ToyWebsiteTest --yes --no-wait
az group delete --resource-group ToyWebsiteProduction --yes --no-wait

Skupina prostředků se odstraní na pozadí.

Remove-AzResourceGroup -Name ToyWebsiteTest -Force
Remove-AzResourceGroup -Name ToyWebsiteProduction -Force