# Deploying Workloads on Oracle Cloud Infrastructure (OCI) via Terraform - PART 2

Welcome to Part 2 of my series [Working with Terraform on Oracle Cloud Infrastructure (OCI)](https://vicknesh.cloud/series/oci-terraform)

If you need a refresher, visit [Part 1](https://vicknesh.cloud/deploying-workloads-on-oracle-compute-infrastructure-oci-via-terraform-part-1)

In [Part 1](https://vicknesh.cloud/deploying-workloads-on-oracle-compute-infrastructure-oci-via-terraform-part-1), we have done the following:
<ul>
<li>Installed Terraform</li>
<li>Created .tf scripts</li>
<li>Executed the workflows, Initialize > Plan > Apply</li>
<li>Authenticated our OCI tenant with the Terraform provider scripts</li>
</ul>

In Part 2, let's look at how we can provision compartments & provision compute & network resources within these compartments.


![terraform-compartment-diagram.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1646790284228/0gBqFYi7Q.png)

In [Part 1](https://vicknesh.cloud/deploying-workloads-on-oracle-compute-infrastructure-oci-via-terraform-part-1), we created the following .tf file which allowed Terraform to authenticate with our OCI tenant.

![provider.tf.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1646792647531/b-6PVIgTC.png)

In Part 2, we will first need to create a compartment to store our resources that we'll be creating later. To do this, we will need 2 .tf files in our working folder.

![provider.tf.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1646793630668/ldtKFgiRQ.png)

# Creating the Working Directory

Let's begin with creating the working directory to store our files in it. Once, the files are defined, we'll initialize Terraform & put it to work from this directory.

Create a directory called 'tf-compartment' under your root directory.

```
$ mkdir tf-compartment
``` 
Copy the provider.tf file from the tf-provider directory to this directory (tf-compartment). Once you have that in place, let's create the **compartment.tf** file & define the parameters. Add the following code in your file.

```
resource "oci_identity_compartment" "tf-compartment" {
    compartment_id = "<tenancy-ocid>"
    description = "Compartment for Terraform resources."
    name = "<your-compartment-name>"
}
``` 
## Defining Policies on OCI

Let's add a policy on our OCI tenant.

1. Navigate to **Identity & Security > Policies**
2. Choose a Compartment if you have one or select the root compartment for now
3. Click **Create Policy** & click **Show manual editor**
4. Paste the following policy & click **Create**

```
allow group <the-group-your-username-belongs> to manage compartments in tenancy
``` 
# Creating the Compartment

## Initialize
First, let's initialize Terraform within our working directory, **tf-compartment**

```
$ terraform init
``` 
You'll get the following output

![terraform init.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646795246805/bSdsqGIGm.PNG)

## Plan
Once, we've initialized Terraform, let's plan our deployment.

```
$ terraform plan
``` 
You'll get an output like this:

![terraform plan.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646796529586/QOKyWMjw5.PNG)

## Apply

Once we're good with our plan, let's proceed with creating the compartment by issuing the following command

```
$ terraform apply
``` 
You'll get an output like these:

![terraform apply1.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646797080616/U-i45kJM9.PNG)

![terraform apply2.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646797086106/QsrA28wky.PNG)
Now, let's verify the creation in our OCI tenant

![terraform compartment oci.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646797170589/XPWQZXdiD.PNG)

There you go. Our compartment has been successfully created on OCI

# Provisioning a Compute Instance in an Existing VCN

Now, let's attempt to provision a compute instance in our compartment, **vickterraform2**. This compute instance will be provisioned into an existing Virtual Cloud Network, **vickvcn1**, in the OCI tenant

## Generating SSH Keys

I'll be generating SSH keys which will be used to connect to the compute instance which I'll be creating later

```
$ ssh-keygen -t rsa -N "" -b 2048 -C <your-ssh-key-name> -f <your-ssh-key-name>
``` 
This will generate a public & private key pair for you. You would get something like this

![ssh key gen.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646875354340/XUeJJcijD.PNG)

## Gather Important Information

Before we begin creating the scripts, let's gather the important information that we'll be using in our scripts
<ul>
<li>**Compartment Name**</li>
<li>**Compartment ID**</li>
<li>**Subnet ID**</li>
<li>**Source ID**</li>
<li>**Shape**</li>
<li>**Path of your SSH public key**</li>
<li>**Path of your SSH private key**</li>
</ul>

### Getting the Source ID
The Source ID refers to the ID of the source image of your compute & this depends on what image (Ubuntu, CentOS, Oracle Linux, Windows, etc.) you would like to provision

To get the Source ID:

1. Identify your region. Mine is **us-ashburn-1**
2. Refer here: [Image Release Notes](https://docs.oracle.com/en-us/iaas/images/image/527d4395-8bd4-4b28-88e4-ac242ff45da8/)
3. For this deployment, I'll be using an **Ubuntu 20.04** image & this is my Source ID to the image according to my region: **ocid1.image.oc1.iad.aaaaaaaaos5ofuq26nipxcybznimsjnhyw3jf7mrq2r3kgpsf6zqbtqei25q**

### Getting the Shape
The shape refers to the size of the compute instance which defines the allocation of CPUs & memory

To get the shape:


1. Refer here: [VM Standard Shapes](https://docs.oracle.com/en-us/iaas/Content/Compute/References/computeshapes.htm#vmshapes__vm-standard)
2. I'll be using a **VM.Standard2.1** shape

## Add Policies to OCI

Let's add the following policy in **Identity & Security > Identity > Policies** under your compartment

```
allow group <the-group-your-username-belongs> to manage all-resources in compartment <your-compartment-name>
``` 

## Creating the Scripts

Let's setup a new working directory for this called 'tf-compute'

```
$ mkdir tf-compute
``` 
Copy your 'provider.tf' file into this directory

```
$ cp ../tf-provider/provider.tf .
``` 
Let's also copy the 'availability-domains.tf' file which we created in the initial setup into this directory

```
cp ../tf-provider/availability-domains.tf .
``` 
The 'availability-domains.tf' has the following code

```
data "oci_identity_availability_domains" "ads" {
  compartment_id = "<tenancy-ocid>"
}
``` 
Let's also create an 'outputs.tf' file in the 'tf-compute' directory

```
output "name-of-first-availability-domain" {
  value = data.oci_identity_availability_domains.ads.availability_domains[0].name
}
``` 
Now, we have the following files in our tf-compute directory
<ul>
<li>provider.tf</li>
<li>availability-domains.tf</li>
<li>outputs.tf</li>
</ul>

Let's initialize > plan > apply our Terraform scripts
```
$ terraform init
$ terraform plan
$ terraform apply
```
Once you've hit the terraform apply, you'll be provided with an output of your availability domain

![terraform apply3.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646878417831/nqPaCsoCb.PNG)

### Declare the Compute Resource

Now that we're ready to provision the compute instance, let's create a 'compute.tf' file & add the following code into the file
```
resource "oci_core_instance" "ubuntu_instance" {
    # Required
    availability_domain = data.oci_identity_availability_domains.ads.availability_domains[0].name
    compartment_id = "<compartment-ocid>"
    shape = "VM.Standard2.1"
    source_details {
        source_id = "<source-ocid>"
        source_type = "image"
    }

    # Optional
    display_name = "<your-ubuntu-instance-name>"
    create_vnic_details {
        assign_public_ip = true
        subnet_id = "<subnet-ocid>"
    }
    metadata = {
        ssh_authorized_keys = file("<ssh-public-key-path>")
    } 
    preserve_boot_volume = false
}
```
This is my completed file

![compute tf.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646878881954/mFHP3HC3-.PNG)

Now, let's run our scripts
```
$ terraform init
$ terraform plan
$ terraform apply
```
And, we have successfully created our Ubuntu server via Terraform. Also, let's verify this on the OCI console

![create instance.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646882211194/_9qxA3-G9.PNG)

![vick ubuntu.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646882261692/dKozCMS6o.PNG)

Let's also try connecting to this server
```
$ ssh -i <ssh-private-key-path> ubuntu@<your-public-ip-address>
```
And there you go. We've successfully accessed the Ubuntu server

![vick ubuntu1.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646882490252/ppm2ynrwn.PNG)

Now that I've tested the deployment & it's working, I will proceed to delete this compute instance

```
$ terraform destroy
``` 
Simultaneously on the console, the instance is getting terminated

![instance terminating.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646884000855/zguam76JB.PNG)

Once the instance is completely terminated, you will get the following output

![instance terminated.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646884060275/QS_9DqMLV.PNG)


# Creating a New Virtual Cloud Network (VCN)

In this section,  I will demonstrate the creation of a new Virtual Cloud Network (VCN) & its associated components such as subnets, gateways, security lists & more

![creating a vcn.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1646884971080/qXyUc-YAj.png)

## Gather Important Information

Before we begin, let's gather the important information that we'll be using in our scripts
<ul>
<li>**Compartment Name**</li>
<li>**Compartment ID**</li>
<li>**Region**</li>
</ul>

## Create a Working Directory

Let's create a new working directory for this demo & let's name it 'tf-vcn'
```
$ mkdir tf-vcn
```

Copy the 'provider-tf' file from the 'tf-provider' directory into this directory
```
cp ../tf-provider/provider.tf .
```

## Declaring the VCN Configuration

Now, let's create a file called 'vcn-module.tf' & insert the following code into it

```
# Source from https://registry.terraform.io/modules/oracle-terraform-modules/vcn/oci/
module "vcn"{
  source  = "oracle-terraform-modules/vcn/oci"
  version = "3.1.0"
  # insert the 5 required variables here

  # Required Inputs
  compartment_id = "<compartment-ocid>"
  region = "<region-identifier>"

  internet_gateway_route_rules = null
  local_peering_gateways = null
  nat_gateway_route_rules = null

  # Optional Inputs
  vcn_name = "vcn-module"
  vcn_dns_label = "vcnmodule"
  vcn_cidrs = ["10.0.0.0/16"]
  
  create_internet_gateway = true
  create_nat_gateway = true
  create_service_gateway = true  
}
``` 
Here's a sample of my code

![vcn module.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646919835663/aIs_Tt-Cg.PNG)

Let's also create an 'outputs.tf' file & insert the following code to get the information of the VCN once we run the scripts

```
# Outputs for the vcn module

output "vcn_id" {
  description = "OCID of the VCN that is created"
  value = module.vcn.vcn_id
}
output "id-for-route-table-that-includes-the-internet-gateway" {
  description = "OCID of the internet-route table. This route table has an internet gateway to be used for public subnets"
  value = module.vcn.ig_route_id
}
output "nat-gateway-id" {
  description = "OCID for NAT gateway"
  value = module.vcn.nat_gateway_id
}
output "id-for-for-route-table-that-includes-the-nat-gateway" {
  description = "OCID of the nat-route table - This route table has a nat gateway to be used for private subnets. This route table also has a service gateway."
  value = module.vcn.nat_route_id
}
``` 
## Creating the VCN

Now that we have the required scripts configured, let's create the VCN

```
$ terraform init
``` 
```
$ terraform plan
```
```
$ terraform apply
```
And there you go. We've successfully created the VCN & the gateways

![vcn created.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646920332013/BEAz6_mmo.PNG)

Let's also verify this on the OCI console

![vcn oci console.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646920435351/4T3tZuS4t.PNG)
Notice that we don't have any subnets in our VCN yet. We'll be creating this later

## Customizing the VCN

Before we jump on to create subnets, I would like to define security lists for my subnets

### Creating a Security List for the Private Subnet

Let's create a file called 'private-security-list.tf' & add the following code into it

```
# Source from https://registry.terraform.io/providers/hashicorp/oci/latest/docs/resources/core_security_list

resource "oci_core_security_list" "private-security-list"{

# Required
  compartment_id = "<compartment-ocid>"
  vcn_id = module.vcn.vcn_id

# Optional
  display_name = "security-list-for-private-subnet"
}
``` 

Let's also add an egress rule to this security list by adding the following code into our file
```
  
  egress_security_rules {
      stateless = false
      destination = "0.0.0.0/0"
      destination_type = "CIDR_BLOCK"
      protocol = "all" 
  }
```
Here's a sample of my code
![SL private subnet.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646921603122/0zwRS61bL.PNG)

Also, let's add the following code into the 'outputs.tf' file
```
# Outputs for private security list

output "private-security-list-name" {
  value = oci_core_security_list.private-security-list.display_name
}
output "private-security-list-OCID" {
  value = oci_core_security_list.private-security-list.id
}
```

Once we have created our scripts, let's run the creation of the security list for our private subnet
```
$ terraform init
``` 
```
$ terraform plan
```
```
$ terraform apply
```
And we've successfully created our security list along with its egress rule

![SL created.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646922257025/pUae3yci_.PNG)

Now that's successful, let's also create ingress rules for our security lists. In the 'private-security-list.tf', let's add the following code into it

```
ingress_security_rules { 
      stateless = false
      source = "10.0.0.0/16"
      source_type = "CIDR_BLOCK"
      # Get protocol numbers from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml TCP is 6
      protocol = "6"
      tcp_options { 
          min = 22
          max = 22
      }
    }
  ingress_security_rules { 
      stateless = false
      source = "0.0.0.0/0"
      source_type = "CIDR_BLOCK"
      # Get protocol numbers from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml ICMP is 1  
      protocol = "1"
  
      # For ICMP type and code see: https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
      icmp_options {
        type = 3
        code = 4
      } 
    }   
  
  ingress_security_rules { 
      stateless = false
      source = "10.0.0.0/16"
      source_type = "CIDR_BLOCK"
      # Get protocol numbers from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml ICMP is 1  
      protocol = "1"
  
      # For ICMP type and code see: https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
      icmp_options {
        type = 3
      } 
    }
``` 
Once again, let's run our scripts
```
$ terraform init
``` 
```
$ terraform plan
```
```
$ terraform apply
```
Now that it has successfully ran, we are able to see the ingress & egress rules created for the security list for the private subnet

![SL Ingress.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646922977169/JnrolDyPu.PNG)

![SL Egress.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646922985360/i7sfnv2JH.PNG)

### Creating a Security List for the Public Subnet

Now, let's replicate the above operations for our public subnet. Let's copy the file 'private-security-list.tf' to a new file called 'public-security-list.tf' & let's modify the parameters

```
$ cp private-security-list.tf public-security-list.tf
```
Let's change the code **private-security-list** in the resource block to **public-security-list** & change the display_name from **security-list-for-private-subnet** to **security-list-for-public-subnet**

We'll maintain the egress rule. However, for the ingress rule let's change the **source = "10.0.0.0/16"** to **source = "0.0.0.0/0"**

Your code should look like this
```
 
ingress_security_rules { 
      stateless = false
      source = "0.0.0.0/0"
      source_type = "CIDR_BLOCK"
      # Get protocol numbers from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml TCP is 6
      protocol = "6"
      tcp_options { 
          min = 22
          max = 22
      }
    }
  ingress_security_rules { 
      stateless = false
      source = "0.0.0.0/0"
      source_type = "CIDR_BLOCK"
      # Get protocol numbers from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml ICMP is 1  
      protocol = "1"
  
      # For ICMP type and code see: https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
      icmp_options {
        type = 3
        code = 4
      } 
    }   
  
  ingress_security_rules { 
      stateless = false
      source = "10.0.0.0/16"
      source_type = "CIDR_BLOCK"
      # Get protocol numbers from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml ICMP is 1  
      protocol = "1"
  
      # For ICMP type and code see: https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
      icmp_options {
        type = 3
      } 
    }
```
Let's also add the following code to the 'outputs.tf' file
```

# Outputs for public security list

output "public-security-list-name" {
  value = oci_core_security_list.public-security-list.display_name
}
output "public-security-list-OCID" {
  value = oci_core_security_list.public-security-list.id
}
```
Now, let's run our scripts
```
$ terraform init
``` 
```
$ terraform plan
```
```
$ terraform apply
```
Now, we are able to see on the console that the security list & its ingress & egress rules have been created

![SL public ingress.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646923985128/LsqbuPTEj.PNG)

![SL public egress.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646923994150/O4UA8_UEt.PNG)

## Creating the Private Subnet

We are not done yet as we have not created the subnets for our VCN. Let's start by creating a private subnet

Let's create a file called 'private-subnet.tf' & add the following code to it

```
# Source from https://registry.terraform.io/providers/hashicorp/oci/latest/docs/resources/core_subnet

resource "oci_core_subnet" "vcn-private-subnet"{

  # Required
  compartment_id = "<compartment-ocid>"
  vcn_id = module.vcn.vcn_id
  cidr_block = "10.0.1.0/24"
 
  # Optional
  # Caution: For the route table id, use module.vcn.nat_route_id.
  # Do not use module.vcn.nat_gateway_id, because it is the OCID for the gateway and not the route table.
  route_table_id = module.vcn.nat_route_id
  security_list_ids = [oci_core_security_list.private-security-list.id]
  display_name = "private-subnet"
}
``` 
Let's also add the following to the 'outputs.tf' file
```

# Outputs for private subnet

output "private-subnet-name" {
  value = oci_core_subnet.vcn-private-subnet.display_name
}
output "private-subnet-OCID" {
  value = oci_core_subnet.vcn-private-subnet.id
}
```
Now, let's run our scripts
```
$ terraform init
``` 
```
$ terraform plan
```
```
$ terraform apply
```
And there you go. We've created our private subnet with a CIDR block of 10.0.1.0/24

## Creating the Public Subnet

Let's do the above for the public subnet. To begin, let's create a file called 'public-subnet.tf' & add the following code
```
# Source from https://registry.terraform.io/providers/hashicorp/oci/latest/docs/resources/core_subnet

resource "oci_core_subnet" "vcn-public-subnet"{

  # Required
  compartment_id = "<compartment-ocid>"
  vcn_id = module.vcn.vcn_id
  cidr_block = "10.0.0.0/24"
 
  # Optional
  route_table_id = module.vcn.ig_route_id
  security_list_ids = [oci_core_security_list.public-security-list.id]
  display_name = "public-subnet"
}
```
And, add the following code to the 'outputs.tf' file
```

# Outputs for public subnet

output "public-subnet-name" {
  value = oci_core_subnet.vcn-public-subnet.display_name
}
output "public-subnet-OCID" {
  value = oci_core_subnet.vcn-public-subnet.id
}
```
Now, let's run our scripts
```
$ terraform init
``` 
```
$ terraform plan
```
```
$ terraform apply
```
And now, we have successfully created our public subnet with a CIDR block of 10.0.0.0/24

![subnets.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1646924706325/2KVPSg88f.PNG)

# Summary
This sums Part 2 of this blog. In Part 2, we've created the following
<ul>
<li>A compartment</li>
<li>A Ubuntu 20.04 compute instance</li>
<li>A Virtual Cloud Network (VCN)</li>
<li>Security List for Private Subnet with Ingress & Egress Rules</li>
<li>Security List for Public Subnet with Ingress & Egress Rules</li>
<li>A Private Subnet</li>
<li>A Public Subnet</li>
</ul>

In Part 3, I will be covering how we can incorporate the above in a single Terraform script & provision an infrastructure on OCI. Stay tuned.
