How to Deploy a React app to Azure using DevOps Pipelines

React has quickly become one of the most popular JavaScript front end frameworks to build production grade applications. Whether it’s using ReactJS for a web app or React Native for a mobile app, this versatile framework is well suited to deliver large scale applications quickly and effectively. In this post, we will cover how to set up and host a ReactJS application from scratch. We will be setting up a new app and configuring DevOps pipelines for continuous integration and delivery to an Azure App Service.

What you will need

Creating a Repo

We will first create a new Git repository in DevOps. Navigate to your DevOps site, and select the project you would like to work with. If you don’t have a repository setup already, select the Repos tab and click “New Repository” and create a Git project. Next, select the “Clone” button to clone the project to a local directory:

screenshot of DevOps showing select the “Clone” button to clone the project to a local directory

If you have VS Code installed, you can then select Clone in VS Code:

Screenshot showing user selecting Clone in VS Code

This will open up VS Code and prompt you to map the project to a new location and create an empty Git directory we will use for this project. After this is mapped, we will create a new React project.

Creating a React Project

To create the project, we will use a command line interface (CLI). Open a command prompt or terminal window, and navigate to the directory created in the previous step. Once there, enter the following command:

npx create-react-app react-azure-demo

Replace “react-azure-demo” with whatever name you want for this project. After setup is finished, type the following to start your project:

cd react-azure-demo

npm start

This will open up the default browser, and if successful, you will see the following:

Screenshot of a default browser window.

Our First Commit

Now that we know our app works, let’s make our first commit to DevOps and make sure everything is connected. To do this, open up the app in VS Code and click the Source Control tab. Since this project is a fresh setup, you should see all the React project files as newly added items. Add a commit message like “initial commit”, click the checkmark to commit these changes, and push the changes to the repo.

screenshot of user clicking the checkmark to commit changes, and push the changes to the repo

Once successful, you should be able to see the new React project files in the repo on DevOps.

Screenshot of new React project files in the repo on DevOps

 

Setting up the web.config for Production

In most web applications you will be using navigation components to go between various pages on your site using client-side URLs. This means having pages like https://root.com/newPage. To do this in React, you could use something like React Router to manage links and URL routing within the app. However, React by nature is a single-page application and navigating to a URL that is not the root will result in a 404 error. To prevent this, we will need to add a web.config file that redirects all URLs back to index.html. The best way to accomplish this is by adding it as part of the source for our project, and to have it packaged as part of the React deployment process.

Open up the project in VS Code and create a new folder in the root of the project called “webconfig”. Inside, create two new files called “copyWebConfig.js” and “web.config”. The project should now look like this:

Screenshot of WebConfig.js” and “web.config”

In copyWebConfig.js, add the following code:

const fs = require("fs-extra");
 
const webConfigPath = "./build/web.config";
 
if (fs.existsSync(webConfigPath)) {
    fs.unlinkSync(webConfigPath);
}
 
fs.copySync("./webconfig/web.config", webConfigPath);

In web.config, we want all URLs that the user may type to point back to the root. To do this, add the following to our web.config file:

<?xml version="1.0"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="React Routes" stopProcessing="true">
                    <match url=".*" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                        <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
                    </conditions>
                <action type="Rewrite" url="/" />
                </rule>
            </rules>
        </rewrite>
        <staticContent>
            <mimeMap fileExtension=".json" mimeType="application/json" />
        </staticContent>
    </system.webServer>
</configuration>

The final step is to update the package.json file with an additional script. Append the “scripts” section with the following:

“postbuild”: “node webconfig/copyWebConfig.js”

This will make it so anytime that “npm build” is run, the web.config file will be copied over as part of the process. This will be happening when we create our first Azure Pipeline a little later. Be sure to commit and push these changes to DevOps just like we did before.

Creating the App Service on Azure

Now it’s time to setup the app service to that we will be deploying our app to. Go to the Azure Portal, and click the App Services icon. Next, select “Add” to create a new app service. Take note of the Resource Group that you use (or create a new one if needed) as you will be using this later. Also, you will need to choose and note down a unique the Name as well. Select “Code” under the Publish setting, and choose a Runtime stack and region of your choosing, then select “Review + Create”.

Screenshot from the Azure Portal showing Runtime stack and region

After it’s finished, you can click on the newly created app service to see the details.

A newly created app service

If you click on the URL, you will be able to see it live:

Now that the app service is up and running, let’s build our service connection to DevOps and create a pipeline to deploy the React project.

Setting up a Service Connection

To connect to the App Service from DevOps, we will need to first setup a service connection. Start by opening up DevOps, and select “Project Settings”.

Project Settings in DevOps

In the Pipelines section, select “Service Connections” and hit the “Create service connection” button.

