iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🤖

Infrastructure Engineers Want to Try Cline Too!

に公開

Motivation

Recently, Cline has been making a lot of noise in the community, and that's what motivated me to try it out.
Since I usually focus on infrastructure rather than application development, I decided to try writing Terraform with Cline.

I wasn't picky about the topic, so I asked it to create a "Basic web application" from the Azure Architecture Center.
https://learn.microsoft.com/ja-jp/azure/architecture/web-apps/app-service/architectures/basic-web-app

Result

I was able to create it.

The models used were as follows, and it generated the code for about 2 dollars.

Mode Model
Plan claude3.7-sonnet
Act gemini-2.0-flash-001*

*I switched to Claude briefly because I hit the rate limit on the free tier.

The folder structure of the generated code looks like this.

What I Did

Creating clinerules

I created clinerules to provide Cline with module separation, naming conventions, test plans, and other guidelines.
These rules were generated by feeding Claude the official Terraform Style Guide and Google Cloud's Best practices for general style and structure.

clinerules
clinerules
# Terraform Coding Practices

## Principles

### DRY Principle (Don't Repeat Yourself)

- Leverage local variables to avoid repetition.
- Abstract common code using modules (Create modules per service, e.g., azurevm, appservice, etc.).
- Use variables and outputs appropriately.
- Use for_each and count to group similar resources.

### Readability and Maintainability

- Consistent code formatting.
- Clear naming conventions.
- Proper comments and documentation.
- Module separation and reuse.

## Implementation Patterns

### File Structure


├── env/                # Environment-specific settings
│   ├── dev/
│   │   └── terraform.tfvars    # Development environment variables
│   ├── test/
│   │   └── terraform.tfvars    # Test environment variables
│   └── prod/
│       └── terraform.tfvars    # Production environment variables
├── main.tf             # Resource definitions
├── variables.tf        # Input variables
├── outputs.tf          # Output variables
├── locals.tf           # Local variables
├── providers.tf        # Provider configuration
├── backend.tf          # Backend configuration
├── terraform.tf        # Settings for Terraform version, etc.
├── README.md           # Project description and usage
└── modules/            # Reusable modules
    └── <module_name>/
        ├── main.tf
        ├── variables.tf
        └── outputs.tf


### Resource Naming

Give resources readable and consistent names. Separate words with underscores and do not include the resource type in the resource identifier.

# Bad Example
resource "azurerm_virtual_network" "vnet-prod-eastus" {...}

# Good Example
resource "azurerm_virtual_network" "sys_vnet" {...}


### Tagging

Always set the following tags on Azure resources. Set an appropriate name for each resource in the Name tag:


resource "azurerm_resource_group" "example" {
  # ...
  tags = {
    Name   = "example-resource-group"  # Appropriate name for each resource
    System = var.system_name
    Env    = var.environment
  }
}

resource "azurerm_virtual_network" "example" {
  # ...
  tags = {
    Name   = "example-vnet"  # Appropriate name for each resource
    System = var.system_name
    Env    = var.environment
  }
}


### Variable Definition

Always include a type and description for all variables. Set appropriate default values for optional variables.

variable "location" {
  type        = string
  description = "The Azure region where resources will be created"
  default     = "eastus"
}

variable "environment" {
  type        = string
  description = "Environment name (dev, test, prod)"
  
  validation {
    condition     = contains(["dev", "test", "prod"], var.environment)
    error_message = "Environment must be one of: dev, test, prod."
  }
}


### Output Variables

Always include a description for output variables.


output "resource_group_id" {
  description = "The ID of the created resource group"
  value       = azurerm_resource_group.main.id
}


### Local Variables

Use local variables to define expressions or values that are used multiple times within the code.


locals {
  name_suffix     = "${var.environment}-${var.location}"
  resource_prefix = "${var.system_name}-${local.name_suffix}"
}


### Secret Management

Never hardcode secret information (passwords, API keys, connection strings, etc.) in the code. Use the following methods:

- Generate random characters during resource construction and store them in Azure Key Vault.


# Bad Example - Hardcoded
resource "azurerm_sql_server" "example" {
  administrator_login_password = "SuperSecret123!"  # Should be avoided
}

# Good Example
resource "azurerm_sql_server" "example" {
  administrator_login_password = var.sql_admin_password  # Pass as a variable
}

### Project-Specific Information
system_name should store "sys".
The resource naming convention should be sys_[environment abbreviation]_[resource abbreviation]_[role].
Please cite resource abbreviations from the following document:
https://learn.microsoft.com/ja-jp/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations

## How to Use Terraform

### AzureRM Provider Configuration


# providers.tf
provider "azurerm" {
  features {}
}

