Skip to content
nikhil.

~/work/liquidmind-azure-to-aws

Liquidmind: four AI products moved from Azure to one AWS architecture

Liquidmind's trade-compliance products ran on Azure: Blob Storage, Cosmos DB, Service Bus, Document Intelligence, Azure OpenAI and App Service. I moved them onto one AWS architecture shared by TradeGuard, Patram AI, TariffIQ and Smart Export. Fargate workers sit behind S3 events and SQS FIFO, Bedrock serves the models, there are separate dev and prod accounts, and everything ships through one OIDC-based pipeline.

client
Liquidmind.ai
year
2026
duration
Aug – Oct 2026
role
AI/ML Engineering Consultant · migration architect
AWS ECS FargateSQS FIFODynamoDBS3Amazon BedrockTextractRDS PostgreSQLpgvectorAmplifyGitHub ActionsPython
Live

4

products on one platform

9

Azure services replaced

24

Fargate services (dev + prod)

6 → 1

TradeGuard repos consolidated

Challenge

Liquidmind builds AI-first software for trade, customs and compliance. Indian exporters, customs house agents and enterprises use it to check shipping documents, answer regulatory questions and classify goods. When I joined there were four products:

  • TradeGuard: cross-checks a shipment's documents against each other (Shipping Bill against Tax Invoice for exports; Bill of Entry, Bill of Lading, Commercial Invoice and Certificate of Origin for imports) and flags every mismatch that could stop a consignment at customs.
  • Patram AI: question answering over trade documents and regulations, with answers that cite the page they came from.
  • TariffIQ: HSN classification plus duty, anti-dumping, RoDTEP and Drawback lookups.
  • Smart Export: export-workflow tooling built on the same document and AI primitives.

TradeGuard, the oldest product, was built on Azure. Uploads went to Blob Storage through SAS URLs. Function-style blob triggers fired workers on App Service, and job state lived in Cosmos DB containers. Service Bus queues connected the extract and compare stages, Document Intelligence read the PDFs, and a pool of three Azure OpenAI GPT-4o keys did the reasoning. TariffIQ's knowledge base was seeded with Azure OpenAI embeddings into an Azure AI Search index.

The newer products were being built on AWS, so the company was running two clouds, two sets of credentials and two ways of deploying for one small team. The goal was one architecture that all four products share, without breaking the exporters who were using TradeGuard every day.

Approach

Map every Azure primitive to its AWS counterpart

I treated the migration as a set of semantic translations, not a lift-and-shift. Each Azure service had a guarantee the code relied on, and each guarantee needed an explicit AWS equivalent:

AzureAWSWhat had to be re-proven
Blob Storage (2 accounts, SAS uploads)S3 with presigned uploadsUpload contract for the frontends
Blob-triggered functionsS3 ObjectCreated → SQS → ECS listener serviceAt-least-once delivery, so dedup moved into DynamoDB
Cosmos DB Files / ImportFilesDynamoDB files / importfiles, on-demandAccess patterns by job id, no cross-partition queries
Service Bus queues + message locksSQS FIFO + visibility heartbeatLong extractions must not be redelivered mid-flight
Table storage "create if absent" claimDynamoDB conditional putExactly-once job claim
Document Intelligence prebuilt-documentTextract AnalyzeDocument (FORMS + TABLES)Field coverage on scanned and born-digital PDFs
Azure OpenAI GPT-4o (3-key pool)Bedrock Claude via the Converse APISame JSON contract, without a native JSON mode
Azure OpenAI embeddings for semantic field matchingOne batched LLM-judge call per document pairMatching quality, at fewer calls
App Service / Container AppsECS Fargate + ECRHealth checks, rollout and rollback

The message-lock row needed the most care. Service Bus holds a lock on the message while the worker runs. SQS instead hides the message for a visibility timeout, and an extraction of a multi-page scan can take longer than that. The extract worker now runs a heartbeat that keeps extending visibility while it works, and it backs off with ChangeMessageVisibility on transient failures instead of crashing and letting the message reappear at once.

