Sometimes, it can be interesting to learn how the Oracle Cloud Infrastructure (OCI) API works, particularly if your favourite Internet search engine fails to locate the exact syntax of an API call. This is typically due to unspecific search terms because the OCI CLI is really well documented, but those things happen.
I would like to thank Sebastian Solbach for this tip; all credit goes to him. I’m merely writing up what he told me :)
Again, I would like to stress that this is a last-resort option: you typically find what you need in the OCI CLI documentation, the Oracle OCI Terraform provider, and, last but not least, the API docs.
Practical example
For the sake of the discussion, let’s assume that the problem at hand is to add multiple SSH keys to an OCI Compute instance. Note that of 241029 this operation must be performed during instance creation, you cannot change the SSH key configuration once the instance is up.
Using Terraform, it is simple enough to create an OCI Compute VM instance; this is one way to do it and you find plenty of examples very similar to this one (note that you might incur cost if you create this instance, and your Terraform looks different to mine!)
resource "oci_core_instance" "my_instance" {
availability_domain = data.oci_identity_availability_domains.local_ads.availability_domains.2.name
compartment_id = var.compartment_ocid
shape = "VM.Standard.E4.Flex"
shape_config {
memory_in_gbs = 32
ocpus = 2
}
create_vnic_details {
assign_public_ip = false
hostname_label = "demo"
subnet_id = module.network.backend_subnet_ocid
}
agent_config {
are_all_plugins_disabled = false
is_management_disabled = false
is_monitoring_disabled = false
}
display_name = "demo"
metadata = {
"ssh_authorized_keys" = file(var.ssh_public_key_path)
}
source_details {
# Oracle-Linux-9.4-2024.09.30-0 available in Frankfurt current as of 241029
source_id = "ocid1.image.oc1.eu-frankfurt-1.aaaaaaaajnfw4vhjfoenaoqhcymfdf47ep4angkowyuh3b4dzi56pkqcxavq"
source_type = "image"
boot_volume_size_in_gbs = 150
}
preserve_boot_volume = false
}
After the VM is created and booted up, the public key referred to by var.ssh_public_key_path can be found in the opc account’s .ssh/authorized_keys file, allowing you to log in as the opc user without ever typing a password.
But what if you need a couple or even more SSH keys? Without looking at the documentation, you might assume you can pass an array of strings to the instance’ metadata.
metadata = {
"ssh_authorized_keys" = [
file(var.ssh_public_key_path),
file(var.ssh_public_key2_path)
]
}
Terraform, however, expects a string, not an array of strings:
$ terraform plan
╷
│ Error: Incorrect attribute value type
│
│ on main.tf line 439, in resource "oci_core_instance" "my_instance":
│ 439: metadata = {
│ 440: "ssh_authorized_keys" = [
│ 441: file(var.ssh_public_key_path),
│ 442: file(var.ssh_public_key2_path)
│ 443: ]
│ 444: }
│
│ Inappropriate value for attribute "metadata": element "ssh_authorized_keys": string required.
Right ok, but how? I failed to find the documentation reference and after a few minutes ran out of patience. I switched to the GUI to create the VM instance. To do so I navigated to Compute > Instances and hit the Create instance button. I completed the wizard with the relevant details concerning O/S image, network, and other settings. When adding the public SSH keys via the browser you can specify multiple keys so I knew it must have been possible to do it via the API, too. I went ahead and added two different keys using Paste public keys in anticipation of what happens next.
To find out, the trick is to open your browser’s developer tools before submitting the request to the backend. With the developer tools open, navigate to the network tab, and only then create the instance. After a very brief moment you can see a bunch of POST requests.

The one in question is .../20160918/instances/. The Request tab shows all the details submitted, with the relevant information highlighted here and in the screenshot.
{
"metadata": {
"ssh_authorized_keys": "ssh-ed25519 AAAc0 ssh-key one\nssh-ed25519 AAAAC3NzaC1lZD ssh-key two"
}
}
The keen observer undoubtedly spotted the newline (\n) in the string. That’s it – a newline character separates multiple SSH keys – which is totally documented by the way, but as I said, I didn’t spot it. There is an easy fix for the Terraform resource:
metadata = {
"ssh_authorized_keys" = join("\u005c\u006e", [
trimspace(file(var.ssh_windows_key_path)),
trimspace(file(var.ssh_public_key_path))
]
)
}
And we’re off to the races. Note that the “\n” had to be unicode-encoded or else Terraform would have inserted a newline which the OCI API didn’t appreciate. It was also necessary to remove the extra newline file() inserts as well so everything is really 1 string, in exactly 1 line.
Happy troubleshooting!
Reference
The OCI API is well documented, here is a list of sections relevant to this article:
- Terraform resource description for oci_core_instance – search for
ssh_authorized_keysand jump to the first hit to see what I missed - OCI Command Line Interace: Launch Instance Reference
- Compute Instance API Reference
- LaunchInstanceDetails Reference
You must be logged in to post a comment.