# Manage your Mikrotik with Terraform

In this article I’m going to show a simple example (specific to homelabers) to manage their networking via TF, why? well because winbox is cool but each change we do you probably create a backup right? well I’m least thats me.

So let’s integrate our changes via TF, at least locally not with the idea of GitOps (for now).

## Requirements

* RouterOS v7.1beta4 or newer
    
* Another user with `write` permissions
    
* A self signed cert for the `www-ssl` service and `root`
    
* The service www-ssl enabled with a cert
    

## Certificates

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1750036148827/6a151ac2-fe1e-4d75-85a4-789b0b398ca7.png align="center")

In case you are on that version, you don’t need to create the `root` one since it comes built in.

To create the `www-ssl` one let’s do the following, first ssh into your router or login via winbox, in the terminal type

```bash
/certificate
```

Inside there let’s do

```bash
/certificate> add name=root-cert common-name=root-cert key-usage=key-cert-sign,crl-sign
/certificate> add name=https-cert common-name=https-cert
```

With that we got our certificates created, now we need to sign them. Let’s do the following

```bash
/certificate> sign https-cert
  progress: done

/certificate> sign root-cert
  progress: done
```

Now let’s enable the web with `ssl`

```bash
/ip service
set www-ssl certificate=https-cert disabled=no
```

## Terraform

Now it’s time to set up our terraform environment, about the provider we are going to use is this [https://registry.terraform.io/providers/terraform-routeros/routeros/latest](https://registry.terraform.io/providers/terraform-routeros/routeros/latest)

### Folder structure

In my case I like to build them like this (at least when is one environment)

```bash
🌳 terraform/
┣ 📁 config/
┃ ┣ 📄 .gitkeep
┃ ┗ 📄 router.tfvars
┣ 📁 router/
┃ ┣ 📄 provider.tf
┃ ┣ 📄 resources.tf
┃ ┗ 📄 variables.tf
┣ 📄 backend.tf
┣ 📄 resources.tf
┗ 📄 versions.tf
```

In the `resources` at root level there is something like this

```yaml
variable "ROS_HOSTURL" {}
variable "ROS_USERNAME" {}
variable "ROS_PASSWORD" {}

module "router" {
  source       = "./router"
  ROS_HOSTURL  = var.ROS_HOSTURL
  ROS_USERNAME = var.ROS_USERNAME
  ROS_PASSWORD = var.ROS_PASSWORD
}
```

Since I’m using modules I create the variables that get injected at execution time via environment variables

Before doing any king of action remember to create your `tf.vars` file

The one used in this example has this structure

```bash
ROS_HOSTURL="https://<IP_MIKROTIK>"
ROS_USERNAME="<USER_WE_CREATED_BEFORE_WITH_WRITE_ACCESS>"
ROS_PASSWORD="<USER_PASSWORD>"
```

Running something like

```bash
terraform <action> -var-file=config/router.tfvars --auto-approve;
```

Inside the `router` module we would need the provider which it’s something like this

```bash
terraform {
  required_providers {
    routeros = {
      source = "terraform-routeros/routeros"
    }
  }
}

provider "routeros" {
  hosturl  = var.ROS_HOSTURL
  username = var.ROS_USERNAME
  password = var.ROS_PASSWORD
  insecure = true # this is needed because routeros is using self-signed certificates
}
```

In my case in `resources` Im just creating DNS so it’s quite simple

```bash
locals {
  ros_subdomains = [
    "...",
    "...",
    "...",
  ]
}

resource "routeros_dns_record" "subdomains" {
  for_each = toset(local.ros_subdomains)
  name     = "${each.key}.${var.ROS_BASE_DOMAIN}"
  cname    = var.ROS_BASE_DOMAIN
  type     = "CNAME"
  comment  = var.ROS_COMMENT_DEFAULT
}
```

And for the variables I’m using are this

```bash
variable "ROS_HOSTURL" {
  description = "RouterOS host URL"
  type        = string
  default     = "http://..."
}

variable "ROS_USERNAME" {
  description = "RouterOS username"
  type        = string
  default     = ""
  sensitive   = true
}

variable "ROS_PASSWORD" {
  description = "RouterOS password"
  type        = string
  default     = ""
  sensitive   = true
}

variable "ROS_BASE_DOMAIN" {
  description = "Base domain for DNS records"
  type        = string
  default     = "internal.jonathan.com.ar"
}

variable "ROS_COMMENT_DEFAULT" {
  description = "Default comment for RouterOS resources"
  type        = string
  default     = "Managed by Terraform"
}
```

I’ll like to add a comment on each resource managed via TF so I’ll remember later to not modify them manually.

Now we could `init` the project

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1750037232541/5d814806-e798-47df-bc03-24f90a51899f.png align="center")

Now I'm going to add a new DNS just to update my state and run a `terraform plan`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1750037785350/7539fba4-0ee5-4bbb-b265-58a924ce5e0b.png align="center")

Now if I run `terraform apply` this would create the entry in my router.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1750037626952/7e200499-77b1-46f4-95e0-aad23e19e713.png align="center")

We can check it out

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1750037681642/c4276112-732f-4733-a4b6-82e6373523eb.png align="center")

And that’s it!

We got our terraform working with RouterOS :)

### Destruction

In case you want to destroy everything you can just run `terraform destroy`
