Certificate management done manually is a series of incidents waiting to happen. An engineer generates a cert, deploys it, and nobody sets a renewal reminder. The certificate expires at 3am and the load balancer starts rejecting connections. OCI Certificates Service automates the full lifecycle: issuance from a private CA, automatic renewal before expiry, and deployment to OCI Load Balancer and API Gateway without manual steps.
Step 1: Private Root CA
resource "oci_certificates_management_certificate_authority" "internal_ca" {
compartment_id = var.compartment_id
name = "internal-root-ca"
kms_key_id = var.vault_master_key_id
certificate_authority_config {
config_type = "ROOT_CA_GENERATED_INTERNALLY"
subject {
common_name = "Internal Root CA"
organization = "OsamaOracle"
organizational_unit = "Platform Engineering"
country = "SA"
}
validity {
time_of_validity_not_before = "2026-09-08T00:00:00Z"
time_of_validity_not_after = "2031-09-08T00:00:00Z"
}
signing_algorithm = "SHA512_WITH_RSA"
version_name = "v1"
}
defined_tags = { "Operations.ManagedBy" = "terraform", "Operations.Environment" = "production" }
}
output "ca_id" { value = oci_certificates_management_certificate_authority.internal_ca.id }
Step 2: Issue Certificate with Auto-Renewal
resource "oci_certificates_management_certificate" "orders_api_tls" {
compartment_id = var.compartment_id
name = "orders-api-tls"
certificate_config {
config_type = "ISSUED_BY_INTERNAL_CA"
certificate_authority_id = oci_certificates_management_certificate_authority.internal_ca.id
certificate_profile_type = "TLS_SERVER_OR_CLIENT"
key_algorithm = "RSA2048"
signature_algorithm = "SHA256_WITH_RSA"
version_name = "v1"
subject { common_name = "orders-api.internal.example.com" }
subject_alternative_names { type = "DNS"; value = "orders-api.internal.example.com" }
subject_alternative_names { type = "DNS"; value = "orders-api-v2.internal.example.com" }
validity {
time_of_validity_not_before = "2026-09-08T00:00:00Z"
time_of_validity_not_after = "2027-09-08T00:00:00Z"
}
}
certificate_rules {
rule_type = "CERTIFICATE_RENEWAL_RULE"
advance_renewal_period = "P30D"
renewal_interval = "P90D"
}
defined_tags = { "Operations.ManagedBy" = "terraform", "Operations.Environment" = "production" }
}
output "certificate_id" { value = oci_certificates_management_certificate.orders_api_tls.id }
Renewal creates a new certificate version with the same OCID. Any downstream resource referencing the certificate by OCID receives the renewed version automatically on the next TLS handshake. No reconfiguration of load balancers, API gateways, or any other consumer is needed when a certificate renews.
Step 3: Deploy to OCI Load Balancer
resource "oci_load_balancer_listener" "https_listener" {
load_balancer_id = var.load_balancer_id
name = "orders-api-https"
default_backend_set_name = var.backend_set_name
port = 443
protocol = "HTTP"
ssl_configuration {
certificate_ids = [oci_certificates_management_certificate.orders_api_tls.id]
verify_peer_certificate = false
server_order_preference = "ENABLED"
cipher_suite_name = "oci-default-ssl-cipher-suite-v1"
protocols = ["TLSv1.2", "TLSv1.3"]
}
}
Step 4: Deploy to API Gateway
resource "oci_apigateway_gateway" "production" {
compartment_id = var.compartment_id
display_name = "production-api-gateway"
endpoint_type = "PRIVATE"
subnet_id = var.private_subnet_id
network_security_group_ids = [var.api_gateway_nsg_id]
certificate_id = oci_certificates_management_certificate.orders_api_tls.id
defined_tags = { "Operations.ManagedBy" = "terraform", "Operations.Environment" = "production" }
}
Step 5: Import an Externally Issued Certificate
resource "oci_certificates_management_certificate" "public_tls" {
compartment_id = var.compartment_id
name = "public-api-tls"
certificate_config {
config_type = "IMPORTED"
version_name = "letsencrypt-2026-09"
certificate_pem = file("${path.module}/certs/fullchain.pem")
private_key_pem = var.certificate_private_key
cert_chain_pem = file("${path.module}/certs/chain.pem")
}
certificate_rules {
rule_type = "CERTIFICATE_RENEWAL_RULE"
advance_renewal_period = "P14D"
renewal_interval = "P30D"
}
}
Step 6: Expiry Alarm
resource "oci_monitoring_alarm" "cert_expiry" {
compartment_id = var.compartment_id
display_name = "certificate-expiry-45-days"
is_enabled = true
metric_compartment_id = var.compartment_id
namespace = "oci_certificates"
query = "DaysToExpiry[1d]{certificateId = '${oci_certificates_management_certificate.orders_api_tls.id}'}.min() < 45"
severity = "WARNING"
pending_duration = "PT24H"
destinations = [var.ops_notification_topic_id]
body = "TLS certificate for orders-api expires in less than 45 days. Verify auto-renewal is configured and the CA is accessible."
}
Step 7: Check Certificate Status with Python
import oci
from datetime import datetime, timezone
def check_certificate_expiry(compartment_id: str, warn_days: int = 45) -> list:
config = oci.config.from_file()
client = oci.certificates_management.CertificatesManagementClient(config)
certs = client.list_certificates(
compartment_id=compartment_id
).data.items
warnings = []
now = datetime.now(timezone.utc)
for cert in certs:
if cert.lifecycle_state != "ACTIVE":
continue
expiry = cert.current_version.validity.time_of_validity_not_after
days_left = (expiry - now).days
if days_left 4} days | {cert['name']:<40} | expires {cert['expires']}")
Operational Notes
Store private keys in OCI Vault, not in Terraform code or state files. For imported certificates, pass the private key as a sensitive Terraform variable sourced from Vault at apply time. Certificate PEM and chain PEM files are not sensitive and can be stored in version control. The private key must never appear in state, logs, or source code.
Set the expiry alarm at 45 days even with auto-renewal configured. Auto-renewal depends on the CA being accessible and the signing key being available in Vault. The alarm gives you 45 days to intervene manually if auto-renewal silently fails, which is enough time to act without incident-level pressure.
Regards,
Osama
#OCI #OracleCloud #TLS #PKI #Terraform #IaC #CloudSecurity #TechBlog #Oracle #PlatformEngineering #DevSecOps #CertificateManagement #LoadBalancer #APIGateway #OracleCloudInfrastructure #AutoRenewal #PrivateCA #Encryption #ZeroTrust #Certificates
Leave a comment