Top 10 Mistakes to Avoid During Oracle DB@Azure Migration

 

Top 10 Mistakes to Avoid During Oracle DB@Azure Migration.


Lessons learned from enterprise implementations — the errors that recur, why they hurt, and how to mitigate the risk before it costs you.

📅
Aug 10, 2026    🏷️ Oracle DB@Azure,  Migration, Lessons Learned, Best Practices, Risk    ⏱ 28 min 





Article Overview

Most Oracle Database@Azure migrations that go badly do not fail for exotic reasons. They fail for the same handful of avoidable mistakes, made again and again across different organisations: a plan that treats a managed platform like infrastructure you fully control, sizing done by guesswork, an address plan that overlaps, licensing that was never modelled, a cutover with no way back. None of these are subtle once you have seen them, and every one is cheaper to avoid than to fix. This article collects the ten that recur most often in real enterprise implementations, and for each gives the same three things: what the mistake looks like, why it hurts, and how to mitigate it. It is deliberately practical and a little blunt — a checklist of the potholes, written so you can steer around them rather than discover them the hard way. It complements the full migration guide elsewhere on this blog, which covers the methods in depth; this piece is about the mistakes.

Before the list, one picture. The ten mistakes are not scattered randomly — they cluster in the phases of a migration, and knowing which phase each belongs to is half of catching it in time.


1.Treating a Managed Platform Like Your Own Hardware

MISTAKE 01 · PLAN

What it looks like

The team approaches the migration as a straight lift-and-shift of infrastructure they will own end to end, the way they ran Oracle in their own datacenter — planning to patch the Exadata, manage the storage, and control every layer. Then they hit the reality that Oracle Database@Azure is a managed platform: Oracle operates the Exadata infrastructure, and the responsibility boundary sits in a different place than it did on-premises.

Why it hurts

Mismatched expectations about who does what surface at the worst times — during patching, during an incident, during a capacity change. A team that assumed it controlled a layer it does not can be left waiting on a process it never mapped, or conversely can neglect a responsibility it wrongly assumed was Oracle's. The confusion is operational friction at best and an outage-extending scramble at worst.

How to mitigate

Map the shared-responsibility model explicitly, early, and in writing — exactly which layer Oracle operates, which Microsoft provides, and which remains yours — and design your runbooks and operating model around that boundary. Know how maintenance is communicated and who to call. The platform removes real operational burden, which is a benefit; the mistake is failing to re-learn the operating model to match, and carrying on-premises assumptions into a place where they no longer hold.

Re-draw the responsibility boundary before you migrate, not during an incident

The single cheapest hour in the whole project is the one spent drawing the responsibility boundary on a whiteboard with everyone in the room. Do it before go-live, write it into the runbooks, and the managed-platform model becomes the advantage it is meant to be rather than a source of mid-incident confusion.

2.Sizing by Guesswork Instead of Assessment

MISTAKE 02 · PLAN

What it looks like

Someone maps the current on-premises server's core count straight onto the new platform, or picks a shape because it feels about right, without measuring what the workload actually consumes. The sizing is a gut call dressed up as a decision, and no one has looked at real utilization data or the platform's ECPU model.

Why it hurts

Guessing goes wrong in both directions. Under-size and the migrated system is slow from day one, generating an emergency scale-up and a bad first impression that follows the project. Over-size and you pay — every month — for capacity the workload never touches, which on a consumption platform is real, recurring money. Both are avoidable with data you almost certainly already have.

How to mitigate

Run a proper assessment first. Gather actual utilization — CPU, memory, I/O, and their peaks, not just averages — from the existing system over a representative period, and size the target from that evidence, in the platform's own units. Leave deliberate headroom for the genuine peak (quarter-end, the sale event), and lean on the platform's elasticity so you can start sensibly and adjust rather than trying to be perfect on day one.

The evidence you need is already in the database's own history. A query against the AWR repository gives you the real CPU and I/O demand — average and peak — to size from, instead of a guess:

