Sunday, August 22, 2021

Create Azure DevOps Pipeline for React App deployment to Azure app service

 Disclaimer: All opinions in this post are mine and not my employer (Microsoft). If you know of a more correct or performant way to accomplish work discussed in this post, please let me know at email javascript-developer@outlook.com. Many projects I work on are in progress by the time I work on them. I can't change previous design or architecture choices, but just solve a specific technical issue. 

Secrets stored in Azure Key Vault

All secrets for the React app are stored in Azure Key Vault. These secrets need to be pulled from Key Vault and set into the environment so that the `npm build` script uses those values. In order for the Azure DevOps Pipeline to connect to Azure Key Vault, you need to complete some work before you develop your Pipeline:

  • Create a Key Vault and store your React build secrets, such as an Azure Functions key, used to authenticate and use the Function. Your secret doesn't have to have the same name as your React build variable. Please don't try. Key vault secrets don't allow underscores in names anyway. Just give the variable a human readable name. The pipeline can map between the secret name and the build environment name.
  • Create a connection from Pipelines to Key Vault - In Azure DevOps, in the project settings, add a service connection to Azure Key Vault. This process creates a service principal. You can find all your service principals in Azure Portal, under the Azure Active Directory section. Service Principals are part of Azure App Registrations. 
  • Set up Azure Key Vault access policies - In the Azure portal, find your Key Vault, and add the Pipeline's Service Principal for `list` and `get`. 

Create an Azure DevOps Pipeline to build React app

The React app is deployed to an Azure app service wrapped in a .NET Core application. The vmImage is `windows-latest`. 

The YAML file is:


# ASP.NET Core (.NET Framework)


# Build and test ASP.NET Core projects targeting the full .NET Framework.
# Add steps that publish symbols, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core

trigger:
main

pool:
  vmImage'windows-latest'

# Variables used in this pipeline
# The `dinatrue` variables is a test used in the echo command to understand the syntax for container:
# bringing in variables.
variables:
  solution'**/*.sln'
  buildPlatform'Any CPU'
  buildConfiguration'Release'
  dinatrue'hello dina'
  buildDate$[format('{0:yyyy}{0:MM}{0:dd}', pipeline.startTime)]


steps:

# Get React app secrets from Key Vault
# Key Vault is used for projects beyond this single client app. If the Key Vault
# were only for this client app, change the `SecretsFilter` value to `*`.
taskAzureKeyVault@2
  inputs:
    azureSubscription'SQL Projects (98c...)'
    KeyVaultName'MSTwitterKeyVault'
    SecretsFilter'PublicApiMessageBeforeLoginKey,PublicApiGetUtcNowKey,PublicApiUrl,ServerUri'
    RunAsPreJobfalse

# Verify/debug variables
taskCmdLine@2
  inputs:
    script'echo %dinatrue% %buildDate%'

# Set Key vault secrets to React app environment variables
# Verify/debug values with SET command
# secrets' values will be `***` on purpose
taskCmdLine@2
  inputs:
    script'SET > env.log && cat env.log'
  env:
    REACT_APP_APP_SERVER_BASE_URL_PUBLIC_API_APP_MESSAGE_BEFORE_LOGIN_KEY$(PublicApiMessageBeforeLoginKey)
    REACT_APP_APP_SERVER_BASE_URL_PUBLIC_API_GET_UTC_NOW_KEY$(PublicApiGetUtcNowKey)
    REACT_APP_APP_SERVER_BASE_URL_PUBLIC_API$(PublicApiUrl)
    REACT_APP_SERVER_URL$(ServerUri)
    REACT_APP_CACHE_BUST$(buildDate)

# Get source code from repo
taskNuGetCommand@2
  inputs:
    restoreSolution'$(solution)'

# Build project, which ultimately builds React app into `$(build.artifactStagingDirectory)\WebApp.zip`
taskVSBuild@1
  inputs:
    solution'$(solution)'
    msbuildArgs'/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactStagingDirectory)\WebApp.zip" /p:DeployIisAppPath="Default Web Site"'
    platform'$(buildPlatform)'
    configuration'$(buildConfiguration)'

# Deploy Zip file to Azure app service, slot named `client-stage` using webDeploy
taskAzureRmWebAppDeployment@4
  inputs:
    ConnectionType'AzureRM'
    azureSubscription'SQL Projects (98c...)'
    appType'webApp'
    WebAppName'MSTwitterApp'
    deployToSlotOrASEtrue
    ResourceGroupName'MSTwitterBot'
    SlotName'client-stage'
    packageForLinux'$(build.artifactStagingDirectory)\WebApp.zip'
    enableCustomDeploymenttrue
    DeploymentType'webDeploy'


Friday, August 6, 2021

Moving database from Azure SQL to localdb

I recently moved an Azure SQL database back to a local development database and ran into a few issues. I took these notes so that they might help the next person that hits the problem.

In my local SSMS, I use the Import/Export wizard with the datasource using SQL Server native client 11.0. This moves the tables and data. Any destination tables will not have IDENTITY as the source tables did. 

Solution # 1 

Move away from INT Identity to use GUIDS. This requires work in the database and client code but is the better choice if you need to move data out of the source datatabase then back into the source database. 

Solution #2

More immediate fix to get past the block that your inserts don't autoincrement. 

Steps:

The following steps are completed in SSMS in the destination (local) database and should have all the data but not the IDENTITY column.

  • Rename the mytable to mytable2.
  • Generate CREATE, INSERT, and SELECT scripts for the table.
  • Modify the CREATE script to use the table name (change mytable2 to mytable) and change the PK row to include the identity requirement.

CREATE TABLE [dbo].[mytable](
[Id] [int] IDENTITY (1,1) NOT NULL,
[Text] [nvarchar](40) NULL,
) ON [PRIMARY]
GO

  • Run the creation script.
  • Create second script from the INSERT/SELECT scripts

SET IDENTITY_INSERT mytable ON
INSERT INTO [dbo].[mytable]
           ([Id]
           ,[Text])
SELECT [Id]
      ,[Text]
  FROM [dbo].[mytable2]     
SET IDENTITY_INSERT mytable OFF