# Migrate ClickOps to IaC

Source: https://docs.ops0.com/docs/how-to/migrate-clickops-to-iac

Transform manually-created AWS resources into version-controlled Terraform code without recreating them.

---

## Scenario

Your team has been creating AWS resources through the console for months or years. You now need to:
- Bring everything under version control
- Enable reproducible deployments
- Meet compliance requirements
- Stop "who changed what?" mysteries

This guide shows how to use ops0's Discovery feature to scan, import, and manage existing resources.

---

## Prerequisites

- AWS integration configured with **read permissions** ([Setup guide](https://docs.ops0.com/docs/getting-started/connect-cloud))

- At least one IaC project created (for importing resources)

---

## Step 1: Run a Discovery Scan

1. Click **Discovery** in the left sidebar

2. Click **New Scan**

3. Select your AWS integration

4. Choose regions to scan (or select "All Regions")

5. Click **Start Scan**

### What Gets Scanned

ops0 discovers resources across these categories:

| Category | Resources |
|----------|-----------|
| **Compute** | EC2 instances, Auto Scaling groups, Lambda functions |
| **Networking** | VPCs, subnets, security groups, NAT gateways, load balancers |
| **Storage** | S3 buckets, EBS volumes, EFS file systems |
| **Database** | RDS instances, DynamoDB tables, ElastiCache clusters |
| **Security** | IAM roles, policies, KMS keys |
| **Containers** | ECS clusters, EKS clusters, ECR repositories |

---

## Step 2: Review Discovered Resources

After the scan completes, you'll see a list of all discovered resources.

### Understanding the Status

| Status | Meaning |
|--------|---------|
| **Unmanaged** | Resource exists in AWS but not in any Terraform state |
| **Managed** | Resource is already tracked by an ops0 project |
| **Drifted** | Resource differs from Terraform state (manual change detected) |

### Filtering Results

Use filters to focus on what matters:

By Type

Show only EC2, S3, RDS, etc.

By Region

Focus on specific regions

By Status

Show unmanaged only

---

## Step 3: Select Resources to Import

Not all resources need to be imported immediately. Start with a logical group:

### Recommended Import Order

1. **Networking first** - VPCs, subnets, route tables (other resources depend on these)

2. **Security next** - Security groups, IAM roles

3. **Storage** - S3 buckets, EBS volumes

4. **Compute last** - EC2, Lambda, containers

Select resources by clicking the checkbox next to each one, or use **Select All** for the filtered view.

---

## Step 4: Create Terraform Code

With resources selected, click **Create Terraform**.

ops0 will:
1. Analyze each resource's current configuration
2. Produce Terraform code that matches the live state
3. Organize code into logical files (vpc.tf, ec2.tf, etc.)

### Example Code

For an EC2 instance:

```hcl
resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.medium"
  subnet_id     = aws_subnet.private_1a.id

  vpc_security_group_ids = [
    aws_security_group.web.id
  ]

  root_block_device {
    volume_size = 50
    volume_type = "gp3"
    encrypted   = true
  }

  tags = {
    Name        = "web-server"
    Environment = "production"
  }
}
```

### Review the Code

ops0 highlights potential issues:

Warnings to Address

• Hardcoded AMI IDs (consider using data sources)

• Missing lifecycle rules (prevent accidental destruction)

• Secrets in environment variables (move to Secrets Manager)

---

## Step 5: Import into Terraform State

This is the critical step - importing resources into Terraform state without recreating them.

1. Click **Import to Project**

2. Select the destination IaC project (or create a new one)

3. ops0 runs `terraform import` for each resource

4. View the import log for each resource

### Import Log Example

```
Importing aws_vpc.main...
  terraform import aws_vpc.main vpc-0123456789abcdef
  Import successful!

Importing aws_subnet.private_1a...
  terraform import aws_subnet.private_1a subnet-0123456789abcdef
  Import successful!

Importing aws_instance.web_server...
  terraform import aws_instance.web_server i-0123456789abcdef
  Import successful!

Import complete: 3 resources imported
```

---

## Step 6: Verify No Changes

After import, run a Terraform plan to confirm the code matches reality:

1. Open your IaC project

2. Click **Plan**

3. Verify the output shows **No changes**

### Expected Output

```
No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration
and found no differences, so no changes are needed.
```

If there are differences, ops0's AI can help you fix the Terraform code to match the live resource exactly.

---

## Step 7: Set Up Ongoing Drift Detection

Now that resources are managed, detect future manual changes:

1. Go to your IaC project settings

2. Enable **Drift Detection**

3. Set schedule (e.g., daily at 6 AM)

4. Configure notifications (Slack, email)

When drift is detected, you can:
- View what changed
- Update Terraform to match (accept the manual change)
- Revert to Terraform state (undo the manual change)

---

## Troubleshooting

### Import Fails with "Resource already managed"

The resource is already in another Terraform state. Check:
- Other ops0 projects
- External Terraform workspaces
- Terraform Cloud/Enterprise

### Plan Shows Unexpected Changes

Common causes:

| Issue | Solution |
|-------|----------|
| **Default values** | AWS adds defaults that weren't in original config. Add them explicitly to Terraform. |
| **Computed attributes** | Some attributes are computed. Add `lifecycle { ignore_changes = [...] }`. |
| **Order differences** | Lists may be in different order. Sort them in Terraform. |

### Large Number of Resources

For 100+ resources:
- Import in batches by resource type
- Start with one environment (dev) before production
- Use Discovery filters to focus on specific VPCs or regions

---

## Best Practices

Start with Non-Production

Test the import process on dev/staging before production resources.

Review Produced Code

Don't blindly accept. Review for hardcoded values, missing tags, security issues.

Add Lifecycle Rules

For critical resources, add `prevent_destroy = true` to avoid accidents.

Document the Migration

Keep a log of what was imported and any manual adjustments made.

---

## Next Steps

[Set Up GitOps Sync your imported code with GitHub](https://docs.ops0.com/docs/how-to/setup-gitops)
[Add Security Policies Prevent non-compliant changes going forward](https://docs.ops0.com/docs/how-to/enforce-security-policies)