How to create a Service Connection screenshot

We will select “Azure Resource Manager” as the type, and hit next.

Selecting Azure Resource Manager

Keep “Service Principle (automatic)” as the Authentication method.

Selecting Service Principle (automatic)” as an Authentication method

Keep “Subscription” as the scope and make sure the correct one is selected. Then make sure to choose the same Resource Group that you created for the App Service above. We will also need to create and note a “Service Connection Name” that you will be using in the pipeline next. After you are finished, hit “Save” to create your service connection.

Saving a service connection

Building an Azure Pipeline for Deployment

In order to have continuous integration and continuous deployment (CI/CD) through DevOps, we will setup a new pipeline. This will make it so that whenever code is checked-in to the repo, our React project will automatically build and deploy the latest code to our App Service. To get started, head back over to DevOps and select the “Pipelines” tab:

Selection Pipelines in DevOps

Click “Create Pipeline” to get started and select Azure Repos Git:

Screenshot of creating "Pipeline” to get started and selecting Azure Repos Git

Select the Repo that we made for this project.

Select the Repo made for this project.

And select “Node.js with React” to generate a new YAML file:

Select “Node.js with React” to generate a new YAML file:

Next, edit the file with the following:

# Node.js with React
# Build a Node.js project that uses React.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
 
trigger:
- master
 
pool:
  vmImage: 'windows-latest'

# Set variables
variables:
  directory: react-azure-demo
  stage: development
  serviceConnection: REACT_AZURE_DEMO
  appServiceName: reactazuredemo
 
steps:
- task: NodeTool@0
  inputs:
    versionSpec: '10.x'
  displayName: 'Install Node.js'
 
- script: 
    npm install
  displayName: 'npm install'
  workingDirectory: $(directory)
 
- script: 
    set "REACT_APP_STAGE=$(stage)" && npm run build
  displayName: 'npm build'
  workingDirectory: $(directory)
 
- task: CopyFiles@2
  displayName: 'Copy files'
  inputs:
    sourceFolder: '$(directory)/build' 
    Contents: '**/*'
    TargetFolder: '$(Build.ArtifactStagingDirectory)'
    cleanTargetFolder: true
 
- task: ArchiveFiles@2
  displayName: 'Archive files'
  inputs:
    rootFolderOrFile: '$(Build.ArtifactStagingDirectory)'
    includeRootFolder: false
    archiveType: zip
    archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
    replaceExistingArchive: true
 
- task: PublishBuildArtifacts@1
  displayName: 'Publish Build Artifacts'
  inputs: 
    pathtoPublish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
 
- task: AzureWebApp@1
  displayName: 'Deploy to App Service'
  inputs:
    azureSubscription: '$(serviceConnection)'
    appName: '$(appServiceName)'
    appType: 'webApp'
    package: '$(System.ArtifactsDirectory)/$(Build.BuildId).zip'

You will need to make sure the variables match up with your project settings. They include:

  • “directory”: the name of the folder in the repo where your React project is stored. If it is the root, you can delete this variable and all instances of it within the code above.
  • “stage”: this will add a custom environment variable that you can use in your React project code by accessing the variable “REACT_APP_STAGE”. If you have multiple environments like dev/test/prod, you can setup multiple pipelines setting this variable accordingly. This is useful to set different configuration settings or API URLs in your project based on the environment.
  • “serviceConnection”: This is the service connection name we created in the previous step.
  • “appServiceName”: This is the name of the app service we created earlier.

The “trigger” is currently set to “master” but can be updated to your project needs. What this means is that every time source code is pushed to the master branch, this pipeline will trigger automatically and begin the build, package, and deployment process to your app service. You will also be notified of the result should any step fail.

After this is updated, click “Save and run”, keep “Commit directly to the master branch”, and click “Save”. After this, you should see your pipeline begin running and complete in a few minutes.

Click “Save and run”, keep “Commit directly to the master branch”, and click “Save”.

You should now be able to see the project by navigating to the App Service URL that we used before.

Browser page

Success!

Conclusion

We have walked through start to finish on creating a React project, housing it in a Git repo through DevOps, setting up a custom web.config that will be part of the React build process, creating a new Azure app service to host the app, setting up a service connection from DevOps to Azure, and building a pipeline to automatically build and deploy our project every time code is pushed by our development team. You have now built the foundation for your new web app that will change the world.

Happy hacking!

Archives

Follow Us

1 thought on “How to Deploy a React app to Azure using DevOps Pipelines”

  1. Avatar

    Thank you for your thorough tutorial! Nothing I had come across had the YAML file correct but you did and I thank you!

Leave a Comment

Your email address will not be published. Required fields are marked *