gather_sizing_evidence.sql · on the source database
-- Peak vs average CPU and I/O from AWR history, by snapshot.
-- Size the target from the PEAK columns, not the averages.
-- (Requires Diagnostics Pack; adjust the retention window to taste.)
SELECT TO_CHAR(s.begin_interval_time,'YYYY-MM-DD HH24:MI') AS snap_time,
       ROUND(AVG(m.average),1)  AS avg_busy_pct,
       ROUND(MAX(m.maxval),1)   AS peak_busy_pct
FROM   dba_hist_sysmetric_summary m
JOIN   dba_hist_snapshot s
       ON s.snap_id = m.snap_id AND s.dbid = m.dbid
WHERE  m.metric_name = 'Host CPU Utilization (%)'
AND    s.begin_interval_time > SYSDATE - 30
GROUP  BY TO_CHAR(s.begin_interval_time,'YYYY-MM-DD HH24:MI')
ORDER  BY peak_busy_pct DESC
FETCH FIRST 20 ROWS ONLY;      -- the 20 busiest intervals in 30 days

-- Companion: average and peak read/write IOPS to size storage throughput.
SELECT ROUND(AVG(average),0) AS avg_iops,
       ROUND(MAX(maxval),0)  AS peak_iops
FROM   dba_hist_sysmetric_summary
WHERE  metric_name IN ('Physical Read Total IO Requests Per Sec',
                       'Physical Write Total IO Requests Per Sec')
AND    snap_id IN (SELECT snap_id FROM dba_hist_snapshot
                   WHERE begin_interval_time > SYSDATE - 30);

⚠️ On-premises core counts do not translate directly

A core on old on-premises hardware and an ECPU on a modern Exadata platform are not the same unit of work, so a one-to-one mapping is almost always wrong — usually wastefully oversized. Size from measured workload against the platform's model, not from the number of cores your old box happened to have. The old core count is a historical accident, not a requirement.

3.Overlapping Addresses and Afterthought DNS

MISTAKE 03 · BUILD

What it looks like

The network is designed in a hurry: the new environment is given an address range that overlaps with on-premises or another cloud space, and DNS is treated as something to sort out later. Everything works in isolated testing, and then falls apart the moment the environments have to talk to each other.

Why it hurts

Overlapping IP ranges are the hybrid networking mistake you cannot easily undo — once two environments that must communicate share a range, you are into network address translation or a painful re-addressing exercise, either of which can stall the project for weeks. And DNS treated as an afterthought produces resolution failures that masquerade as connectivity problems, sending teams chasing the wrong cause for days.

How to mitigate

Plan the whole address space — on-premises, Azure, and the database subnet — before provisioning anything, with distinct, non-overlapping ranges, and treat the plan as sacred. Design DNS resolution as deliberately as routing, with conditional forwarding so names resolve correctly in every direction. The hybrid-architecture guide on this blog covers the mechanics; the mistake is doing this reactively instead of up front.

Before you commit ranges, prove they do not overlap. A few lines catch the collision on a whiteboard instead of in an incident:

check_cidr_overlap.py · run before provisioning
import ipaddress

# Every range that must route to every other — on-prem, Azure, DB subnet.
planned = {
    "on_prem":       "10.0.0.0/8",
    "azure_hub":     "10.100.0.0/16",
    "azure_app":     "10.101.0.0/16",
    "db_delegated":  "10.102.0.0/24",
}

nets = {name: ipaddress.ip_network(cidr) for name, cidr in planned.items()}
clashes = []
names = list(nets)
for i in range(len(names)):
    for j in range(i + 1, len(names)):
        a, b = nets[names[i]], nets[names[j]]
        if a.overlaps(b):
            clashes.append((names[i], names[j], str(a), str(b)))

if clashes:
    for a, b, ca, cb in clashes:
        print(f"OVERLAP: {a} ({ca}) collides with {b} ({cb})")
    raise SystemExit("Fix the address plan BEFORE provisioning anything.")
print("No overlaps. Address plan is safe to build on.")

Addressing and DNS are decided best on a whiteboard before anything is provisioned. Rediscovered during an incident, they are among the most expensive lessons a migration can teach.

