Storing Terraform remote state to Oracle Cloud Infrastructure Object Storage

As soon as your use of Terraform goes beyond the simple usage scenarios of bringing up and down personal use development and test environments, you need to consider how to persist and share the Terraform state across projects or a team.

Terraform’s Remote State configuration supports a number of remote storage options, and even though Terraform does not currently provide a specific remote state backend for the Oracle Cloud Infrastructure (OCI), several of the existing backend options are compatible with the Oracle Object Storage services.

In this article we’ll look at three specific options for use with the OCI Object Storage and OCI Object Storage Classic (and Cloud at Customer) services.

  • OCI Object Storage with an HTTP remote state backend
  • OCI Object Storage with an S3 compatible remote state backend
  • OCI Object Storage Classic with an S3 compatible remote state backend

Terraform also supports several other remote state backend storage options including artifactory, etcd, and consul. These may be the topic of future articles…

The choice of remote state backend configuration is independent of the Terraform providers being used, all of the below configuration options can be used with either the oci provider for Oracle Cloud Infrastructure, the opc provider for Oracle Cloud Infrastructure Classic and Oracle Cloud at Customer, or any other Terraform provider.

Using the HTTP remote state backend

This option works with the OCI Object Storage service, and simply requires the creation of a Read/Write Pre-Authenticated Request (PAR) to the shared state file object in a storage bucket that will be used for the remote state. The state file must exist in the bucket before creating the PAR. Either upload the existing state or an empty file for the initial state.

The pre-authenticated request URL is then used in the http backend configuration.

terraform {
backend "http" {
update_method = "PUT"
address = "
https://objectstorage.us-phoenix-1.oraclecloud.com/p/_WqQoGnjzyKmqoW0RysqViW-XCEMlOX_JmYM8-eBOh0/n/example/b/terraform-state/o/terraform.tfstate"
}
}

For security, consider setting an appropriate expiry date on the pre-authenticated request and update the PARs or a regular basis. Multiple PARs can be created for an object to a support key rotation policy.

The terraform_remote_statedata source can also be configure with http backend to share state across projects. For sharing of remote state with other configurations you can use the same PAR URL, or consider creating a separate Read-only PAR

data "terraform_remote_state" "project_a" {
backend = "http"
config {
address = "https://objectstorage.us-phoenix-1.oraclecloud.com/p/T-MwMoeU7Y03qTE30bO1IAMElJdbYmMGvAYblYg3f3c/n/oraclepts/b/terraform-state/o/terraform.tfstate"
}
}

Using the S3 remote state backend

Both OCI Object Storage and OCI Classic Object Storage provide S3 compatible APIs that work with the s3 backend as of Terraform v0.11.3

Using the s3 backend requires a bit of additional setup. First the object storage account must be enabled with s3 authentication keys

For OCI Object Storage the S3 Compatible API Key are set on a per user basis, generate the key in the OCI console “Identity > Users > User Details > Amazon S3 Compatibility API Keys”. See Managing User Credentials for more details

OCI console: Identity > Users > User Details > Amazon S3 Compatibility API Keys

For OCI Classic Object Storage the S3 API Keys are set at the Account level in the Storage Classic console “Storage Classic Console > Account”. See Using S3 API-Compatible Clients to Access Storage Classic

Storage Classic console: Account

By default the s3 backend looks for the storage account credentials in ~/.aws/credentials. An alternative location for the credentials file can be set using s3 backendshared_credentials_file option.

Note: never set the access_key and secret_key attributes directly in the Terraform backend configuration, this is bad security practice.

Configure the [default] entry in the credentials file with the appropriate object storage credentials. The credentials file can contain multiple credential profiles, if a different profile name is used be sure to also set the s3 backend profile option in the Terraform configuration.

E.g. for OCI Object Storage:

[default]
aws_access_key_id=ocid1.credential.oc1..aaaaaaaasbmhehdmefolvqwtbdjgwfsojsgxgipdbph7odn2bwgurgsyztca
aws_secret_access_key=mSTdaWhlbWj3ty4JZClm0NUZV52elImWjayJLJ6OH9A=

Where aws_access_key_id and aws_secret_access_key are the user specific values provided from the OCI console.

E.g. For OCI Classic Object Storage:

[default]
aws_access_key_id=Storage-acme
aws_secret_access_key=SecretKey1

Where aws_access_key_id can be obtained from the Storage Classic REST Endpoint shown in the My Services console. Set aws_secret_access_key to s3 key configured on the storage account.

REST Endpoint https://{foo}.storage.oraclecloud.com/v1/Storage-{bar}
\_________________ _________________/ \_____ _____/
v v
endpoint access_key_id

In the Terraform s3 backend configuration set the OCI or OCI Classic object storage endpoint in the format:

OCI Object Storage:
https://{tenancy}.compat.objectstorage.{region}.oraclecloud.com

Storage Classic:
https://{foo}.storage.oraclecloud.com

Example Object Storage S3 backend configuration

terraform {
backend "s3" {
bucket = "terraform-state"
key = "terraform.tfstate"
region = "us-phoenix-1"
endpoint = "https://acme.compat.objectstorage.us-phoenix-1.oraclecloud.com"
    skip_region_validation      = true
skip_credentials_validation = true
skip_requesting_account_id = true
skip_get_ec2_platforms = true
skip_metadata_api_check = true
force_path_style = true
}
}

Similar to the HTTP backend example, the s3 backend configuration can also be used for the terraform_remote_state data source for sharing state across Terraform projects

After configuring the backend, run terraform init to complete the setup. If you have an existing terraform.tfstate then Terraform will prompt to confirm that the current state should be uploaded to the remote state.