In the landscape of modern cloud computing, serverless architecture represents a significant paradigm shift, allowing developers to build and run applications without managing the underlying infrastructure. Oracle Cloud Infrastructure (OCI) Functions provides a powerful, fully managed, multi-tenant, and highly scalable serverless platform. While creating functions through the OCI console is straightforward for initial exploration, managing them at scale in a production environment demands a more robust, repeatable, and automated approach. This is where Infrastructure as Code (IaC) becomes indispensable.
This article provides a comprehensive guide on how to provision, manage, and invoke an OCI Function with Terraform. By leveraging Terraform, you can codify your entire serverless infrastructure, from the networking and permissions to the function itself, enabling version control, automated deployments, and consistent environments. We will walk through every necessary component, provide practical code examples, and explore advanced topics like invocation and integration with API Gateway, empowering you to build a fully automated serverless workflow on OCI.
Table of Contents
Prerequisites for Deployment
Before diving into the Terraform code, it’s essential to ensure your environment is correctly set up. Fulfilling these prerequisites will ensure a smooth deployment process.
- OCI Account and Permissions: You need an active Oracle Cloud Infrastructure account. Your user must have sufficient permissions to manage networking, IAM, functions, and container registry resources. A policy like
Allow group <YourGroup> to manage all-resources in compartment <YourCompartment>
is sufficient for this tutorial, but in production, you should follow the principle of least privilege. - Terraform Installed: Terraform CLI must be installed on the machine where you will run the deployment scripts. This guide assumes a basic understanding of Terraform concepts like providers, resources, and variables.
- OCI Provider for Terraform: Your Terraform project must be configured to communicate with your OCI tenancy. This typically involves setting up an API key pair for your user and configuring the OCI provider with your user OCID, tenancy OCID, fingerprint, private key path, and region.
- Docker: OCI Functions are packaged as Docker container images. You will need Docker installed locally to build your function’s image before pushing it to the OCI Container Registry (OCIR).
- OCI CLI (Recommended): While not strictly required for Terraform deployment, the OCI Command Line Interface is an invaluable tool for testing, troubleshooting, and invoking your functions directly.
Core OCI Components for Functions
A serverless function doesn’t exist in a vacuum. It relies on a set of interconnected OCI resources that provide networking, identity, and storage. Understanding these components is key to writing effective Terraform configurations.
Compartment
A compartment is a logical container within your OCI tenancy used to organize and isolate your cloud resources. All resources for your function, including the VCN and the function application itself, will reside within a designated compartment.
Virtual Cloud Network (VCN) and Subnets
Every OCI Function must be associated with a subnet within a VCN. This allows the function to have a network presence, enabling it to connect to other OCI services (like databases or object storage) or external endpoints. It is a security best practice to place functions in private subnets, which do not have direct internet access. Access to other OCI services can be granted through a Service Gateway, and outbound internet access can be provided via a NAT Gateway.
OCI Container Registry (OCIR)
OCI Functions are deployed as Docker images. OCIR is a private, OCI-managed Docker registry where you store these images. Before Terraform can create the function, the corresponding Docker image must be built, tagged, and pushed to a repository in OCIR.
IAM Policies and Dynamic Groups
To interact with other OCI services, your function needs permissions. The best practice for granting these permissions is through Dynamic Groups and IAM Policies.
- Dynamic Group: A group of OCI resources (like functions) that match rules you define. For example, you can create a dynamic group of all functions within a specific compartment.
- IAM Policy: A policy grants a dynamic group specific permissions. For instance, a policy could allow all functions in a dynamic group to read objects from a specific OCI Object Storage bucket.
Application
In the context of OCI Functions, an Application is a logical grouping for one or more functions. It provides a way to define shared configuration, such as subnet association and logging settings, that apply to all functions within it. It also serves as a boundary for defining IAM policies.
Function
This is the core resource representing your serverless code. The Terraform resource defines metadata for the function, including the Docker image to use, the memory allocation, and the execution timeout.
Step-by-Step Guide: Creating an OCI Function with Terraform
Now, let’s translate the component knowledge into a practical, step-by-step implementation. We will build the necessary infrastructure and deploy a simple function.
Step 1: Project Setup and Provider Configuration
First, create a new directory for your project and add a provider.tf
file to configure the OCI provider.
provider.tf
:
terraform {
required_providers {
oci = {
source = "oracle/oci"
version = "~> 5.0"
}
}
}
provider "oci" {
tenancy_ocid = var.tenancy_ocid
user_ocid = var.user_ocid
fingerprint = var.fingerprint
private_key_path = var.private_key_path
region = var.region
}
Use a variables.tf
file to manage your credentials and configuration, avoiding hardcoding sensitive information.
Step 2: Defining Networking Resources
Create a network.tf
file to define the VCN and a private subnet for the function.
network.tf
:
resource "oci_core_vcn" "fn_vcn" {
compartment_id = var.compartment_ocid
cidr_block = "10.0.0.0/16"
display_name = "FunctionVCN"
}
resource "oci_core_subnet" "fn_subnet" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.fn_vcn.id
cidr_block = "10.0.1.0/24"
display_name = "FunctionSubnet"
# This makes it a private subnet
prohibit_public_ip_on_vnic = true
}
# A Security List to allow necessary traffic (e.g., egress for OCI services)
resource "oci_core_security_list" "fn_security_list" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.fn_vcn.id
display_name = "FunctionSecurityList"
egress_security_rules {
protocol = "all"
destination = "0.0.0.0/0"
}
}
Step 3: Creating the Function Application
Next, define the OCI Functions Application. This resource links your functions to the subnet you just created.
functions.tf
:
resource "oci_functions_application" "test_application" {
compartment_id = var.compartment_ocid
display_name = "my-terraform-app"
subnet_ids = [oci_core_subnet.fn_subnet.id]
}
Step 4: Preparing the Function Code and Image
This step happens outside of the main Terraform workflow but is a critical prerequisite. Terraform only manages the infrastructure; it doesn’t build your code or the Docker image.
- Create Function Code: Write a simple Python function. Create a file named
func.py
.
import io
import json
def handler(ctx, data: io.BytesIO=None):
name = "World"
try:
body = json.loads(data.getvalue())
name = body.get("name")
except (Exception, ValueError) as ex:
print(str(ex))
return { "message": "Hello, {}!".format(name) }
- Create
func.yaml
: This file defines metadata for the function.
schema_version: 20180708
name: my-tf-func
version: 0.0.1
runtime: python
entrypoint: /python/bin/fdk /function/func.py handler
memory: 256
- Build and Push the Image to OCIR:
- First, log in to OCIR using Docker. Replace
<region-key>
,<tenancy-namespace>
, and<your-username>
. You’ll use an Auth Token as your password.$ docker login <region-key>.ocir.io -u <tenancy-namespace>/<your-username>
- Next, build, tag, and push the image.
# Define image name variable
$ export IMAGE_NAME=<region-key>.ocir.io/<tenancy-namespace>/my-repo/my-tf-func:0.0.1
# Build the image using the OCI Functions build image
$ fn build
# Tag the locally built image with the full OCIR path
$ docker tag my-tf-func:0.0.1 ${IMAGE_NAME}
# Push the image to OCIR
$ docker push ${IMAGE_NAME}
- First, log in to OCIR using Docker. Replace
The IMAGE_NAME
value is what you will provide to your Terraform configuration.
Step 5: Defining the OCI Function Resource
Now, add the oci_functions_function
resource to your functions.tf
file. This resource points to the image you just pushed to OCIR.
functions.tf
(updated):
# ... (oci_functions_application resource from before)
resource "oci_functions_function" "test_function" {
application_id = oci_functions_application.test_application.id
display_name = "my-terraform-function"
image = var.function_image_name # e.g., "phx.ocir.io/your_namespace/my-repo/my-tf-func:0.0.1"
memory_in_mbs = 256
timeout_in_seconds = 30
}
Add the function_image_name
to your variables.tf
file and provide the full image path.
Step 6: Deploy with Terraform
With all the configuration in place, you can now deploy your serverless infrastructure.
- Initialize Terraform:
terraform init
- Plan the Deployment:
terraform plan
- Apply the Configuration:
terraform apply
After you confirm the apply step, Terraform will provision the VCN, subnet, application, and function in your OCI tenancy.
Invoking Your Deployed Function
Once deployed, there are several ways to invoke your function. Using Terraform to manage an OCI Function with Terraform also extends to its invocation for testing or integration purposes.
Invocation via OCI CLI
The most direct way to test your function is with the OCI CLI. You’ll need the function’s OCID, which you can get from the Terraform output.
# Get the function OCID
$ FUNCTION_OCID=$(terraform output -raw function_ocid)
# Invoke the function with a payload
$ oci fn function invoke --function-id ${FUNCTION_OCID} --body '{"name": "Terraform"}' output.json
# View the result
$ cat output.json
{"message":"Hello, Terraform!"}
Invocation via Terraform Data Source
Terraform can also invoke a function during a plan or apply using the oci_functions_invoke_function
data source. This is useful for performing a quick smoke test after deployment or for chaining infrastructure deployments where one step depends on a function’s output.
data "oci_functions_invoke_function" "test_invocation" {
function_id = oci_functions_function.test_function.id
invoke_function_body = "{\"name\": \"Terraform Data Source\"}"
}
output "function_invocation_result" {
value = data.oci_functions_invoke_function.test_invocation.content
}
Running terraform apply
again will trigger this data source, invoke the function, and place the result in the `function_invocation_result` output.
Exposing the Function via API Gateway
For functions that need to be triggered via an HTTP endpoint, the standard practice is to use OCI API Gateway. You can also manage the API Gateway configuration with Terraform, creating a complete end-to-end serverless API.
Here is a basic example of an API Gateway that routes a request to your function:
resource "oci_apigateway_gateway" "fn_gateway" {
compartment_id = var.compartment_ocid
endpoint_type = "PUBLIC"
subnet_id = oci_core_subnet.fn_subnet.id # Can be a different, public subnet
display_name = "FunctionAPIGateway"
}
resource "oci_apigateway_deployment" "fn_api_deployment" {
gateway_id = oci_apigateway_gateway.fn_gateway.id
compartment_id = var.compartment_ocid
path_prefix = "/v1"
specification {
routes {
path = "/greet"
methods = ["GET", "POST"]
backend {
type = "ORACLE_FUNCTIONS_BACKEND"
function_id = oci_functions_function.test_function.id
}
}
}
}
This configuration creates a public API endpoint. A POST request to <gateway-invoke-url>/v1/greet
would trigger your function.
Frequently Asked Questions
- Can I manage the function’s source code directly with Terraform?
- No, Terraform is an Infrastructure as Code tool, not a code deployment tool. It manages the OCI resource definition (memory, timeout, image pointer). The function’s source code must be built into a Docker image and pushed to a registry separately. This process is typically handled by a CI/CD pipeline (e.g., OCI DevOps, Jenkins, GitHub Actions).
- How do I securely manage secrets and configuration for my OCI Function?
- The recommended approach is to use the
config
map within theoci_functions_function
resource for non-sensitive configuration. For secrets like API keys or database passwords, you should use OCI Vault. Store the secret OCID in the function’s configuration, and grant the function IAM permissions to read that secret from the Vault at runtime. - What is the difference between `terraform apply` and the `fn deploy` command?
- The
fn
CLI’sdeploy
command is a convenience utility that combines multiple steps: it builds the Docker image, pushes it to OCIR, and updates the function resource on OCI. In contrast, the Terraform approach decouples these concerns. The image build/push is a separate CI step, and `terraform apply` handles only the declarative update of the OCI infrastructure. This separation is more robust and suitable for production GitOps workflows. - How can I automate the image push before running `terraform apply`?
- This is a classic use case for a CI/CD pipeline. The pipeline would have stages:
- Build: Checkout the code, build the Docker image.
- Push: Tag the image (e.g., with the Git commit hash) and push it to OCIR.
- Deploy: Run `terraform apply`, passing the new image tag as a variable. This ensures the infrastructure update uses the latest version of your function code.

Conclusion
Automating the lifecycle of an OCI Function with Terraform transforms serverless development from a manual, click-based process into a reliable, version-controlled, and collaborative practice. By defining your networking, applications, and functions as code, you gain unparalleled consistency across environments, reduce the risk of human error, and create a clear audit trail of all infrastructure changes.
This guide has walked you through the entire process, from setting up prerequisites to defining each necessary OCI component and finally deploying and invoking the function. By integrating this IaC approach into your development workflow, you unlock the full potential of serverless on Oracle Cloud, building scalable, resilient, and efficiently managed applications. Thank you for reading theย DevopsRolesย page!