Terraform for dummies Part1 : Launch an instance with a static website on OCI
Tags: tutorial, terraform, oraclecloud, oci
Published: 2023-06-18 13:36:19

---
title: Terraform for dummies Part1 : Launch an instance with a static website on OCI
published: true
date: 2023-06-10 00:44:18 UTC
tags: tutorial,terraform,oraclecloud,oci
canonical_url: 
---

### Terraform for dummies Part1 : Launch an instance with a static website on OCI

### Intro

Terraform brings a new paradigm where Infrastructure becomes a Code, and with Cloud becoming what it is today, everyone is invited at the (devops) table. Therefore, after provisioning with oci-cli in my previous[BlogPost](https://brokedba.blogspot.com/2020/06/launch-oci-instance-with-oci-cli-in-10.html), I will explore the same task using terraform.To add more fun, we won’t just deploy an instance but also configure a website linked to its public IP.  
 **Note** This lab will also help you practice if you are preparing for[OCI Operations Associate exam(1Z0–1067)](https://www.oracle.com/cloud/iaas/training/operations.html) .

### Topology

The following illustration shows the layers involved between your workstation an Oracle cloud infrastructure while running the terraform commands along with the instance attributes we will be provisioning .

![](https://cdn-images-1.medium.com/max/853/0*7jjncmJgjYmBtj96.png)

Besides describing my [GitHub repo](https://github.com/brokedba/terraform-examples) before starting this tutorial, I’ll just briefly discuss some principles.

- **Infrastructure As Code** Manages and provisions cloud resources using a declarative code (i.e Terraform) and definition files avoiding interactive configuration. Terraform is an immutable Orchestrator that creates and deletes all resources in the proper sequence.Each Cloud vendor has what we call a provider that terraform uses in order to convert declarative texts into API calls reaching the Cloud infrastructure layer.
- **Terraform Files**

- Can be a single file or split into multiple **tf** or **tf.json** files, any other file extension is ignored  
- Files are merged in alphabetical order but resource definition order doesn’t matter (subfolders are not read)  
- Common configurations have 3 type of **tf** files and a statefile

- **Terraform resource declaration syntax** looks like this:

```
Component "Provider_Resource_type" "MyResource_Name" { Attribute1 = value ..
                                                       Attribute2 = value ..}
```

**Where the hell do I find a good deployment sample**?  
The most important thing when learning a new program is accomplishing your first HelloWorld. Unfortunately, google can’t always make the cut as samples I used had errors. Luckily, OCI Resource Manager had some samples I managed to export and tweak which was a good starting point for this lab.

**Terraform lab content:** I have deliberately split this lab in 2 :

- [**VCN Deployment**](https://brokedba.blogspot.com/2020/07/terraform-for-dummies-launch-instance.html#partial): To grasp the basics of a single resource deployment
- [**FULL Instance Deployment**](https://brokedba.blogspot.com/2020/07/terraform-for-dummies-launch-instance.html#full): which is a more complex deployment (instance provisioning with a hosted web sever).

### I.Terraform setup

Although I’m on windows I tried the lab using [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) (Ubuntu) terminal (but same applies to Mac).

- **Windows:** Download and run the installer from their [website](https://www.terraform.io/downloads.html) ([32-bit](https://releases.hashicorp.com/terraform/0.12.28/terraform_0.12.28_windows_386.zip) ,[64-bit](https://releases.hashicorp.com/terraform/0.12.28/terraform_0.12.28_windows_amd64.zip))
- **Linux:** Download, unzip and move the binary to the local bin directory

```
$ wget https://releases.hashicorp.com/terraform/1.0.3/terraform_1.0.3_linux_amd64.zip 
$ unzip terraform_1.0.3_linux_amd64.zip 
$ mv terraform /usr/local/bin/
```

- Once installed run the version command to validate your installation

```
$ terraform --version
  Terraform v1.0.3
```

**OCI API Key based authentication**

- Tenancy\_ocid, Compartment\_ocid, user\_ocid and the region
- The private API key path and its fingerprint to authenticate with your tenancy account
- The SSH key pair (Private/Public) required when launching the new compute instance

**Assumptions**

- _Terraform_ shares most of the authentication parameters with oci-cli (located in ~/.oci/config ). Please refer to my [Other post](https://brokedba.blogspot.com/2020/05/install-oci-cli-in-5-minutes.html) for details on how to setup oci-cli if it isn’t done yet.
- However, terraform also allows using environment variables to define these parameters. This is why I will be using a shell script that sets them before the deployment (I still needed oci-cli for API keys).

### II. Clone the repository

- Pick an area that is close to your oci-terraform directory on your file system and issue the following command

```
$ git clone https://github.com/brokedba/terraform-examples.git
```

**Note:** As explained earlier you will find 2 directories inside the repository which will make things easier:

1. [**terraform-provider-oci/create-vcn/**](https://github.com/brokedba/terraform-examples/tree/master/terraform-provider-oci/create-vcn) To grasp how we deploy a VCN.
2. [**terraform-provider-oci/launch-instance/**](https://github.com/brokedba/terraform-examples/tree/master/terraform-provider-oci/launch-instance) For the full instance deploy, once comfortable with terraform.

### III. Provider setup

1. **INSTALL AND SETUP THE OCI PROVIDER**

- Cd Into the subdirectory **terraform-provider-oci/create-vcn** where our configuration resides (i.e vcn )

```
$ cd ~/terraform-examples/terraform-provider-oci/create-vcn
```

OCI provider plugin is distributed by HashiCorp hence it will be automatically installed by **terraform init**.

```
$ terraform init
  Initializing the backend...

  Initializing provider plugins...
  - Checking for available provider plugins...
  - Downloading plugin for provider "oci" (hashicorp/oci) 3.83.1...
  * provider.oci: version = "~> 3.83"

$ terraform --version
  Terraform v0.12.24
  + provider.oci v3.83.1 ---> the privider is now installed
```

Let’s see what’s in the directory. Here, only files matter along with (click to see content)

```
$ tree
  .
  |-- env-vars ---> TF_environment_variables needed to authenticate to OCI 
  |-- outputs.tf ---> displays the resources detail at the end of the deploy
  |-- schema.yaml ---> Contains the stack (variables) description    
  |-- variables.tf ---> Resource variables needed for the deploy   
  `-- vcn.tf ---> Our vcn terraform declaration code (configuration) 
```

Adjust the required authentication parameters in file according to your tenancy and key pairs .

```
$ vi env-vars 

  export TF_VAR_tenancy_ocid="ocid1.tenancy.oc1..aaaaaaaa" # change me 
  export TF_VAR_user_ocid="ocid1.user.oc1..aaaaaaaa" # change me 
  export TF_VAR_compartment_ocid="ocid1.tenancy.oc1..aaaaaaaa" # change me 
  export TF_VAR_fingerprint=$(cat PATH_To_Fing/oci_api_key_fingerprint)# change me 
  export TF_VAR_private_key_path=PATH_To_APIKEY/oci_api_key.pem # change me 
  export TF_VAR_ssh_public_key=$(cat PATH_To_PublicSSH/id_rsa.pub) # change me 
  export TF_VAR_ssh_private_key=$(cat PATH_To_PrivateSSH/id_rsa) # change me 
  export TF_VAR_region="ca-toronto-1" # change me 
  $ . env-vars
```

### IV. Partial Deployment

#### DEPLOY A SIMPLE VCN

- Now that **env-vars** values are set and sourced, we can run **terraform plan** command to create an execution plan (quick dry run to check the desired state/actions )

```
$ terraform plan
   Refreshing Terraform state in-memory prior to plan... 
  ------------------------------------------------------------------------
  An execution plan has been generated and is shown below.
    Terraform will perform the following actions:

    # oci_core_default_route_table.rt will be created
    + resource "oci_core_default_route_table" "rt" 
    {..}
    # oci_core_internet_gateway.gtw will be created
    + resource "oci_core_internet_gateway" "gtw" 
    {..}
       
    # oci_core_security_list.terra_sl will be created
    + resource "oci_core_security_list" "terra_sl" {
        + egress_security_rules {..}
        + ingress_security_rules {..
            + tcp_options {+ max = 22 + min = 22}}
        + ingress_security_rules {..
             + tcp_options { + max = 80 + min = 80}}
     }

    # oci_core_subnet.terrasub[0] will be created
    + resource "oci_core_subnet" "terrasub" {
        + availability_domain = "BahF:CA-TORONTO-1-AD-1"
        + cidr_block = "192.168.78.0/24"
        ...}

    # oci_core_vcn.vcnterra will be created
    + resource "oci_core_vcn" "vcnterra" {
        + cidr_block = "192.168.64.0/20"
        ...}

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

- The output being too verbose I deliberately kept only relevant attributes   
 for each VCN component

- Next, we can finally run **terraform deploy** to apply the changes required to create our VCN ( listed in the plan )

```
 $ terraform apply -auto-approve
oci_core_vcn.vcnterra: Creating...
...
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.

Outputs:

default_dhcp_options_id = ocid1.dhcpoptions.oc1.ca-toronto-1.aaaaaaaaasxxxx
default_route_table_id = ocid1.routetable.oc1.ca-toronto-1.aaaaaaaaaxxx
default_security_list_id = ocid1.securitylist.oc1.ca-toronto-1.aaaaaaaaxx
internet_gateway_id = ocid1.internetgateway.oc1.ca-toronto-1.aaaaaaaaxxxx
subnet_ids = ["ocid1.subnet.oc1.ca-toronto-1.aaaaaaaaxxx,]
vcn_id = ocid1.vcn.oc1.ca-toronto-1.amaaaaaaaxxx
```

![](https://cdn-images-1.medium.com/max/1024/1*qBT4G2rRVwZ-vqbKJwVe0w.png)

**Observations :**

- The deploy started by loading the resources variables in [variables.tf](https://github.com/brokedba/terraform-examples/blob/master/terraform-provider-oci/create-vcn/variables.tf) which allowed the execution of [vcn.tf](https://github.com/brokedba/terraform-examples/blob/master/terraform-provider-oci/create-vcn/vcn.tf)  
- Finally terraform fetched the variables (ocids) of the resources listed in [outputs.tf](https://github.com/brokedba/terraform-examples/blob/master/terraform-provider-oci/create-vcn/outputs.tf) (lookup)

**Note**  : In order to continue the lab we will need to destroy the vcn as the full instance launch will recreate it.

```
$ terraform destroy -auto-approve

Destroy complete! Resources: 5 destroyed.
```

### V. Full deployment (Instance)

1. **OVERVIEW**

- Awesome, After our small test let’s launch a full instance from scratch .
- First we need to switch to the second directory [**terraform-provider-oci/launch-instance/**](https://github.com/brokedba/terraform-examples/tree/master/terraform-provider-oci/launch-instance)  
Here's its content:

```
$ tree ./terraform-provider-oci/launch-instance
.
|-- cloud-init ---> SubFolder
| `--> vm.cloud-config ---> script to install a web server & add a Webpage at startup
|-- compute.tf ---> Instance related terraform configuration
|-- env-vars ---> authentication envirment variables
|-- outputs.tf ---> displays the resources detail at the end of the deploy
|-- schema.yaml ---> Containes the stack (variables)
|-- variables.tf ---> Resource variables needed for the deploy   
|-- vcn.tf ---> same vcn terraform declaration
```

**Note:** As you can see we have 2 additional files and one Subfolder.   
 **compute.tf** is where the compute instance and all its attributes are declared. All the other tf files come from my vcn example with some additions for variables.tf and output.tf

- **Cloud-init**  : is a cloud instance initialization method that executes tasks upon instance startup by providing the **user\_data** entry in the **metadata** block of the Terraform oci\_core\_instance resource definition (See below).

```
$ vi compute.tf
resource "oci_core_instance" "terra_inst" {
...
metadata = {
  ssh_authorized_keys = file("../../.ssh/id_rsa.pub") ---> Upload sshkey
  user_data = base64encode(file("./cloud-init/vm.cloud-config")) ---> Run tasks 
      }      
...
```

- In my lab, I used **cloud-init** to install nginx and write an html page that will be the server’s HomePage at startup.

**2. LAUNCH THE INSTANCE**

- Once in the **launch-instance** directory make sure you copied the adjusted file and sourced it (see ). You can then run the plan command (output is truncated for more visibility)

```
$ terraform plan
   Refreshing Terraform state in-memory prior to plan... 
  ------------------------------------------------------------------------
  An execution plan has been generated and is shown below.
    Terraform will perform the following actions:

  ... # VCN declaration 
  # oci_core_instance.terra_inst will be created
  + resource "oci_core_instance" " terra_inst" {
      + ...
      + defined_tags = (known after apply)
      + display_name = "TerraCompute"
      + metadata = {
         + "ssh_authorized_keys" =...
         + "user_data" = " ...
      + shape = "VM.Standard.E2.1.Micro"
      + ...
      + create_vnic_details {
      + hostname_label = "terrahost"
      + private_ip = "192.168.78.51"
      ..}
      + source_details {
          + boot_volume_size_in_gbs = "50"
          + source_type = "image"
           ..}
   # oci_core_volume.terra_vol will be created
   + resource "oci_core_volume" "terra_vol" {..}
   # oci_core_volume_attachment.terra_attach will be created
   + resource "oci_core_volume_attachment" "terra_attach" {..}
   ...
  Plan: 8 to add, 0 to change, 0 to destroy.
```

- Now let the cloud party begin and provision our instance (output has been truncated for more visibility)

```
$ terraform apply -auto-approve
...
oci_core_instance.terra_inst: Creation complete after 1m46s
oci_core_volume.terra_vol: Creation complete after 14s  
oci_core_volume_attachment.terra_attach: Creation complete after 33s  
...
Apply complete! Resources: 8 added, 0 changed, 0 destroyed.

Outputs:
...
private_ip = ["192.168.78.51",]
public_ip = ["132.145.108.51",]
```

![](https://cdn-images-1.medium.com/max/999/1*ZN-aPiEn8w_fkckA6py9ew.png)

**3. CONNECTION TO YOUR INSTANCE WEB PAGE**

![](https://cdn-images-1.medium.com/max/1024/1*8S3aatz2GTxdw0OfNsMb5Q.png)

**— — — — — — — — — — — — Your Web Page is 🔥 :)**

- Once the instance is provisioned, just copy the public IP address([132.145.108.51](http://132.145.108.51/)) in your browser and Voila!
- You have made yourself a fresh and shiny website ready to roll.
- Here I just embedded a video link into the index page but you can adapt the cloud-config to your own liking
- You can also tear down this configuration by simply running **terraform destroy** from the same directory

### CONCLUSION

- We have demonstrated in this tutorial how to quickly deploy an instance using terraform in OCI and leverage Cloud-init to bootstrap your instance into a webserver .
- Remember that all used attributes in this exercise can be modified in the **variables.tf** file.
- In my next blog post we will explore how to provision instances using oci ansible modules.