# Your First IaC Deployment

Source: https://docs.ops0.com/docs/how-to/first-iac-deployment

Create an IaC project, write Terraform, run a plan, get approval, and deploy — all from the ops0 editor.

---

## Scenario

You want to deploy a new piece of infrastructure (an S3 bucket, EC2 instance, or any cloud resource) using Terraform through ops0, with a full audit trail and optional approval step.

---

## Prerequisites

- AWS (or GCP/Azure) integration connected with **deploy permissions**

- State backend ready — an S3 bucket (AWS), GCS bucket (GCP), or Azure Storage container for storing Terraform state

---

## Step 1: Create a New IaC Project

1. Click **Infrastructure as Code** in the left sidebar

2. Click **New Project**

3. Enter a **project name** (e.g. `my-first-project`) and optional description

4. Select **Terraform** or **OpenTofu** as your IaC type

5. Click **Create**

---

## Step 2: Configure the State Backend

The state backend is where Terraform stores the record of what it has deployed. This is required before your first deployment.

1. In the project, click **Settings → Backend**

2. Select your backend type

**AWS S3 backend (recommended for AWS):**

| Field | Example |
|-------|---------|
| Bucket | `my-terraform-state` |
| Key | `my-first-project/terraform.tfstate` |
| Region | `us-east-1` |
| DynamoDB Table | `terraform-locks` (optional, for state locking) |

ops0 generates the `backend.tf` block automatically from these values.

---

## Step 3: Link Your Cloud Integration

1. Click **Settings → Cloud Integration**

2. Select the AWS/GCP/Azure integration to deploy with

3. Save

This tells ops0 which cloud credentials to use when running `terraform plan` and `terraform apply`.

---

## Step 4: Write Your Infrastructure Code

The project opens the Monaco editor — the same editor used in VS Code, with full Terraform HCL syntax highlighting, autocomplete, and validation.

You can write code manually or ask the AI assistant to generate it.

**Manual — add a new file:**

Click **New File**, name it `main.tf`, and write your Terraform:

```hcl
resource "aws_s3_bucket" "example" {
  bucket = "my-ops0-tutorial-bucket"

  tags = {
    Environment = "tutorial"
    ManagedBy   = "ops0"
  }
}

resource "aws_s3_bucket_versioning" "example" {
  bucket = aws_s3_bucket.example.id

  versioning_configuration {
    status = "Enabled"
  }
}
```

**AI-assisted — ask the assistant:**

Type in the chat panel: *"Create an S3 bucket with versioning enabled and a lifecycle rule that transitions objects to Glacier after 90 days"* — the AI generates the code and adds it to the editor.

---

## Step 5: Run a Plan

The plan shows what Terraform will create, modify, or destroy — before touching anything.

1. Click **Deploy** in the toolbar

2. Select your target region (for AWS)

3. Click **Run Plan**

ops0 runs `terraform init` then `terraform plan` in the background. The output streams in real time:

```
Terraform will perform the following actions:

  # aws_s3_bucket.example will be created
  + resource "aws_s3_bucket" "example" {
      + bucket = "my-ops0-tutorial-bucket"
      + tags   = { "Environment" = "tutorial" }
    }

  # aws_s3_bucket_versioning.example will be created
  + resource "aws_s3_bucket_versioning" "example" {
      + bucket = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.
```

Review the plan output carefully. Confirm that only the resources you expect are listed.

---

## Step 6: Approve and Apply

After the plan completes, the deployment moves to **Awaiting Approval**.

1. Review the plan output one more time

2. Click **Approve & Apply**

3. Watch the apply output stream in real time

The apply output shows Terraform creating each resource:

```
aws_s3_bucket.example: Creating...
aws_s3_bucket.example: Creation complete after 3s [id=my-ops0-tutorial-bucket]
aws_s3_bucket_versioning.example: Creating...
aws_s3_bucket_versioning.example: Creation complete after 1s

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
```

---

## Verification

After the apply completes:

1. The deployment status shows **Succeeded**
2. Go to your AWS console → S3 — the bucket exists
3. Back in ops0, run another **Plan** — it should show **0 changes** (state matches infrastructure)

A clean second plan confirms ops0 is accurately tracking the deployed state.

---

## Next Steps

[Set up GitOps → Route all infrastructure changes through pull requests](https://docs.ops0.com/docs/how-to/setup-gitops)
[Add compliance policies → Block non-compliant deployments before they apply](https://docs.ops0.com/docs/how-to/policies-and-compliance)

---

## Troubleshooting

Plan fails: "No valid credential sources found"

The cloud integration is not linked to this project. Go to Settings → Cloud Integration and select your AWS/GCP/Azure integration.

Plan fails: backend initialization error

Check that your state backend bucket exists and the integration has read/write access to it. For S3, the IAM role needs `s3:GetObject`, `s3:PutObject`, and `s3:ListBucket` on the state bucket.

Apply blocked by policy

A policy violation is preventing the apply. Review the violation details shown in the deployment output and either fix the code to comply or ask an admin to update the policy rule.
