{"id":2259,"date":"2026-05-03T22:49:02","date_gmt":"2026-05-03T22:49:02","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/all-things-azure\/?p=2259"},"modified":"2026-05-03T22:49:02","modified_gmt":"2026-05-03T22:49:02","slug":"removing-the-monkey-work-of-migration-using-agentic-platform-engineering","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/all-things-azure\/removing-the-monkey-work-of-migration-using-agentic-platform-engineering\/","title":{"rendered":"Removing The Monkey Work of Migration"},"content":{"rendered":"<p><img decoding=\"async\" class=\"alignnone wp-image-2260\" src=\"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-content\/uploads\/sites\/83\/2026\/05\/word-image-2259-1.webp\" alt=\"git-APE\" width=\"1024\" height=\"1024\" srcset=\"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-content\/uploads\/sites\/83\/2026\/05\/word-image-2259-1.webp 1024w, https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-content\/uploads\/sites\/83\/2026\/05\/word-image-2259-1-300x300.webp 300w, https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-content\/uploads\/sites\/83\/2026\/05\/word-image-2259-1-150x150.webp 150w, https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-content\/uploads\/sites\/83\/2026\/05\/word-image-2259-1-768x768.webp 768w, https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-content\/uploads\/sites\/83\/2026\/05\/word-image-2259-1-24x24.webp 24w, https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-content\/uploads\/sites\/83\/2026\/05\/word-image-2259-1-48x48.webp 48w, https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-content\/uploads\/sites\/83\/2026\/05\/word-image-2259-1-96x96.webp 96w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p><span style=\"font-size: 12pt; font-family: 'comic sans ms', sans-serif;\">Removing The Monkey Work of Migration; in this post we show how Git-Ape analyses an AWS deployment repo and generates an Azure-native replacement, with design critique built in.<\/span><\/p>\n<p>This post walks through a real migration workflow: start with an AWS deployment repo and end with an Azure deployment repo. The goal isn\u2019t a 1:1 syntax conversion. It\u2019s <strong>intent extraction and architecture remapping<\/strong>\u2014agents that read what your deployment does, propose an Azure-native equivalent, and generate deployment-ready artefacts. Along the way, a critique step flags design issues early, before you ship them.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>Related reading:<\/strong> <a href=\"https:\/\/devblogs.microsoft.com\/all-things-azure\/platform-engineering-for-the-agentic-ai-era\/\">Platform engineering for the agentic AI era (Microsoft DevBlogs)<\/a><\/p>\n<p><strong>Related reading:<\/strong> <a href=\"https:\/\/devblogs.microsoft.com\/all-things-azure\/putting-agentic-platform-engineering-to-the-test\/\">Putting agentic platform engineering to the test (Microsoft DevBlogs)<\/a><\/p>\n<h2>TL;DR<\/h2>\n<ul>\n<li>Git-Ape analysed an AWS Terraform repo, extracted the real deployment intent, and mapped it to an Azure-native design.<\/li>\n<li>A critique step caught two blockers early (don\u2019t build on startup; don\u2019t mirror S3 with Blob if CI deploys artefacts).<\/li>\n<li>Output: Bicep + GitHub Actions that deploys a Next.js app to Azure App Service (Linux, Node 20), with Managed Identity and App Insights\/Log Analytics.<\/li>\n<\/ul>\n<p><strong>Note:<\/strong> This walkthrough stops at repo and artefact generation. Git-Ape can also help with onboarding (OIDC\/RBAC), quality gates (security, what-if, cost), and controlled deployments, but those steps aren\u2019t shown here.<\/p>\n<h2>Prerequisites<\/h2>\n<p>You\u2019ll need access to an Azure subscription, AWS credentials for the source environment (read-only is enough for analysis), and GitHub access to read\/write repos. In the walkthrough, Git-Ape validates your local CLI tooling and active sign-ins up front (see Step 1).<\/p>\n<h2>The scenario: migrate an AWS Terraform deployment to Azure<\/h2>\n<p><strong>Input (AWS): <\/strong><a href=\"https:\/\/github.com\/dawright22\/contoso-migration.git\">contoso-migration<\/a> \u2014 Terraform that deploys the Contoso Outdoors web app on AWS with VPC\/networking, EC2 (Ubuntu 22.04), an Application Load Balancer, S3, and IAM; the app runs via PM2 and exposes an ALB URL.<\/p>\n<p><strong>Output (Azure): <\/strong><a href=\"https:\/\/github.com\/dawright22\/contoso-azure.git\">contoso-azure<\/a> \u2014 an Azure-native equivalent using git-ape + GitHub Actions to deploy to Azure App Service (Linux, Node.js 20 LTS) with Managed Identity, health checks, and monitoring via Application Insights\/Log Analytics.<\/p>\n<h2>What happened (step by step)<\/h2>\n<p>Below is the sequence Git-Ape followed\u2014from prerequisites through to generated Azure artefacts.<\/p>\n<h3>Step 1: Validate tooling and sign-in state<\/h3>\n<p>Invoking @git-ape triggered a prerequisite check that validated all required CLI tools and active auth sessions:<\/p>\n<table>\n<tbody>\n<tr>\n<td>Tool<\/td>\n<td>Status<\/td>\n<td>Found Version<\/td>\n<td>Minimum<\/td>\n<\/tr>\n<tr>\n<td>az<\/td>\n<td>\u2705<\/td>\n<td>2.84.0<\/td>\n<td>2.50<\/td>\n<\/tr>\n<tr>\n<td>aws<\/td>\n<td>\u2705<\/td>\n<td>2.34.30<\/td>\n<td>2.0<\/td>\n<\/tr>\n<tr>\n<td>gh<\/td>\n<td>\u2705<\/td>\n<td>2.45.0<\/td>\n<td>2.0<\/td>\n<\/tr>\n<tr>\n<td>jq<\/td>\n<td>\u2705<\/td>\n<td>1.7<\/td>\n<td>1.6<\/td>\n<\/tr>\n<tr>\n<td>git<\/td>\n<td>\u2705<\/td>\n<td>2.43.0<\/td>\n<td>any<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Auth sessions confirmed: Azure subscription active, AWS credentials valid, GitHub CLI authenticated.<\/p>\n<h3>Step 2: Extract intent from the AWS repo<\/h3>\n<p>Git-Ape read the source repo via GitHub API \u2014 every Terraform file, the user_data.sh bootstrap script, and all documentation. From this, it extracted deployment intent:<\/p>\n<ul>\n<li>Runtime: Next.js on Node.js 20, built with npm ci &amp;&amp; npm run build, run via PM2 in cluster mode on port 3000<\/li>\n<li>Compute: EC2 t3.micro (Ubuntu 22.04) with a 30GB gp3 root volume<\/li>\n<li>Ingress: Application Load Balancer (HTTP port 80 \u2192 EC2 port 3000), public<\/li>\n<li>Artefacts: App tarball stored in S3, downloaded to EC2 on boot via user_data.sh<\/li>\n<li>Identity: IAM role allowing EC2 to read from S3<\/li>\n<li>Networking: VPC (10.0.0.0\/16) with two subnets across two AZs, internet gateway, security groups (ports 22, 80, 443, 3000 open to 0.0.0.0\/0)<\/li>\n<li>Monitoring: None<\/li>\n<li>Estimated cost: ~$34\/month<\/li>\n<\/ul>\n<h3>Step 3: Map services to an Azure-native design<\/h3>\n<p>Based on the extracted intent, Git-Ape proposed an Azure mapping focused on lowest cost:<\/p>\n<table>\n<tbody>\n<tr>\n<td>AWS (source)<\/td>\n<td>Azure (target)<\/td>\n<td>Outcome<\/td>\n<\/tr>\n<tr>\n<td>EC2 + user_data.sh + PM2<\/td>\n<td>App Service (B1, Linux, Node 20)<\/td>\n<td>No server patching\/SSH; platform-managed runtime<\/td>\n<\/tr>\n<tr>\n<td>Application Load Balancer (HTTP)<\/td>\n<td>App Service built-in LB + HTTPS-only<\/td>\n<td>Simpler ingress + improved security defaults<\/td>\n<\/tr>\n<tr>\n<td>IAM roles\/policies<\/td>\n<td>System-assigned Managed Identity<\/td>\n<td>No stored credentials; Azure RBAC<\/td>\n<\/tr>\n<tr>\n<td>Terraform apply from laptop<\/td>\n<td>GitHub Actions (OIDC) + Bicep<\/td>\n<td>Repeatable deployments with audit trail<\/td>\n<\/tr>\n<tr>\n<td>No monitoring<\/td>\n<td>Application Insights + Log Analytics<\/td>\n<td>First-class telemetry and troubleshooting<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Step 4: Critique the design before you generate code<\/h3>\n<p>Before generating any code, Git-Ape consulted its rubber-duck critique agent for an independent design review. This caught two blocking issues that significantly improved the final output:<\/p>\n<p><strong>Blocking fix 1 \u2014 Deployment model: <\/strong>The initial plan replicated the AWS pattern (download tarball from storage, run npm ci &amp;&amp; npm run build on startup). The critique flagged this as a production anti-pattern: slow cold starts, nondeterministic deployments, broken scale-out, and build failures becoming runtime outages.<\/p>\n<p><strong>Fix<\/strong>: build in CI (GitHub Actions), deploy a ready-to-run artefact via zip deploy.<\/p>\n<p><strong>Blocking fix 2 \u2014 Storage layer unnecessary: <\/strong>The original plan included Azure Blob Storage to mirror S3 artefact storage. The critique pointed out this added cost and complexity with no benefit when deploying via CI.<\/p>\n<p><strong>Fix<\/strong>: drop Blob Storage entirely.<\/p>\n<p><strong>Other improvements adopted: <\/strong>drop PM2 (App Service manages processes), add Application Insights from day one, enforce HTTPS-only + TLS 1.2 + disable FTP.<\/p>\n<h3>Step 5: Generate IaC<\/h3>\n<p>Git-Ape initially started generating Terraform with the AzureRM provider. On human in loop review this then changed to: \u201cwrite the most efficient code\u2014doesn\u2019t have to be Terraform.\u201d That switch landed on <strong>Bicep<\/strong> (Azure\u2019s native IaC), producing a single ~80-line template instead of 200+ lines of Terraform.<\/p>\n<h3>Step 6: Create the target repo and generate deployment artefacts<\/h3>\n<p>Once the target shape was agreed, Git-Ape generated the Azure repo and pushed it to GitHub:<\/p>\n<ul>\n<li>Created the GitHub repo <a href=\"https:\/\/github.com\/dawright22\/contoso-azure.git\">contoso-azure<\/a> via gh CLI<\/li>\n<li>Cloned the source repo, pushed history to the new repo<\/li>\n<li>Generated infra\/main.bicep: App Service Plan (B1 Linux), Web App (Node 20 LTS, HTTPS-only, TLS 1.2, FTP disabled, managed identity, health checks, always-on), Application Insights, Log Analytics<\/li>\n<li>Generated .GitHub\/workflows\/deploy.yml: checkout \u2192 setup Node \u2192 npm ci \u2192 npm run build \u2192 Azure login (OIDC) \u2192 create RG \u2192 deploy Bicep \u2192 zip deploy \u2192 health check<\/li>\n<li>Updated README.md and DEPLOYMENT_GUIDE.md for Azure<\/li>\n<li>Removed all AWS files (main.tf, variables.tf, outputs.tf, terraform.tf, user_data.sh, etc.)<\/li>\n<li>Committed and pushed to main<\/li>\n<\/ul>\n<h2>What improves with the generated Azure design<\/h2>\n<p>This is an architecture-level comparison derived from the generated configuration<\/p>\n<table>\n<tbody>\n<tr>\n<td>Dimension<\/td>\n<td>AWS (was)<\/td>\n<td>Azure (generated)<\/td>\n<\/tr>\n<tr>\n<td>Cost (estimate)<\/td>\n<td>~$34\/month (EC2 $8 + ALB $15 + S3 $1 + bandwidth $10)<\/td>\n<td>~$13\/month (App Service B1 $13, monitoring free tier)<\/td>\n<\/tr>\n<tr>\n<td>Security<\/td>\n<td>HTTP-only, SSH open to 0.0.0.0\/0, IAM keys<\/td>\n<td>HTTPS-only, TLS 1.2, FTP disabled, OIDC + Managed Identity<\/td>\n<\/tr>\n<tr>\n<td>Operations<\/td>\n<td>SSH + PM2 babysitting, manual terraform apply<\/td>\n<td>Push-to-main deploy, health checks, App Insights<\/td>\n<\/tr>\n<tr>\n<td>Auditability<\/td>\n<td>Terraform state on laptop<\/td>\n<td>Bicep + Actions in Git, deployment logs in GitHub<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>What Git-Ape could have also added for production environments.<\/h2>\n<p>The full Git-Ape workflow includes additional capabilities that were not used in this run:<\/p>\n<ul>\n<li>Onboarding: OIDC federation setup, RBAC role assignments, GitHub environment creation<\/li>\n<li>Security gate: automated security analysis of generated templates with blocking\/pass verdicts<\/li>\n<li>Preflight validation: ARM what-if analysis before deployment<\/li>\n<li>Cost estimation: Azure Pricing API queries for per-resource cost breakdown<\/li>\n<li>Deployment execution: az deployment create with progress monitoring<\/li>\n<li>Integration testing: post-deployment health checks and resource verification<\/li>\n<li>Human approval gates: explicit confirmation before any Azure changes<\/li>\n<\/ul>\n<h2>The control frameworks on Offer<\/h2>\n<p>To prevent &#8220;AI freestyle migrations&#8221; in production, Git-Ape supports a phased control framework:<\/p>\n<ul>\n<li>Phase 0 \u2014 Pre-req validation: confirm tooling and auth (exercised \u2705)<\/li>\n<li>Phase 1 \u2014 Intake &amp; intent extraction: read source repo, extract deployment intent (exercised \u2705)<\/li>\n<li>Phase 2 \u2014 Repo onboarding: configure OIDC, RBAC, GitHub environments (available, not exercised)<\/li>\n<li>Phase 3 \u2014 Target blueprint: define Azure mapping, get design critique (exercised \u2705)<\/li>\n<li>Phase 4 \u2014 Generate artefacts: produce Bicep, workflows, docs (exercised \u2705)<\/li>\n<li>Phase 5 \u2014 Quality gates: security, preflight, cost checks (available, not exercised)<\/li>\n<li>Phase 6 \u2014 Human approval + deploy + validate (available, not exercised)<\/li>\n<\/ul>\n<h2>Human-in-the-loop controls<\/h2>\n<p>Git-Ape supports two review models:<\/p>\n<p><strong>Option A: Local &#8220;generate then review&#8221; <\/strong>\u2014 generate artefacts locally, review diffs in your editor, run local validations, commit only artefacts that pass. This is what was demonstrated.<\/p>\n<p><strong>Option B: Pull request as the control plane <\/strong>\u2014 create a branch, open a PR with generated artefacts, require reviewers, run CI checks (template lint, security scans, preflight), use protected GitHub environments for staged deployment approval.<\/p>\n<h2>What to watch for<\/h2>\n<ul>\n<li>Over-permissioned deployments: keep RBAC scopes tight; require PR review for role assignments<\/li>\n<li>Hidden runtime assumptions: user_data scripts encode tribal knowledge; ensure Azure runtime has equivalent environment variables, build steps, and health probes<\/li>\n<li>Network exposure drift: confirm inbound rules match intent (the AWS setup had SSH open to the world \u2014 the Azure version closes this)<\/li>\n<li>SKU creep: require cost checks for plan sizes and monitoring retention<\/li>\n<li>One-click production risk: protect GitHub environments so merges don&#8217;t automatically push to prod without approval<\/li>\n<\/ul>\n<h2>Wrap-up<\/h2>\n<p>This example shows the shape of an agent-assisted migration: Git-Ape analysed an AWS repo, extracted deployment intent, proposed an Azure architecture, incorporated design critique that caught two blocking issues, and generated a deployment-ready Azure repo with Bicep infrastructure and CI\/CD pipeline.<\/p>\n<p><strong>The real story isn\u2019t \u201cone IaC method over another.\u201d It\u2019s<\/strong> the combination of <strong>intent extraction<\/strong> from existing infrastructure code, <strong>architecture remapping<\/strong> to Azure-native services, <strong>critique-driven correction<\/strong> that improves the design before any code is written, and the <strong>generation<\/strong> of cleaner, more secure deployment assets than the original.<\/p>\n<p>The win here is a safer, more deterministic starting point for \u201cwhat I can run on Azure\u201d to a clear path on how to layer on onboarding, quality gates, and controlled deployment when you\u2019re ready to take it to production.<\/p>\n<h2>Next steps<\/h2>\n<ul>\n<li>Run the generated workflow in a dev subscription, then confirm the app responds to the health check and emits telemetry to Application Insights.<\/li>\n<li>Decide how you want to handle environment separation (dev\/test\/prod) and protect deployments with GitHub environments and approvals.<\/li>\n<li>Harden identity and networking based on your real requirements (RBAC scope, secret storage, private endpoints\/VNet integration if needed).<\/li>\n<\/ul>\n<p><strong>If you try this:<\/strong> treat the first generated Azure design as a draft. Review it like any other change, especially identity, inbound exposure, and the gates that control promotion to production.<\/p>\n<p> Go to <a href=\"https:\/\/azure.github.io\/git-ape\/\">Intelligent Cloud Deployment Agents | Git-Ape<\/a> to find out more and get started.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Removing The Monkey Work of Migration; in this post we show how Git-Ape analyses an AWS deployment repo and generates an Azure-native replacement, with design critique built in. This post walks through a real migration workflow: start with an AWS deployment repo and end with an Azure deployment repo. The goal isn\u2019t a 1:1 syntax [&hellip;]<\/p>\n","protected":false},"author":193916,"featured_media":2260,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[112,35,1],"tags":[123,137],"class_list":["post-2259","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-agentic-devops","category-agents","category-azure","tag-agenticdevops","tag-git-ape"],"acf":[],"blog_post_summary":"<p>Removing The Monkey Work of Migration; in this post we show how Git-Ape analyses an AWS deployment repo and generates an Azure-native replacement, with design critique built in. This post walks through a real migration workflow: start with an AWS deployment repo and end with an Azure deployment repo. The goal isn\u2019t a 1:1 syntax [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-json\/wp\/v2\/posts\/2259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-json\/wp\/v2\/users\/193916"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-json\/wp\/v2\/comments?post=2259"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-json\/wp\/v2\/posts\/2259\/revisions"}],"predecessor-version":[{"id":2269,"href":"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-json\/wp\/v2\/posts\/2259\/revisions\/2269"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-json\/wp\/v2\/media\/2260"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-json\/wp\/v2\/media?parent=2259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-json\/wp\/v2\/categories?post=2259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/all-things-azure\/wp-json\/wp\/v2\/tags?post=2259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}