4.Getting the Licensing Model Wrong

MISTAKE 04 · PLAN

What it looks like

Licensing is treated as a procurement detail to resolve at the end, rather than an architectural input at the start. The team does not model bring-your-own-license against license-included, does not check how existing Oracle entitlements map to the platform's units, and does not factor licensing into the sizing and cost picture until the bill or the true-up forces the conversation.

Why it hurts

Licensing is one of the largest cost levers on the platform, and getting it wrong is expensive in a way that compounds monthly. Choosing license-included when you hold reusable entitlements, or failing to plan the conversion window for bringing licenses across, can leave substantial savings on the table or create a compliance gap. Because it interacts with sizing, it cannot be bolted on afterward without unpicking earlier decisions.

How to mitigate

Model the licensing options alongside the sizing, not after it. Understand how your existing Oracle licenses map to the platform's units, compare bring-your-own-license against license-included for your actual estate, and plan any conversion window deliberately. The cost-optimization guide on this blog treats the levers in depth; the mistake here is simply leaving the single biggest lever until it is too late to pull cleanly.

Licensing is an architecture input, not a closing formality

Bring licensing into the design conversation on day one, next to sizing and cost. It shapes what the right shape and commitment look like, and modelling it early is often where the largest savings in the whole migration are found — or the largest overspend is quietly locked in.

5.Choosing the Wrong Migration Method

MISTAKE 05 · PLAN

What it looks like

A single migration method is chosen by habit or familiarity and applied to every database, regardless of size or how much downtime each can tolerate. A team comfortable with Data Pump exports uses it for a multi-terabyte system that cannot be down for the hours the export-and-import would take; or a team reaches for real-time replication on a small, downtime-tolerant database where a simple export would have been far less effort.

Why it hurts

The method has to match two things: the size of the database and the downtime the business will accept. Mismatch them and you either blow through the acceptable outage window — discovering mid-cutover that the chosen method needs six hours the business gave you one for — or you spend enormous effort standing up replication machinery a trivial database never needed. Either way the method fought the requirement instead of serving it.

How to mitigate

Pick the method per database, from a clear-eyed reading of size and downtime tolerance. The migration guide on this blog lays out the options; the short version of the decision is below.

If the database is...A sensible method
Small and downtime-tolerantData Pump export/import — simple and sufficient
Large but with a maintenance windowBackup-based (RMAN) or a physical standby switched over
Large and near-zero-downtime requiredGoldenGate replication with a brief cutover
Same-endian, minimal downtime, physicalData Guard standby instantiated, then role transition

The method serves the requirement, not your comfort zone

The right method is the one that fits this database's size and this system's tolerance for downtime — not the one the team happens to know best. A portfolio of databases will usually need more than one method across it, and that is correct, not a failure of standardization. Match each to its requirement and stop.

6.Underestimating the Data-Transfer Network

MISTAKE 06 · MIGRATE

What it looks like

The plan assumes the data will simply move, without doing the arithmetic on how long it actually takes to push terabytes across the available link. The transfer of the initial copy — and, for replication methods, the ongoing change stream — is treated as instantaneous in the schedule, when in reality it is gated by bandwidth.

Why it hurts

Moving a large database is fundamentally constrained by the network between source and target. Underestimate it and the initial load that the schedule allotted a weekend for is still running on Monday, the replication can never catch up because the link is saturated, and the whole cutover timeline slips. It is a simple physical limit that an optimistic plan quietly ignores until it bites.

How to mitigate

Do the transfer math early: size of data, available bandwidth, resulting time — and validate it with a real test transfer rather than a theoretical calculation. Provision adequate connectivity for the migration (ExpressRoute for serious volumes), and for very large datasets consider the appropriate bulk data-transfer approach for the initial seed so only the change stream has to cross the wire live. Size the link for peak change volume so replication can keep pace, not just average.

The math takes a minute and saves a weekend. Run your real numbers before the schedule assumes the impossible:

