Migrating an Springboot Java API: Part 4 - Azure Release

laura-lucciola

Laura

Posted on November 12, 2023

Migrating an Springboot Java API: Part 4 - Azure Release

On the previous post I used AWS Elastic Beanstalk to pull and run 2 dockerized Java APIs in a fully fledged application.
In this guide I will show how I used Azure DevOps release to continously deploy new versions of the APIs into AWS.

Pre-requisites

IAM role

In order to allow my Azure releases to deploy new AWS Elastic Beanstalk versions, I created IAM role with the following permission policies:

AdministratorAccess-AWSElasticBeanstalk
AmazonEC2ContainerRegistryFullAccess
EC2InstanceProfileForImageBuilderECRContainerBuilds
AmazonSSMFullAccess
AmazonEC2FullAccess
AWSElasticBeanstalkMulticontainerDocker
AWSElasticBeanstalkRoleECS
Enter fullscreen mode Exit fullscreen mode

Changing the pipelines to include the deployment information

In the previous post I created a deployment folder in my solution repository, so now I changed the existing azure-pipelines.yml to publish the deployment into a different artifact.

trigger:
  - main

resources:
  - repo: self

stages:
  - stage: ReplaceVariables
    displayName: Replace variables
    jobs:
      - job: ReplaceAzureVariables
        displayName: Replace Azure variables
        pool:
          vmImage: ubuntu-latest
        steps:
        - task: replacetokens@5
          displayName: Replace Tokens
          inputs:
            targetFiles: |
              **/application.yml
              **/application.properties
              deployment/**

            encoding: 'auto'
            tokenPattern: 'doublebraces'
            writeBOM: true
            actionOnMissing: 'warn'
            keepToken: false
            actionOnNoFiles: 'continue'
            enableTransforms: false
            enableRecursion: false
            useLegacyPattern: false
            enableTelemetry: false

        - task: CopyFiles@2
          displayName: Copy deployment files
          inputs:
            Contents: 'deployment/**'
            TargetFolder: '$(Build.ArtifactStagingDirectory)'

        - publish: $(Build.ArtifactStagingDirectory)/deployment
          displayName: Publish deployment files
          artifact: deployment

        - task: CopyFiles@2
          displayName: Copy all files 
          inputs:
            Contents: '**'
            TargetFolder: '$(Build.ArtifactStagingDirectory)'

        - publish: $(Build.ArtifactStagingDirectory)
          displayName: Publish all files
          artifact: drop

  - stage: CustomerDockerBuildPublish
    displayName: Customer API
    dependsOn: ReplaceVariables
    jobs:
      - job: Build_and_Push
        displayName: Customer - Build & Push Docker image
        pool:
          vmImage: ubuntu-latest
        steps:
          # Skip source code checkout and reuse sources
          - checkout: none
          # Download the artifact from the ReplaceVariables stage
          - task: DownloadPipelineArtifact@2
            inputs:
              buildType: 'current'
              artifact: drop
              targetPath: '$(Build.SourcesDirectory)'

          - task: Docker@2
            displayName: Customer - Build Customer API Docker image
            inputs:
              command: build
              dockerfile: './Customer.Dockerfile'
              buildContext: '$(Build.SourcesDirectory)'
              repository: $(CUSTOMER_DOCKER_REPOSITORY_NAME)
              tags: |
                $(Build.BuildNumber)

          - task: ECRPushImage@1
            displayName: Customer - Push Admin API Docker image
            inputs:
              awsCredentials: 'my-azure-service-connection'
              regionName: '$(AWS_REGION)'
              imageSource: 'imagename'
              sourceImageName: '$(CUSTOMER_DOCKER_REPOSITORY_NAME)'
              sourceImageTag: '$(Build.BuildNumber)'
              pushTag: '$(Build.BuildNumber)'
              repositoryName: '$(CUSTOMER_DOCKER_REPOSITORY_NAME)'
              logRequest: true
              logResponse: true

  - stage: AdminDockerBuildPublish
    displayName: Admin API
    dependsOn: ReplaceVariables
    jobs:
      - job: Build_and_Push
        displayName: Admin - Build & Push Docker image
        pool:
          vmImage: ubuntu-latest
        steps:
          # Skip source code checkout and reuse sources
          - checkout: none
          # Download the artifact from the ReplaceVariables stage
          - task: DownloadPipelineArtifact@2
            inputs:
              buildType: 'current'
              artifact: drop
              targetPath: '$(Build.SourcesDirectory)'

          - task: Docker@2
            displayName: Admin - Build Admin API Docker image
            inputs:
              command: build
              dockerfile: './Admin.Dockerfile'
              buildContext: '$(Build.SourcesDirectory)'
              repository: $(ADMIN_DOCKER_REPOSITORY_NAME)
              tags: |
                $(Build.BuildNumber)

          - task: ECRPushImage@1
            displayName: Admin - Push Admin API Docker image
            inputs:
              awsCredentials: 'my-azure-service-connection'
              regionName: '$(AWS_REGION)'
              imageSource: 'imagename'
              sourceImageName: '$(ADMIN_DOCKER_REPOSITORY_NAME)'
              sourceImageTag: '$(Build.BuildNumber)'
              pushTag: '$(Build.BuildNumber)'
              repositoryName: '$(ADMIN_DOCKER_REPOSITORY_NAME)'
              logRequest: true
              logResponse: true

Enter fullscreen mode Exit fullscreen mode

Change the release trigger

Then I changed the release to be triggered when new builds from main are successful.

Image description

Add Azure DevOps Release

Since we want the image size to be linked to the BuildNumber I have changed the release name configuration to be:

Release Name

steps:
- task: UsePythonVersion@0
  displayName: 'Use Python 3.7'
  inputs:
    versionSpec: 3.7

- script: 'pip install --upgrade pip'
  workingDirectory: '$(System.DefaultWorkingDirectory)/_MyJavaSolutionAPI/deployment'
  displayName: 'Command Line Script - Install pip'

- script: 'pip install awsebcli --upgrade --user'
  workingDirectory: '$(System.DefaultWorkingDirectory)/_MyJavaSolutionAPI/deployment'
  displayName: 'Command Line Script- Install awsebcli '


- task: AmazonWebServices.aws-vsts-tools.AWSShellScript.AWSShellScript@1
  displayName: 'AWS Shell Script - Elastic Beanstalk init'
  inputs:
    awsCredentials: 'my-aws-azure-service-connection'
    regionName: '$(AWS_REGION)'
    scriptType: inline
    inlineScript: 'eb init $(AWS_BEANSTALK_ENVIRONMENT_NAME) --platform "$(AWS_PLATFORM)" --tags $(AWS_TAGS) --keyname $(AWS_BEANSTALK_KEYNAME_NAME) --region $(AWS_REGION)'
    disableAutoCwd: true
    workingDirectory: '$(System.DefaultWorkingDirectory)/_MyJavaSolutionAPI/deployment'


- task: AmazonWebServices.aws-vsts-tools.AWSShellScript.AWSShellScript@1
  displayName: 'AWS Shell Script - Elastic Beanstalk deploy'
  inputs:
    awsCredentials: 'my-aws-azure-service-connection'
    regionName: '$(AWS_REGION)'
    scriptType: inline
    inlineScript: 'eb deploy $(AWS_BEANSTALK_ENVIRONMENT_NAME) --label "$(Release.ReleaseName)" --message "$(Release.ReleaseDescription)" --process --region $(AWS_REGION)'
    disableAutoCwd: true
    workingDirectory: '$(System.DefaultWorkingDirectory)/_MyJavaSolutionAPI/deployment'
Enter fullscreen mode Exit fullscreen mode

Release steps

And with that we have a completed CI/CD pipeline in Azure DevOps
This concludes my post series "Migrating an Springboot Java API".
Creating a dockerized version of a Java 1.8 Springboot solution containing 2 APIs, then creating a CI/CD pipeline in Azure DevOps and using AWS Elastic Beanstalk to host and orchestrate the running server.

💖 💪 🙅 🚩
laura-lucciola
Laura

Posted on November 12, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related