Migrating Existing Infrastructure to Facets

Overview

Migrating existing infrastructure to Facets means bringing cloud resources - already provisioned and running - under Facets-managed Terraform state without destroying or recreating them. Once under management, Facets handles all future drift detection, planning, and deployment through its standard release workflow.

This guide covers the end-to-end migration process and describes how Praxis, the Facets AI assistant, can guide and execute each stage.

Critical distinction: This is not a lift-and-shift. Resources remain running throughout. The goal is to make Terraform "adopt" existing resources so Facets can manage them going forward.


Migration Flow

Migration follows nine stages. Praxis can execute or guide each stage interactively.

Step 1: Identify the Target Resource

Understand what you are migrating and where it lives. Provide Praxis with:

  • Cloud account which AWS/GCP/Azure account holds the resource
  • Region where the resource is deployed
  • Cloud resource identifier the actual ID as seen in the cloud console (e.g., EKS cluster name, RDS instance ID, GKE cluster name)
  • and any other extra information that Praxis should know before discovery.

Step 2: Discover Cloud Resources

Collect the cloud IDs for every Terraform resource in the module.

Praxis queries the cloud account using read-only CLI commands to build a complete mapping of Terraform resource address → Cloud resource ID / ARN.


Step 3: Prepare the Module

After Step 2, Praxis maps the discovered modules to the modules present in the Facets Module Registry.

Incase the module is not present in the registry, Praxis makes production ready custom terraform modules and registers them in the control plane for usage across projects.


Step 4: Run a Targeted Plan

Once all the required infrastructure modules are available in the control plane, Praxis creates a project with a blueprint that mirrors the existing infrastructure — based on the discovery carried out in the previous step.

With the blueprint in place, Praxis launches an environment using the selected resources and runs a targeted Terraform plan scoped only to the resource being migrated, ensuring no interference with other modules.

Praxis then fetches the full plan logs and extracts the resource addresses, which are used directly in the import blocks during the next stage.


Step 5: Build Imports Incrementally

Construct Terraform import blocks for every resource and verify each batch with a plan.

Recommended Import Order (Bottom-Up): Import dependencies before the resources that depend on them:

  • Security group rules, IAM policy attachments, IAM policies
  • Security groups, IAM roles
  • CloudWatch log groups (only if they exist), OIDC providers, Addons / extensions
  • Main resource (cluster, database, etc.)

Praxis always uses base64 encoding to pass imports.tf via custom release — this avoids shell-escaping failures with HCL's quotes, brackets, and special characters.

Praxis adds resources in batches, verifying each batch before proceeding. Logs are analyzed with targeted grep patterns.


Step 6: Reconcile Drifts

For each imported resource, resolve all plan diffs until the plan is clean.

After importing, the Terraform plan will often show differences between what Facets expects and what exists in the cloud. For each diff, Praxis presents three choices:

  • Ignore drift — Add the field to lifecycle in the module
  • Allow the change — Let Terraform update the field on apply (Praxis explains risk)
  • Make it configurable — Add a spec field to facets.yaml, wire it through the module, and update the blueprint to match the existing cloud value — so the plan becomes a no-op

After each module change, republish and re-plan. Repeat until: Plan: 0 to add, 0 to change, 0 to destroy.


Step 7: Apply

Import all resources into Terraform state. This is the irreversible step. Praxis will always present a full summary and require explicit user approval before proceeding.

Pre-apply checklist Praxis validates:

  • Zero forces replacement diffs remaining. All in-place updates have been reviewed and approved
  • No unexpected destroy operations. Plan summary shows 0 to destroy

The apply performs two operations atomically: Imports each resource into Terraform state and applies any remaining approved in-place updates


Working with Praxis

Praxis automates the majority of this workflow interactively. Here is how to engage it:

Starting a Migration: Tell Praxis what you want to migrate:

"I want to import my existing EKS cluster 'prod-api-cluster' in us-east-1 into the Facets resource kubernetes_cluster/prod-api in project my-platform."

Praxis will:

  • Load the terraform_import skill and follow the structured workflow
  • Download and analyze the module. Query the cloud account for resource IDs
  • Build imports.tf incrementally, verifying each batch
  • Present every diff with a risk assessment before any apply
  • Request your explicit approval before the final apply step

At minimum, Praxis needs:

  • Cloud account and region
  • The primary cloud resource identifier (cluster name, instance ID, etc.)

What Praxis Will Never Do Without Approval

  • Run terraform apply with imports
  • Proceed past a plan that shows forces replacement
  • Proceed past a plan that shows destroy