transfer_time.py · do this before committing a window
def transfer_hours(data_tb, link_gbps, efficiency=0.7):
    """Realistic wall-clock hours to move data_tb over a link.
    efficiency accounts for overhead/contention — 0.6-0.8 is typical,
    never assume 100% of the rated bandwidth."""
    data_gigabits = data_tb * 1024 * 8          # TB -> gigabits
    effective_gbps = link_gbps * efficiency
    seconds = data_gigabits / effective_gbps
    return seconds / 3600

for tb, gbps in [(5, 1), (20, 1), (20, 10), (50, 10)]:
    h = transfer_hours(tb, gbps)
    print(f"{tb:>3} TB over {gbps:>2} Gbps  ~ {h:6.1f} h  ({h/24:4.1f} days)")

#   5 TB over  1 Gbps  ~   16.3 h  ( 0.7 days)
#  20 TB over  1 Gbps  ~   65.0 h  ( 2.7 days)   <- a "weekend" job that isn't
#  20 TB over 10 Gbps  ~    6.5 h  ( 0.3 days)
#  50 TB over 10 Gbps  ~   16.3 h  ( 0.7 days)
# If the initial load alone exceeds the window, seed in bulk and let
# only the change stream cross the wire live.

⚠️ "The data will just move" is not a plan

Bandwidth is a hard physical constraint, and terabytes do not cross a modest link in the time an optimistic Gantt chart assumes. Calculate the transfer time from real numbers, prove it with a test, and build the schedule around the answer — not around the hope that the network is faster than it is.

7.Skipping Production-Scale Testing

MISTAKE 07 · MIGRATE

What it looks like

Testing is done on a small subset of data, at low concurrency, on a system that looks nothing like production load. Functionally everything passes, everyone signs off, and the first time the migrated system meets real production volume is in production, in front of users.

Why it hurts

The failures that matter in a migration — a plan that regresses at scale, a performance cliff under real concurrency, a process that works for a gigabyte and falls over at a terabyte — are exactly the ones a small-scale test cannot reveal. Passing a toy test builds false confidence that shatters at go-live, turning what should have been a caught defect into a production incident with an audience.

How to mitigate

Test at production scale and realistic peak load before cutover. Validate performance against the real worst case — the highest concurrency and volume the business actually produces — and measure the tail, not just the average. Rehearse the migration itself end to end, including the cutover, on a full-scale copy. The mission-critical guide on this blog puts it plainly: find the ceiling in a test, never in an incident.

A migration that has only been tested at toy scale has not been tested. The behaviours that break at production volume are precisely the ones the small test was structurally unable to show you.

8.Letting Security Slip During the Move

MISTAKE 08 · BUILD

What it looks like

Security controls that were solid on-premises are quietly dropped or deferred in the move — encryption keys handled carelessly during the transfer, temporary broad network rules opened "just for the migration" and never closed, auditing switched off to reduce noise, identity integration left for later. The system is migrated first and secured afterward, in theory.

Why it hurts

A migration is a window of elevated exposure — data in motion, temporary access, half-configured controls — and "we will secure it after go-live" has a way of becoming "we never came back to it." Temporary broad firewall rules become permanent, unencrypted paths become normal, and a compliance gap opens that no one owns. For a system worth migrating carefully, a security lapse is not a lesser problem than an outage; it can be a worse one.

How to mitigate

Carry the security posture through the migration as a first-class requirement, not a follow-up. Keep data encrypted in transit and at rest throughout, manage the encryption keys deliberately, scope any temporary access tightly and with an expiry, keep auditing on, and integrate identity from the start. The security guide on this blog covers the target posture; the migration-specific mistake is relaxing it during the move and never tightening it back.

Two quick checks catch the classic leftovers — a wide-open rule that outlived the migration, and a table that never got encrypted:

post_migration_security_checks.sh · verify after cutover
# 1. Find any NSG rule that allows traffic from ANY source (0.0.0.0/0).
#    These are the "just for the migration" holes that quietly stayed open.
az network nsg rule list \
  --nsg-name "nsg-database" --resource-group "$RG" \
  --query "[?access=='Allow' && (sourceAddressPrefix=='*' \
           || sourceAddressPrefix=='0.0.0.0/0')].\
           {rule:name, port:destinationPortRange, src:sourceAddressPrefix}" \
  -o table