Make the pipeline event-driven end to end

The TradeGuard flow on AWS:

  1. The backend writes each uploaded PDF to S3. It never touches a queue.
  2. S3 ObjectCreated events land on a standard SQS queue. An upload listener deduplicates them in DynamoDB and forwards the work to an extract FIFO queue.
  3. The extract worker runs Textract and Claude on Bedrock and writes structured JSON back to S3. That write fires a second S3 → SQS notification.
  4. A blob checker waits until every document in the job has been extracted. It then claims the job exactly once with a DynamoDB conditional write and sends it to the comparison FIFO queue. It also runs a 300-second watchdog that re-drives stuck jobs.
  5. The comparison worker runs the rule engine, writes the xlsx and JSON reports to S3, and records the result in DynamoDB.
  6. The browser polls the backend over HTTP for status.

Each stage is a separate Fargate service that scales on its own queue. A slow Textract call backs up the extract queue and nothing else.

One platform, four products

All four products now share one shape:

  • Compute: ECS Fargate for every backend and worker, 24 services across dev and prod, all Python images. API services sit behind an ALB with TLS 1.3. Frontends are Vite/React single-page apps on Amplify static hosting, so no Node server runs at request time.
  • Data: one RDS PostgreSQL 18 instance (Multi-AZ and encrypted in prod) shared by TariffIQ and Patram, with pgvector for Patram's embeddings. DynamoDB holds TradeGuard's job state. S3 holds documents, with Block Public Access on every bucket.
  • AI: Bedrock for every model call, through cross-region inference profiles so a regional capacity crunch does not take a product down. Textract handles layout, and Bedrock Knowledge Bases back TariffIQ's tariff search.
  • Identity and billing: one Clerk instance behind a Liquidmind platform gateway. The gateway forwards a signed identity header and meters credits per product.
  • Accounts: separate dev and prod AWS accounts in ap-south-1, with secrets in Secrets Manager (one secret per service, injected as ECS task secrets) and SSO for people. Dev services scale to zero outside 08:00–19:00 IST on weekdays.

One repo, one pipeline

TradeGuard alone had grown to six repositories: backend, frontend and four workers. I consolidated them into one monorepo with a shared tradeguard-core package and a parity-check script that proved the monorepo built the same artefacts as the six old repos.

  • Local development runs the whole system with one compose command. A local AWS emulator stands in for S3, SQS, DynamoDB, Secrets Manager and KMS, reached through per-service AWS_ENDPOINT_URL_<SVC> variables, so the same code runs against the emulator and real AWS.
  • CI/CD is GitHub Actions with OIDC, so there are no stored AWS keys. Each deploy builds, pushes to ECR, and rolls ECS by swapping the image on the live task definition. Rollback is a one-command script.

What shipped

  • TradeGuard's Azure footprint fully replaced: storage, triggers, database, queues, OCR, LLM and hosting.
  • TariffIQ moved onto Bedrock (Claude for disambiguation, Titan embeddings and a Bedrock Knowledge Base for tariff search).
  • Patram AI and Smart Export running on the same Fargate, RDS, S3 and Bedrock platform, behind the same gateway and pipeline.
  • A six-repo-to-monorepo consolidation with full-local development and unified CI/CD.

Results

  • One cloud, one way to deploy: four products, 24 Fargate services, two accounts, one pipeline pattern.
  • Fewer moving parts in the AI path: semantic field matching went from many embedding calls to one batched judge call per document pair.
  • Independent scaling per stage: extraction, aggregation and comparison each scale on their own queue depth.
  • Cheaper idle: the dev environment sleeps outside working hours.

The full story of the reasoning is in the migration write-up, and the AI layer the migration carried across is in architecting trade-EXIM document intelligence.

What I'd do differently

Write the infrastructure as Terraform from the first day of the migration. Moving fast through the console got four products onto AWS quickly, but every resource created by hand is one that has to be imported into code later.

Share

More case studies

Related writing

// need something similar?

Let's talk about your system.

A 15-minute call is enough to tell you whether I can help and roughly what it takes.