A client was migrating their internal portal from GCP to Azure. Their portal consisted of a Spring Boot backend and React frontend hosted in GCP, supported by GCS file storage, Postgres DB and credential management. Our client was planning a lift-and-shift migration. During our assessment, they wanted to understand opportunities to take advantage of Azure competencies to improve their SDLC, uptime, monitoring, and alerting to enhance the team’s productivity on their portal. Our first step was to make an assessment of their current environment and create a migration plan.
Our team worked alongside the client engineers during our discovery process in order to document the application runtime, cloud-specific dependencies and processes (e.g. code dependencies on cloud storage and CI/CD pipelines) and critical infrastructure needed to support the application in production.
A crucial step after gathering requirements was to obtain access to read-client systems (including lower environments and github repositories) in order to ensure our assessment was comprehensive and we did not have any unknown-unknowns that would add risk to our Migration Phase.
Migrating an application from one environment to another requires more effort than just changing lines of code. Here are some of the critical components that we analyze during our assessment:
- Code: The actual engine that gets packaged and deployed. Often these will have cloud/platform specific components and packages that need to be refactored to be cloud-native and/or migrated.
- Firewalls: An organization’s network security and firewall rules need to be migrated
- Config/Secret Management: Passwords, API keys and external configurations crucial to your app.
- Authentication/Authorization: Front-end sign-in, API authentication. Maintain or migrate.
- CI/CD Pipelines: Not only do we want our apps in production, but also a maintainable, extensible, automated process to deploy them.
- Cloud Storage: Most apps leverage cloud object storage to store key objects (assets, user-generated content etc) and having a storage solution collocated with the application is a must in all projects since it removes friction in development and latency in production.
- Databases: Hosted or self-managed database services are crucial for application runtimes and are targets for migration.
- Environment parity: Ensuring that we have a prod-like lower environment for development, integration and user-acceptance testing.
Throughout our migration assessment, we are looking for opportunities to optimize workflows, increase efficiency and reduce cost both by recommending best-in-breed practices and tooling, and with unique Azure capabilities.
Once we have completed our assessment, our team evaluates and presents different approaches for the migration and document them in a simple presentation to the client stakeholders. This presentation includes our findings, recommendations, timelines, cost implications and risks for the next phase.
- Findings: A thorough report including the discovered infrastructure and necessary effort to migrate the application (and supporting infrastructure) system to Azure.
- Recommendations: Operational improvements resulted from the migration such as centralizing configuration – an industry best practice to have in any application infrastructure.
- Timeline: A full migration plan, including no-downtime strategy/ies to migrate all critical infrastructure and applications along with expected effort and go-live estimates.
- Costs: New platform costs to perform migration as well as the ongoing costs to keep the system running at the same capacity as before.
- Risks: Strategies to mitigate application system downtime and all affected operations
- Client’s Duties: Highlight the efforts needed to facilitate the migration process, i.e granting access, collaboration coding sessions, etc
Here is a sample snapshot from the final report showing the cloud parity migration plan:
Once we got the green-light from the client to commence the migration we started our five phase migration plan: preparation, infrastructure deployment, code modernization, deployment and validation.
Preparation: The preparation phase of the migration was a crucial step that involved key tasks to ensure a smooth and successful migration. The first step was to validate the front-end, the credentials provided, and ensure that all the necessary information was in place and that the migration could proceed without any issues. Next, the team validated the schema and assessed the changes required in the database, cloud storage, and other related components. This step was critical to identify any potential roadblocks or challenges that may arise during the migration process. The team then took steps to address these issues before proceeding with the migration.
Infrastructure. The infrastructure migration phase involved creating migration tests for all the components of the portal, writing Infrastructure-as-Code (IaC) scripts to create smaller infrastructure components such as storage buckets and databases, and using migration scripts to transfer data with necessary transformations. The migration scripts were then tested in lower environments and the CI pipelines were updated to deploy the apps in lower and upper environments (dev/prod) in the new cloud. We also created scripts to transfer the data from Google Cloud Storage to Azure Blob Storage, with necessary transformations. These scripts were designed to run automatically and validate the migration was successful. We again tested our scripts in lower environments to ensure their reliability and accuracy before proceeding to production, though we aren’t ready to migrate prod yet, first we need the code to be ready.
Modernization: At Enfuse, the team places a strong emphasis on collaboration for all crucial code changes. The migration to Azure presented a valuable opportunity for the team to modernize their practices and enhance the quality of their products. To further support these modernization efforts, the team made modifications to the client’s backend application. This included transitioning files to be stored on Azure Blob Storage instead of Google Cloud Storage. To ensure future adaptability, the team also developed an abstraction layer that facilitates seamless communication with any cloud storage provider. This layer accounts for future changes and positions the team to be prepared for future migrations. In addition, the team also adopted Azure Spring Apps Enterprise’s built-in External Configuration Service to update the system infrastructure. This service provides the client with the ability to manage their configurations through versioning on a repository, thereby simplifying their deployment process. This update has been instrumental in modernizing the client’s infrastructure, enhancing the stability and efficiency of their deployments.
Deployment. In the deployment stage, the apps were built and deployed onto Azure Spring Apps through the use of Tanzu Build Packs. Our CI pipeline facilitated the testing of the apps in the development environment. Additionally, the method for building and integrating the frontend application was adapted to deploy our frontend as an nginx container. Azure Spring Apps allows you to deploy your own containers, Java Spring, Python Flask, .NET Steeltoe apps natively. Our client decided to keep leveraging Google authentication to log into the app, so we updated the configuration to recognize the new frontend and backend running in Azure Spring Apps. After deploying to lower environments, resolving some (typical) CORS issues, we were ready for a prod migration.
Validation. The goal is to design validation to ensure functionality parity between the GCP and Azure environments. We need a set of validation steps we perform during and after our migration. We start by automating and validating unit and integration tests in the new platform. Then we move on to validating that the new infrastructure components (i.e. External Configuration Service, Spring Gateway) work as expected by testing a couple of configurations and seeing the changes being applied. Smoke tests are also helpful during a migration as it increases our confidence in the process and can detect problems in the API early. These tests run as part of our automated CI/CD pipeline validating every deployment. In this case, everything was green and we were ready to flip the switch and go-live in Azure.
The next steps would be to validate traffic and begin shutting down the GCP environment, but that is out of scope for this blog post.
At the end of the migration, the client’s application was deployed on Azure Spring Apps as a single service able to host all backend and frontend applications, it’s set up to take advantage of the Application Configuration Service to manage configuration to different environments in a single location, use blue-green deployment strategies in the CI/CD workflows… These are only some of the advantages that Azure Spring Apps provides.
While not perfect (the UI and ability to parse deployment logs could definitely be improved), the internal portal is secure and resilient now.
The resulting infrastructure set up in Azure allowed the client to have the right-sized infrastructure for their applications while also having the option of leveraging automation for scaling up and down their applications as needed. It was now possible to modernize their platform further with all the Spring infrastructure components that the service offers, which is always a treat for making development easier.