# Expect: no rows. Any row is a hole to close.
verify_tde.sql · confirm nothing migrated unencrypted
-- Any application tablespace NOT encrypted after the move is a gap.
SELECT t.tablespace_name,
       NVL(e.encryptionalg, 'NOT ENCRYPTED') AS status
FROM   dba_tablespaces t
LEFT   JOIN v$encrypted_tablespaces e
       ON e.ts# = (SELECT ts# FROM sys.ts$ WHERE name = t.tablespace_name)
WHERE  t.tablespace_name NOT IN ('SYSTEM','SYSAUX','TEMP','UNDOTBS1')
ORDER  BY status DESC;   -- 'NOT ENCRYPTED' rows float to the top

Every "just for the migration" exception needs an expiry date

Temporary security relaxations are where lasting holes come from. If you must open something for the migration, write down when it closes and who closes it — and verify it actually shut after go-live. An exception with no expiry is a permanent hole wearing a temporary label.

9.A Big-Bang Cutover With No Fallback

MISTAKE 09 · CUT OVER

What it looks like

The whole estate switches to the new platform in one irreversible moment, with no rehearsed way back if something goes wrong. The old system is decommissioned or allowed to drift out of sync immediately, so the instant a serious problem appears after cutover, there is no current environment to return to — only forward, through the problem, under pressure.

Why it hurts

This is the costliest single mistake on the list because it converts any post-cutover problem into a crisis. Without a fallback, a defect discovered an hour after go-live cannot be escaped by switching back; the team must fix it live, on the new platform, with the business down and no safety net. A migration is never truly risk-free at the moment of cutover, and removing the way back removes the one thing that would have made a bad surprise survivable.

How to mitigate

Keep a fallback path and use a phased cutover. With a replication-based method, set up the reverse flow so that after switching to the target, changes there can replicate back to the old source for a grace period — keeping the old system a viable fallback until the new one has proven itself. Where possible, move in waves rather than all at once, so a problem affects a slice, not everything. Dismantle the safety net only when confidence is earned.

Concretely, the reverse path is a GoldenGate flow pointing the other way — captured on the new target, applied back to the old source — armed at cutover and kept ready until you trust the new platform:

reverse_fallback.obey · arm at cutover, keep for a grace period
-- On the NEW platform (now primary): capture changes to ship BACK.
DBLOGIN USERIDALIAS new_primary DOMAIN OracleGoldenGate
ADD EXTRACT ext_rev INTEGRATED TRANLOG BEGIN NOW
ADD EXTTRAIL rb EXTRACT ext_rev
START EXTRACT ext_rev

-- On the OLD source: apply those changes so it stays current & switch-back-ready.
DBLOGIN USERIDALIAS old_source DOMAIN OracleGoldenGate
ADD REPLICAT rep_rev PARALLEL EXTTRAIL rb
START REPLICAT rep_rev

-- Grace period passes, new platform is trusted -> tear the safety net down:
--   STOP REPLICAT rep_rev  /  STOP EXTRACT ext_rev  /  DELETE both
-- Do this ONLY once you would never choose to switch back.

⚠️ The way back is what makes cutover survivable

A cutover without a fallback bets the entire migration on nothing going wrong at the one moment things most often do. Keep the old environment current and switch-back-ready for a defined grace period after go-live. The reverse-replication path is cheap insurance against the most expensive kind of migration failure.

10.Assuming the Job Ends at Go-Live

MISTAKE 10 · OPERATE

What it looks like

The project celebrates at cutover and disbands. No one owns cost governance on the new consumption platform, monitoring was never fully wired up, the sizing chosen on day one is never revisited against real usage, and the operating discipline that a cloud platform rewards simply never gets established. The migration is declared done the moment the data is across.

Why it hurts

