[Yandex Cloud documentation](../../../index.md) > [Tutorials](../../index.md) > Basic infrastructure > Network > [Routing through a NAT instance](index.md) > Terraform

# Routing through a NAT instance using Terraform


To set up [routing through a NAT instance](index.md) using Terraform:

1. [Get your cloud ready](#before-you-begin).
1. [Create an infrastructure](#deploy).
1. [Test the NAT instance](#test).

If you no longer need the resources you created, [delete them](#clear-out).

## Getting started {#before-you-begin}

Sign up for Yandex Cloud and create a [billing account](../../../billing/concepts/billing-account.md):
1. Navigate to the [management console](https://console.yandex.cloud) and log in to Yandex Cloud or create a new account.
1. On the **[Yandex Cloud Billing](https://center.yandex.cloud/billing/accounts)** page, make sure you have a billing account linked and it has the `ACTIVE` or `TRIAL_ACTIVE` [status](../../../billing/concepts/billing-account-statuses.md). If you do not have a billing account, [create one](../../../billing/quickstart/index.md) and [link](../../../billing/operations/pin-cloud.md) a cloud to it.

If you have an active billing account, you can create or select a [folder](../../../resource-manager/concepts/resources-hierarchy.md#folder) for your infrastructure on the [cloud page](https://console.yandex.cloud/cloud).

[Learn more about clouds and folders here](../../../resource-manager/concepts/resources-hierarchy.md).


### Required paid resources {#paid-resources}

The cost of NAT instance support includes:

* Fee for continuously running VMs (see [Yandex Compute Cloud pricing](../../../compute/pricing.md)).
* Fee for using a dynamic or static external IP address (see [Yandex Virtual Private Cloud pricing](../../../vpc/pricing.md)).


## Create your infrastructure {#deploy}

With [Terraform](https://www.terraform.io/), you can quickly create a cloud infrastructure in Yandex Cloud and manage it using configuration files. These files store the infrastructure description written in HashiCorp Configuration Language (HCL). If you change the configuration files, Terraform automatically detects which part of your configuration is already deployed, and what should be added or removed.

Terraform is distributed under the [Business Source License](https://github.com/hashicorp/terraform/blob/main/LICENSE). The [Yandex Cloud provider for Terraform](https://github.com/yandex-cloud/terraform-provider-yandex) is distributed under the [MPL-2.0](https://www.mozilla.org/en-US/MPL/2.0/) license.

For more information about the provider resources, see the relevant documentation on the [Terraform](https://www.terraform.io/docs/providers/yandex/index.html) website or [its mirror](../../../terraform/index.md).

To create an infrastructure with Terraform:

1. [Install Terraform](../../infrastructure-management/terraform-quickstart.md#install-terraform), [get the authentication credentials](../../infrastructure-management/terraform-quickstart.md#get-credentials), and specify the Yandex Cloud provider source (see [Configure your provider](../../infrastructure-management/terraform-quickstart.md#configure-provider), Step 1).
1. Set up your infrastructure description file:

    {% list tabs group=infrastructure_description %}

    - Ready-made configuration {#ready}

      1. Clone the configuration file repository:

         ```bash
         git clone https://github.com/yandex-cloud-examples/yc-compute-nat-instance.git
         ```

      1. Navigate to the repository directory. It should now contain the following files:
         * `nat-instance.tf`: New infrastructure configuration.
         * `nat-instance.auto.tfvars`: User data.

    - Manually {#manual}

      1. Create a folder for the infrastructure description file.
      1. In this folder, create the `nat-instance.tf` configuration file:

          {% cut "nat-instance.tf" %}

          ```hcl
          # Declaring variables for custom parameters
          
          variable "folder_id" {
            type = string
          }
          
          variable "vm_user" {
            type = string
          }
          
          variable "vm_user_nat" {
            type = string
          }
          
          variable "ssh_key_path" {
            type = string
          }
          
          # Adding other variables
          
          locals {
            network_name     = "my-vpc"
            subnet_name1     = "public-subnet"
            subnet_name2     = "private-subnet"
            sg_nat_name      = "nat-instance-sg"
            vm_test_name     = "test-vm"
            vm_nat_name      = "nat-instance"
            route_table_name = "nat-instance-route"
          }
          
          # Configuring the provider
          
          terraform {
            required_providers {
              yandex = {
                source  = "yandex-cloud/yandex"
                version = ">= 0.47.0"
              }
            }
          }
          
          provider "yandex" {
            folder_id = var.folder_id
          }
          
          # Creating a cloud network
          
          resource "yandex_vpc_network" "my-vpc" {
            name = local.network_name
          }
          
          # Creating subnets
          
          resource "yandex_vpc_subnet" "public-subnet" {
            name           = local.subnet_name1
            zone           = "ru-central1-a"
            network_id     = yandex_vpc_network.my-vpc.id
            v4_cidr_blocks = ["192.168.1.0/24"]
          }
          
          resource "yandex_vpc_subnet" "private-subnet" {
            name           = local.subnet_name2
            zone           = "ru-central1-a"
            network_id     = yandex_vpc_network.my-vpc.id
            v4_cidr_blocks = ["192.168.2.0/24"]
            route_table_id = yandex_vpc_route_table.nat-instance-route.id
          }
          
          # Creating a security group
          
          resource "yandex_vpc_security_group" "nat-instance-sg" {
            name       = local.sg_nat_name
            network_id = yandex_vpc_network.my-vpc.id
          
            egress {
              protocol       = "ANY"
              description    = "any"
              v4_cidr_blocks = ["0.0.0.0/0"]
              from_port      = 0
              to_port        = 65535
            }
          
            ingress {
              protocol       = "TCP"
              description    = "ssh"
              v4_cidr_blocks = ["0.0.0.0/0"]
              port           = 22
            }
          
            ingress {
              protocol       = "TCP"
              description    = "ext-http"
              v4_cidr_blocks = ["0.0.0.0/0"]
              port           = 80
            }
          
            ingress {
              protocol       = "TCP"
              description    = "ext-https"
              v4_cidr_blocks = ["0.0.0.0/0"]
              port           = 443
            }
          }
          
          # Adding a prebuilt VM image
          
          resource "yandex_compute_image" "ubuntu-1804-lts" {
            source_family = "ubuntu-1804-lts"
          }
          
          resource "yandex_compute_image" "nat-instance-ubuntu" {
            source_family = "nat-instance-ubuntu"
          }
          
          # Creating boot disks
          
          resource "yandex_compute_disk" "boot-disk-ubuntu" {
            name     = "boot-disk-ubuntu"
            type     = "network-hdd"
            zone     = "ru-central1-a"
            size     = "20"
            image_id = yandex_compute_image.ubuntu-1804-lts.id
          }
          
          resource "yandex_compute_disk" "boot-disk-nat" {
            name     = "boot-disk-nat"
            type     = "network-hdd"
            zone     = "ru-central1-a"
            size     = "20"
            image_id = yandex_compute_image.nat-instance-ubuntu.id
          }
          
          # Creating a VM
          
          resource "yandex_compute_instance" "test-vm" {
            name        = local.vm_test_name
            platform_id = "standard-v3"
            zone        = "ru-central1-a"
          
            resources {
              core_fraction = 20
              cores         = 2
              memory        = 2
            }
          
            boot_disk {
              disk_id = yandex_compute_disk.boot-disk-ubuntu.id
            }
          
            network_interface {
              subnet_id          = yandex_vpc_subnet.private-subnet.id
              security_group_ids = [yandex_vpc_security_group.nat-instance-sg.id]
            }
          
            metadata = {
              user-data = "#cloud-config\nusers:\n  - name: ${var.vm_user}\n    groups: sudo\n    shell: /bin/bash\n    sudo: 'ALL=(ALL) NOPASSWD:ALL'\n    ssh_authorized_keys:\n      - ${file("${var.ssh_key_path}")}"
            }
          }
          
          # Creating an NAT instance
          
          resource "yandex_compute_instance" "nat-instance" {
            name        = local.vm_nat_name
            platform_id = "standard-v3"
            zone        = "ru-central1-a"
          
            resources {
              core_fraction = 20
              cores         = 2
              memory        = 2
            }
          
            boot_disk {
              disk_id = yandex_compute_disk.boot-disk-nat.id
            }
          
            network_interface {
              subnet_id          = yandex_vpc_subnet.public-subnet.id
              security_group_ids = [yandex_vpc_security_group.nat-instance-sg.id]
              nat                = true
            }
          
            metadata = {
              user-data = "#cloud-config\nusers:\n  - name: ${var.vm_user_nat}\n    groups: sudo\n    shell: /bin/bash\n    sudo: 'ALL=(ALL) NOPASSWD:ALL'\n    ssh_authorized_keys:\n      - ${file("${var.ssh_key_path}")}"
            }
          }
          
          # Creating a route table and static route
          
          resource "yandex_vpc_route_table" "nat-instance-route" {
            name       = "nat-instance-route"
            network_id = yandex_vpc_network.my-vpc.id
            static_route {
              destination_prefix = "0.0.0.0/0"
              next_hop_address   = yandex_compute_instance.nat-instance.network_interface.0.ip_address
            }
          }
          ```

          {% endcut %}

      1. Create the `nat-instance.auto.tfvars` user data file:

          {% cut "nat-instance.auto.tfvars" %}

          ```hcl
          folder_id    = "<folder_ID>"
          vm_user      = "<VM_username>"
          vm_user_nat  = "<NAT_instance_username>"
          ssh_key_path = "<path_to_public_SSH_key>"
          ```

          {% endcut %}

    {% endlist %}

    For more information about Terraform resource properties, see the relevant provider guides:

    * [Network](../../../vpc/concepts/network.md#network): [yandex_vpc_network](../../../terraform/resources/vpc_network.md).
    * [Subnets](../../../vpc/concepts/network.md#subnet): [yandex_vpc_subnet](../../../terraform/resources/vpc_subnet.md).
    * [Security groups](../../../vpc/concepts/security-groups.md): [yandex_vpc_security_group](../../../terraform/resources/vpc_security_group.md).
    * [Image](../../../compute/concepts/image.md): [yandex_compute_image](../../../terraform/resources/compute_image.md).
    * [Disk](../../../compute/concepts/disk.md): [yandex_compute_disk](../../../terraform/resources/compute_disk.md).
    * [VM instance](../../../compute/concepts/vm.md): [yandex_compute_instance](../../../terraform/resources/compute_instance.md).
    * [Route table](../../../vpc/concepts/routing.md#rt-vpc): [yandex_vpc_route_table](../../../terraform/resources/vpc_route_table.md).

1. In the `nat-instance.auto.tfvars` file, set the following user-defined properties:

    * `folder_id`: [Folder ID](../../../resource-manager/operations/folder/get-id.md).
    * `vm_user`: VM user name.
    * `vm_user_nat`: NAT instance username.
    * `ssh_key_path`: Path to the public SSH key to authenticate the user on the VM. For more information, see [Creating an SSH key pair](../../../compute/operations/vm-connect/ssh.md#creating-ssh-keys).

1. Create the required resources:

    1. In the terminal, navigate to the configuration file directory.
    1. Make sure the configuration is correct using this command:
    
       ```bash
       terraform validate
       ```
    
       If the configuration is valid, you will get this message:
    
       ```bash
       Success! The configuration is valid.
       ```
    
    1. Run this command:
    
       ```bash
       terraform plan
       ```
    
       You will see a list of resources and their properties. No changes will be made at this step. Terraform will show any errors in the configuration.
    1. Apply the configuration changes:
    
       ```bash
       terraform apply
       ```
    
    1. Type `yes` and press **Enter** to confirm the changes.

After you create the infrastructure, [test the NAT instance](#test).

## Test the NAT gateway instance {#test}

1. [Connect](../../../compute/operations/vm-connect/ssh.md#vm-connect) to the VM via a private IP address, using the NAT instance as a jump host:

    ```bash
    ssh -J <NAT_instance_username>@<NAT_instance_public_IP_address> \
      <VM_user_name>@<VM_internal_IP_address>
    ```

    You can also connect to the test VM using the standard input/output redirection (`-W` flag) to forward the connection through a NAT instance:

    ```bash
    ssh -o ProxyCommand="ssh -i <NAT_key_file_path/name> -W %h:%p <NAT_username>@<NAT_public_IP_address>" \
      -i <VM_key_file_path/name> <VM_user_name>@<VM_internal_IP_address>
    ```

    Use this command for connection in the following cases:

    * Your VM is running an OpenSSH version below 7.3.
    * Your SSH keys are stored outside the default directory or have non-standard names.

1. Type **yes** to connect to the NAT instance and re-enter **yes** to connect to the test VM.

    {% note info %}

    When you type **yes**, the command may not be displayed in the terminal, but it will run anyway.

    {% endnote %}

1. Make sure the test VM is connected to the internet via the public IP address of the NAT instance. Run this command:

    ```bash
    curl ifconfig.co
    ```

    If it returns the public IP address of the NAT instance, the configuration is correct.


## How to delete the resources you created {#clear-out}

To stop incurring charges for the resources you created:

1. Open the `nat-instance.tf` file and delete your infrastructure description from it.
1. Apply the changes:

    1. In the terminal, navigate to the configuration file directory.
    1. Make sure the configuration is correct using this command:
    
       ```bash
       terraform validate
       ```
    
       If the configuration is valid, you will get this message:
    
       ```bash
       Success! The configuration is valid.
       ```
    
    1. Run this command:
    
       ```bash
       terraform plan
       ```
    
       You will see a list of resources and their properties. No changes will be made at this step. Terraform will show any errors in the configuration.
    1. Apply the configuration changes:
    
       ```bash
       terraform apply
       ```
    
    1. Type `yes` and press **Enter** to confirm the changes.

#### See also {#see-also}

* [Routing through a NAT instance using the management console](console.md)