# terraform.tf
Set the version of azurerm to the latest major version.
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0.0"
    }
  }
}


### Using for_each and count

Use for_each or count when creating multiple similar resources.


# Example of for_each
variable "subnets" {
  type = map(object({
    address_prefix = string
    service_endpoints = list(string)
  }))
  default = {
    web = {
      address_prefix = "10.0.1.0/24"
      service_endpoints = ["Microsoft.Web"]
    },
    app = {
      address_prefix = "10.0.2.0/24"
      service_endpoints = ["Microsoft.Sql"]
    },
    data = {
      address_prefix = "10.0.3.0/24"
      service_endpoints = ["Microsoft.Sql", "Microsoft.Storage"]
    }
  }
}

resource "azurerm_subnet" "example" {
  for_each = var.subnets
  
  name                 = each.key
  resource_group_name  = azurerm_resource_group.main.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = [each.value.address_prefix]
  service_endpoints    = each.value.service_endpoints
}


## Module Design

### Basic Module Structure


modules/
└── virtual_machine/
    ├── main.tf       # Resource definitions
    ├── variables.tf  # Input variables
    └── outputs.tf    # Output variables

### Calling Modules

module "web_server" {
  source = "./modules/virtual_machine"
  
  name           = "web"
  vm_size        = "Standard_B2s"
  admin_username = var.admin_username
  location       = var.location
  
  resource_group_name  = azurerm_resource_group.main.name
  subnet_id            = azurerm_subnet.main.id
  
  system_name  = var.system_name
  environment  = var.environment
}

## Code Quality and Workflow

### terraform fmt

Always run `terraform fmt` before committing to maintain consistent code formatting.

### terraform validate

Use `terraform validate` to detect syntax errors and common issues.

### Security Check

Use `checkov` to scan for Infrastructure as Code security and compliance best practices.

checkov -d .

### .gitignore

Exclude the following files from version control:

.terraform/
.terraform.lock.hcl

### Implementation Steps

1. **Start with Design**
   - Identify required resources
   - Plan environment variables and module design

2. **Incremental Implementation**
   - Start with basic infrastructure
   - Add security settings
   - Add application resources

3. **Ensure Code Quality**
   - Format with terraform fmt
   - Validate with terraform validate
   - Security check with checkov
   - Confirm content with terraform plan

4. **Create Documentation**
   - Describe usage in README.md
   - Provide comprehensive variable descriptions
   - Provide examples

Requesting from Cline

After finishing the initial setup of Cline, it was time to execute. I was curious about how vague a request it could handle, so the prompt was very rough.

I provided a screenshot of the architecture diagram for a basic web application and made a casual request to create it accordingly.

(Although I wrote "Speak in Japanese" in the system prompt, it occasionally spoke in English, so I included it in the prompt as well.)

It formulated an execution plan in Plan mode, started writing code when switched to Act, and once the code was written, it even ran terraform init/plan/apply.

Seeing Cline running commands in the local environment and troubleshooting by looking over the project when an error occurred really made me feel like we've entered a new era...!

What Didn't Go Well

While I was amazed by Cline, there were a few points that didn't go as expected, perhaps due to my own lack of "Cline skills." I'll share them here. I would appreciate any comments if you have suggestions for improvement.

It doesn't read clinerules properly

After submitting the initial prompt, it reads clinerules as shown below.

Then, the results of the plan were displayed, but the file structure was different from what was in clinerules...
The modules weren't separated either...

In addition, despite instructions to generate secrets randomly and store them in Key Vault, it asked for information from the user...

However, I was able to improve the situation by pointing out the incorrect parts and instructing it to re-read them properly.
I will continue to explore whether there are ways to write rules with more mandatory force.

Unable to intelligently incorporate checkov results

I've set it up to check the quality of the generated code with checkov.
This time, about 20 items failed, and Cline proactively went about fixing those failures.
I was watching, thinking "That's impressive," but upon closer look, it was trying to create a Private Endpoint even though the architecture didn't have a Vnet, or it was blocking public access indiscriminately...
This might be related to the performance of the model used in Act mode, but I would be happy if it could behave in a way where it understands the architecture, makes an initial judgment on whether to incorporate the findings, and then asks for direction.

Summary

These were my thoughts after trying out Cline for the first time.
As for the positive aspects, its ability to generate reasonably functional code from vague instructions and even detect and fix errors lived up to its reputation, and I truly felt its potential!

On the other hand, there were areas where there is still room for improvement, such as adherence to clinerules and the appropriate incorporation of checkov results.
I plan to experiment with prompts and clinerules to explore ways to set more mandatory rules and create mechanisms that allow for judgments based on an understanding of the architecture.

Discussion