On a consumption-based platform, go-live is where the ongoing cost and operational story begins, not ends. Without cost governance, spend drifts upward unwatched — idle capacity, oversized shapes, unused commitments — and the savings the business case promised quietly evaporate. Without monitoring and a real operating model, small problems grow unseen. The migration can be a technical success and an operational disappointment purely because everyone went home at cutover.

How to mitigate

Treat go-live as a handover into a run phase, not the finish line. Assign ownership of cost governance and review spend on a cadence against the platform's cost tooling; right-size based on the real usage you can now measure; make sure monitoring and alerting are genuinely in place (the monitoring guide on this blog covers the unified picture); and keep optimizing. The cost-optimization guide frames the levers — the mistake is never pulling them because the project ended.

Give the run-phase owner a standing signal to act on. A simple recurring check for chronically idle databases turns "we should right-size someday" into a concrete monthly list:

idle_capacity_report.sql · run monthly in the run phase
-- Databases whose 30-day PEAK CPU never crossed a modest threshold are
-- right-sizing candidates — you are paying for capacity nobody uses.
SELECT dbid,
       ROUND(MAX(maxval),1)  AS peak_cpu_pct_30d,
       ROUND(AVG(average),1) AS avg_cpu_pct_30d,
       CASE WHEN MAX(maxval) < 40 THEN 'RIGHT-SIZE CANDIDATE'
            WHEN MAX(maxval) < 60 THEN 'review'
            ELSE 'ok' END     AS verdict
FROM   dba_hist_sysmetric_summary
WHERE  metric_name = 'Host CPU Utilization (%)'
AND    snap_id IN (SELECT snap_id FROM dba_hist_snapshot
                   WHERE begin_interval_time > SYSDATE - 30)
GROUP  BY dbid
ORDER  BY peak_cpu_pct_30d;

-- Pair with Azure Cost Management (cross-charge tags on the ODB@Azure
-- resource) so each "RIGHT-SIZE CANDIDATE" carries a monthly dollar figure.

Name a run-phase owner before the project disbands

Before the migration team scatters, name who owns cost, who owns monitoring, and who revisits sizing in ninety days. A consumption platform quietly punishes the absence of that ownership, and rewards its presence. The last act of a good migration is handing it cleanly to the people who will run it.

Bringing It Together

Read back over the ten and a pattern emerges: almost none of these are technical failures in the narrow sense. They are failures of planning, of honesty, and of discipline — carrying old assumptions into a new operating model, guessing where you could measure, deferring what you should decide early, and declaring victory before the work is done. The platform is not the hard part; the thinking around it is.

#MistakeOne-line mitigation
1Treating a managed platform like your hardwareMap the responsibility boundary before go-live
2Sizing by guessworkSize from measured workload in the platform's units
3Overlapping addresses, afterthought DNSPlan non-overlapping space and DNS up front
4Getting licensing wrongModel licensing alongside sizing, on day one
5Wrong migration methodMatch method to each database's size and downtime tolerance
6Underestimating the transfer networkDo the bandwidth math and prove it with a test
7Skipping production-scale testingTest at real scale and peak load before cutover
8Security slipping during the moveCarry the posture through; expire every exception
9Big-bang cutover, no fallbackKeep a reverse path; phase the cutover
10Assuming the job ends at go-liveHand over to a named run phase with cost ownership

Frequently Asked Questions 

Which of these mistakes is the most damaging?

The big-bang cutover with no fallback (number nine), because it converts any post-cutover problem into a full crisis with no way back. The most common, though, is assuming the job ends at go-live (number ten) — and the cheapest to fix are the planning mistakes, because catching them early costs an order of magnitude less than fixing them late.

Where do most of these originate?

In the plan phase. Four of the ten — the responsibility model, sizing, licensing, and method choice — are planning decisions, and they are both the cheapest to get right and the most often rushed. Time spent planning is the highest-return time in the whole migration.

How do I avoid the sizing mistake specifically?

