Quickops: creating ssh key and inserting inside OCI instance using terraform
Tags: oci, cloud, terrafom, tutorial
Published: 2021-11-18 06:27:29

During an instance creation on Oracle cloud infrastructure, there is one step to provide a SSH Public key, paste the content of a public key o let Oracle generate private and public key for you.

![Instance-creation-ssh-option](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akd9lq8xykhl0ctoveyo.png)

When yo are creating an instance from Terraform, and consult the documentation 
[oci_core_instance](https://registry.terraform.io/providers/hashicorp/oci/latest/docs/resources/core_instance#source_type)

You will notice that this option correspond to the field **metadata** which says:
> (Optional) (Updatable) Custom metadata key/value pairs that you provide, such as the SSH public key required to connect to the instance.

So based on this you can have the option to send you local public key to the instance during creation by using this inside the **main.tf** file
```
resource "oci_core_instance" "instance01" {
    #the rest of the output not shown 
    #METADATA for the instance
    metadata = {
        ssh_authorized_keys = file("<path to your ssh public key>")
    } 
    preserve_boot_volume = false
```

Also you can have this inside the main.tf file:
```
resource "oci_core_instance" "instance01" {
    #the rest of the output not shown 
    #METADATA for the instance
    metadata = {
        ssh_authorized_keys = file(var.path_local_public_key)
    } 
    preserve_boot_volume = false
```

And then another file called **variables.tf** and inside declare the variable:

```
variable "path_local_public_key" {
  default = "~/.ssh/id_rsa.pub"
  sensitive = true
}
```
and the current path inside another file with this naming best practice convention **env.tfvars**:

```
path_local_public_key = "/Users/user01/.ssh/id_rsa.pub"
```

**note** this .tfvars should not be send to the code repo as it can contain sensitive information as IDs.

***********************************************************

### But, is there another way?

Indeed, there is a way where you can control de key creation and send it to the **main.tf** template

There is a [TLS Provider](https://registry.terraform.io/providers/hashicorp/tls/latest/docs) that comes with terraform:
> The TLS provider provides utilities for working with Transport Layer Security keys and certificates. It provides resources that allow private keys, certificates and certficate requests to be created as part of a Terraform deployment.

 Under this scenario you can have another file called **keys.tf** with the following content:

```
resource "tls_private_key" "key01" {
  algorithm   = "RSA"
  rsa_bits = "2048"
}
```

This are the config required to create a key for OCI instances but you can also use `rsa_bits = "4096"` or   `algorithm   = "ECDSA" ecdsa_curve = "P384"` it depends on the requirement.

The point is that this file will generate a private key and the public key, but how can we pass it to the **main.tf** file? well you need to generate the outputs first.

Create a file called **outputs.tf** and write the following inside:

```
output "key-private-pem" {
  value = tls_private_key.key01.private_key_pem
  sensitive = true
}

output "key-public-openssh" {
  value = tls_private_key.key01.public_key_openssh
}
```

If we move to the documentation of the [tls resource](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) it mentions that the resource creation generate the following outputs:
* algorithm 
* private_key_pem 
* public_key_pem 
* public_key_openssh 
* public_key_fingerprint_md5 

Basically we need the private key to store inside the bastion server and the public key to send it to the instance creation. But, and here is the interesting part, OCI for example only support the openssh format

> The public key data in OpenSSH authorized_keys format. ll RSA keys are supported, and ECDSA keys with curves "P256", "P384" and "P521" are supported.
These format start with `ssh-rsa ...`

That is the reason why we only put the **private_key_pem** and **public_key_openssh** inside the outputs file.

Notice also that the output file has a `sensitive = true ` under the **private_key_pem** output definition, this is a requirement by terraform as the private key is sensitive information otherwise you will receive an error about sharing sensitive information.

Don't worry this only avoid the key to show on logs or in the console but it will display on the **tfstate** file.

Ok back to our configuration, inside the main file you can put this:
```
resource "oci_core_instance" "instance01" {
    #the rest of the output not shown 
    #METADATA for the instance
    metadata = {
        ssh_authorized_keys = tls_private_key.key01public_key_openssh
    } 
    preserve_boot_volume = false
```
So basically you are calling the output from one resource and using as an input ion other resource.

after the creation you can copy the private and public keys from the **tfstate** and create your own private and .pub files (example id_rsa and id_rsa.pub)

Don't forget to assign the required permissions to the private key file:
```
$ sudo chmod 600 id_rsa
```

And also remember to store the **tfstate** file in a secure way.

**Note**: Terraform is intelligent enough to know that it will need to create first the key and then imported inside the instance creation, there is no need for a `depends on`