Measure before you size. Collect real utilization — including peaks, not just averages — from the existing system, and size the target from that evidence in the platform's own units, with deliberate headroom for the genuine peak. Do not map old on-premises core counts one-to-one; they are a historical accident, not a requirement.

Is a fallback really necessary if testing went well?

Yes. Good testing lowers the odds of a problem but never to zero, and cutover is the moment problems most often appear. A reverse-replication path that keeps the old system current for a grace period is cheap relative to the cost of being stuck on a broken new platform with no way back. Keep it until the new platform has earned your confidence.

We finished the migration — are we done?

Not on a consumption platform. Go-live starts the run phase, where cost governance, right-sizing against real usage, and operating discipline determine whether the business case actually lands. Name owners for cost and monitoring before the project team disbands, and revisit sizing once you have real usage data.

Do these apply to a small, simple database too?

Most do, in proportion. A small database still needs the right method, sane addressing, and a security posture — but it can lean on simpler methods and lighter fallback. The mistakes to never skip regardless of size are the planning ones and security; the heavy machinery (replication, phased cutover) scales with the stakes.

Key Takeaways

The short version

•  Most migration failures are planning failures. Four of the ten live in the plan phase — the responsibility model, sizing, licensing, and method — where mistakes are cheapest to fix and most often rushed.

•  Measure, do not guess. Size from real utilization in the platform's units, do the transfer-bandwidth math, and test at production scale — the evidence you need almost always already exists.

•  Plan the network and DNS first. Non-overlapping addresses and deliberate resolution prevent two of the most expensive and hardest-to-undo problems.

•  Keep a way back. A reverse-replication fallback and a phased cutover turn the riskiest moment into a survivable one — the cheapest insurance in the project.

•  Carry security through the move. Never relax the posture "just for the migration" without an expiry — temporary holes become permanent ones.

•  Go-live is a handover, not a finish line. Name run-phase owners for cost and monitoring, and keep optimising — a consumption platform rewards the discipline and punishes its absence.

None of the ten mistakes here require deep expertise to avoid — they require the discipline to plan honestly, measure instead of guess, decide the hard things early, keep a way back, and stay engaged after go-live. That is oddly reassuring: it means a successful Oracle Database@Azure migration is less about heroics and more about not tripping over the well-worn obstacles that trip everyone. The teams whose migrations go smoothly are rarely the ones with the cleverest architecture; they are the ones who saw these potholes coming and simply steered around them.

If you take one thing from this list, let it be the shape of it: the cheapest fixes live earliest, in planning, and the most expensive surprises detonate later, at cutover and in operations, precisely because the planning was skipped. Move your attention left — toward the assessment, the address plan, the licensing model, the method choice, the fallback design — and most of the pain further right simply never materializes. A migration is a project you can make boring, and boring, here, is exactly what success looks like.

SZ

Syed Zaheer

Service Delivery Director · Techvisions · Cloud, AI & Managed Infrastructure

Writes, speaks, and builds across cloud, AI, enterprise platforms, and digital transformation. The insights shared here are shaped by real-world experience delivering complex technology initiatives, modernizing enterprise environments, and helping organizations accelerate innovation across multi-cloud ecosystems. Published with the hope that they save others time, simplify complexity, and inspire practical solutions.


This article is independent commentary drawn from general implementation experience and reflects Oracle and Microsoft platform behaviour available at the time of writing, which changes frequently — verify current sizing units, licensing rules, migration methods, and platform features against Oracle and Microsoft documentation before planning a migration. Guidance here is general and not a substitute for a design tailored to your environment. All SQL, shell, and code examples are illustrative, use placeholder names and values, may depend on specific Oracle options or packs (for example, the Diagnostics Pack for AWR views), and must be validated against your own environment, licensing, and current documentation before use. This article is not affiliated with, endorsed by, or sponsored by Oracle or Microsoft.


Comments

Popular posts from this blog

Installation of Oracle Applications R12.1.1 on Linux and vmware

EBS R12.2 Install Error - oracle.apps.fnd.txk.config.ProcessStateException: Patch directory does not exist or not writable -

ntp service in Maintenance mode Solaris 10