Note:
- This tutorial requires access to Oracle Cloud. To sign up for a free account, see Get started with Oracle Cloud Infrastructure Free Tier.
- It uses example values for Oracle Cloud Infrastructure credentials, tenancy, and compartments. When completing your lab, substitute these values with ones specific to your cloud environment.
Invoke Oracle Cloud Infrastructure custom function with notification event and publish updated message to Lark collaboration tool
Introduction
Oracle Cloud Infrastructure (OCI) customers receive notifications via subscribed OCI Notification topics, covering events like instance events, alarms, console announcements, and more. These subscribed topics also support OCI Functions as Notification Targets, allowing customers to invoke custom function during notification event and send updated notification details to communication channels that support API invocation. In this tutorial, we’ll show you how to forward these updated notifications to Lark, an enterprise collaboration platform with messaging APIs.
Using a custom OCI Function, you can retrieve various data and perform actions, below are few examples such as:
- Instance Information: Gather details about OCI instances, including metadata, shape, and lifecycle states.
- Networking Details: Retrieve VNIC and subnet information, including private and public IP addresses.
- Resource Tags: Access and modify resource tags for better organization and tracking.
- Scaling and Management: Perform actions like starting, stopping, or resizing instances based on specific conditions.
- Logging and Metrics: Collect logs and metrics for custom monitoring and reporting purposes.
- Security Groups and ACLs: Manage security group rules and access control lists.
This tutorial will provide a comprehensive walkthrough of how to trigger a custom OCI Function in response to an instance’s power event, such as STOP. Users have the flexibility to customize this function and trigger it based on various subscribed OCI Notification topics. For more information about OCI Functions, see OCI Functions Overview.
Objective
OCI Functions allows you to write custom code which can be invoked based on OCI notification, OCI Announcement’s subscribed Notification topic, Compute Instance’s event and more. In this tutorial we have created a custom function written in Python code which gets invoked based on a Notification event and publishes the updated message to Lark collaboration tool using a webhook URL within the code. This provides you an option to include and update necessary additional information which can be consumed by the end user.
Prerequisites
- An active OCI tenancy with required permissions and resources quota to support architecture.
- Familiarity with OCI notification, OCI Functions as service and cloud services.
- User can download this architecture’s custom function code here using Object Storage PAR URL valid until December, 2026.
Audience
This tutorial is intended for Cloud Service Provider administrators and professionals.
Architecture
Below is a sample use case topology and high level architecture of the solution.
You can refer to this architecture when you want to create a custom OCI Function and invoke it based on notification event. You can follow series of tasks outlined in this Tutorial to setup & validate this architecture.
Task 1: Create a Dynamic Group
Dynamic groups allow you to group OCI compute instances as “principal” actors (similar to user groups).
Note: You need Administrator privileges to create Dynamic groups.
-
Sign in to the OCI Console tenancy account.
-
From the services menu, select Identity & Security and then from the Identity section, select Dynamic Groups.
-
Click Create Dynamic Group.
-
Enter a meaningful Name and Description.
-
In the Rule 1 section, add the following line:
ALL {resource.type = 'fnfunc', resource.compartment.id = 'ocid1.compartment.oc1', instance.compartment.id = 'ocid1.compartment.XXXXX'}
Note:
- Replace compartment ID with your desired compartment ID.
- You can also include specific function resource OCID in your dynamic group.
-
Click Create, and your dynamic group should be created successfully as shown in the following image.
Task 2: Create Dynamic Group OCI IAM Policy
Oracle Cloud Infrastructure Identity and Access Management (OCI IAM) enables you to control who has access to your cloud resources. In order for this architecture to work, we need to grant access for the function as a service faas to the compute, networks and several other services.
-
From the services menu, select Identity & Security and then from the Identity section, select Policies.
-
Click Create Policy.
-
Enter an appropriate Name and Description and make sure to pick the correct compartment (in this example, we will use the compartment name
oc-demo
). -
In the Policy Builder section, make sure to enable the Show manual editor option and paste the following lines:
Allow dynamic-group <dynamic_group_name> to read instances in compartment <compartment_name_path>
Allow dynamic-group <dynamic_group_name> to read vnics in compartment <compartment_name_path>
Allow dynamic-group <dynamic_group_name> to read vnic-attachments in compartment <compartment_name_path>
Note: Replace the dynamic_group_name name and compartment_name_path with your desired values.
-
Click Create, and your policy should be created successfully at root/your compartment level as shown in the following image.
Task 3: Create a Repository to store the custom function image
Container Registry enables you to store, share, and manage container images (such as Docker images) in an Oracle-managed registry.
-
In the Console, open the service menu and click Developer Services. Under Containers, click Container Registry.
-
Click Create Repository.
-
In the Create Repository dialog box, specify Compartment and Repository Name of your choice and leave it Private.
-
Click Create Repository, and your container registry should be created successfully in your specified compartment as shown in the following image.
Task 4: Create a Virtual Cloud Network with Subnet to store the function application.
A virtual cloud network (VCN) is a network that you define in OCI. It includes subnets, route tables, and gateways.
Note: If you already have a VCN, subnet created earlier you can skip this task and proceed with Task 5.
-
Click the services menu near the upper left corner of the OCI web console.
-
Under Networking, select Virtual Cloud Networks.
-
On the Virtual Cloud Networks page, click Start VCN Wizard.
-
Select Create VCN with Internet Connectivity and click Start VCN Wizard.
-
Complete the VCN configuration, and then click Next
-
Click Create, and your virtual cloud networks with subnets should be created successfully as shown in the following image.
Task 5: Create an Application to store functions
-
Open the service menu and click Developer Services. Under Functions, click Applications.
-
Click Create Application.
-
On the Create Application window, select the Name of the application (for example, function-app), select the VCN you created in Task 4 and select public subnet.
-
Click Create, and your function application should be created successfully as shown in the following image.
Task 6: Deploy the function to store functions
This step will use the zip archive that can be downloaded from the Prerequisites section of this tutorial.
-
Navigate to your OCI cloud console and click on Developer Tools, Cloud Shell.
-
Once you click the Launch Cloud Shell button, a new small shell window will display at the end of the page. In the right upper corner of that window, click on the cogwheel menu and select Upload.
-
Upload the zip file available in the Prerequisites section of this tutorial and navigate to the copied directory.
-
You will see the func.py file as below which we have created to ensure you can read notification JSON and add additional fields like IP Address details and use updated message to send message to the Lark webhook URL.
- parse_message: Function read notification JSON and update message
- get_ip_address: Function using Compute, Network client library to read additional details like IP address based of instance’s resource OCID for example.
- make_post: Function to make a post to Lark channel using webhook URL.
Note:
- We are using OCI python sdk but you can customize based on other supported language using go/java sdks.
- You must update your webhook URL either Lark or other collaboration tools like Slack.
- You can customize this function based on your requirements. We have used certain notification JSON payload to read values, but you can also use console announcement JSON or other event JSON too.
### This is a sample code, End user can modify as needed ### import io import oci import json import logging import requests """ Read Notification Message Add IP address of Resource. """ def parse_message(body): notification = "" ocid = "" type = body["source"] description = body["eventType"] compartment_id = body["data"]["compartmentId"] compartment_name = body["data"]["compartmentName"] affected_resources = "" if len(body["data"]) > 0: signer = oci.auth.signers.get_resource_principals_signer() affected_resources = "Affected Resources: " ocid = body["data"]["resourceId"] notification_reason = body["data"]["additionalDetails"]["instanceActionType"] affected_resources += "\nResource OCID: " + ocid resource_ip_address = get_ip_address(signer, ocid) affected_resources += "\nResource Private IP Address: " + resource_ip_address notification = type + " " + "\n\n" + compartment_name + " - " + compartment_id + "\n\n" + description + "\n\n" + affected_resources + "\n\n" + notification_reason logging.getLogger().debug(notification) make_post(notification) """ Get Resource IP address based of resource OCID. """ def get_ip_address(signer, resource_id): compute_client = oci.core.ComputeClient(config={}, signer=signer) network_client = oci.core.VirtualNetworkClient(config={}, signer=signer) instance_details = compute_client.get_instance(instance_id=resource_id).data vnic_response = compute_client.list_vnic_attachments(compartment_id=instance_details.compartment_id, instance_id=resource_id) vnics = vnic_response.data for vnic in vnics: vnic_details = network_client.get_vnic(vnic_id=vnic.vnic_id).data private_ip = vnic_details.private_ip return private_ip """ Post a Message to Lark. You can also use OCI Vault/Secret to store Lark credentials and access them here. """ def make_post(post_text): url = "https://open.larksuite.com/open-apis/bot/v2/hook/XXXX" headers = { "Content-Type": "application/json" } req_body = { "msg_type": "text", "content": { "text": post_text } } payload = json.dumps(req_body) try: post_response=requests.post(url=url, data=payload, headers=headers) except Exception as e: logging.getLogger().error(e) return response_dict = json.loads(post_response.text) code = response_dict.get("StatusCode", -1) if code != 0: logging.getLogger().error("error sending post text, code: " + str(code)) return def to_bool(a): return True if a == "True" else False """This is the function entry point. ctx will contain function variables defined in OCI console, data contains the payload """ def handler(ctx, data: io.BytesIO = None): try: cfg = dict(ctx.Config()) for a in cfg: cfg[a]=to_bool(cfg[a]) except Exception as e: print('ERROR: Missing configuration keys', e, flush=True) logging.getLogger().debug(cfg) return 'error parsing config keys: ' + str(ex) try: raw_body = data.getvalue() body = json.loads(raw_body) logging.getLogger().info(body) parse_message(body) except (Exception, ValueError) as ex: logging.getLogger().error('error parsing json payload: ' + str(ex)) return 'error parsing json payload: ' + str(ex)
-
Deploy the function using
fn deploy
command to build the function docker image and associated dependencies in cloud shell. You can push the image to the specified Docker registry, and deploy the function to OCI Functions in the application created earlier.fn -v deploy --app <app-name>
For example:
fn -v deploy --app function-app
Note: You can view
func.yaml
file see function docker image required details. -
Once deployed, your function should be created successfully as shown in the following image.
Task 7: Subscribe the Function to a topic
-
Open the service menu and click Developer Services. Under Application Integration, click Notifications. Make sure to be in the correct compartment.
-
Click Create Topic.
-
Set an appropriate name and then click Create.
-
After the topic is created select it from the list of topics.
-
On the Topics window, select Subscriptions from the left side under Resources and click Create Subscription.
-
In the Create Subscription side window, select the protocol to be Functions, select the compartment, the application and the function that were created in Task 6.
-
Click Create, your function should be subscribed successfully to a topic as shown in the following image.
Task 8: Create an Instance and Notification Event Rule
To validate the function in this step we will create an instance and associated create notification topic in Task 7 to an event like instance-stopped.
-
Within the Console, navigate to Compute, Instances to create a compute instance.
-
Click on the Create Instance icon and provide the required details:
- Instance Name
- Instance’s Compartment
- Select the desired AD
- Select the Network VCN and Public Subnet
- Copy-paste your public ssh key
- Click Create.
-
Wait for the instance to display running and then log into the instance using the following command.
ssh opc@<public ip> -i <private key>
-
Navigate to Compute, Instances, Instance Details, Notification window to create an notification event.
-
Click Create Notification and select a QuickStart template like Instance Status Change to Stopped.
-
Enter Event Rule Name and select created topic from Task 7, and click Create Notification. Your notification event rule should be created successfully to a subscribed topic as shown in the following image.
Task 9: Validate Notification Event Rule
In this step you will stop the instance which will trigger the associated notification topic. You can also include your email address to see the notification payload.
-
Navigate to the created instance and click Stop.
-
Once the instance is stopped, you should get an email through the subscribed notification topic and the function should be triggered.
-
Confirm that you have received an email as shown in the following image.
-
Go to the Lark channel which you have used to include the webhook URL.
Note: If you notice this update message carefully from the received email, based on custom function we have included instance’s IP address using OCI sdk.
-
[Optional] You can also associate this notification topic with update function details as per console announcement details. You can follow these docs to learn more.
Note: You must modify the custom function code to ensure you are reading the correct JSON payload and modify accordingly. You can use the sample announcement JSON payload received from service teams.
Next Steps
This tutorial shows an end user how you can use a customized function to read notification JSON payload and using OCI SDKs libraries to update that payload and publish messages to common collaboration tools like Lark. You can expand this function to add additional details like Resource Tags, Network Details and more.
Related Links
- Overview of Vault
- Overview of Monitoring
- Overview of Console Announcements
- Overview of Container Registry
- Getting Started with Policies
Acknowledgments
- Author - Arun Poonia, Principal Solution Architect
More Learning Resources
Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.
For product documentation, visit Oracle Help Center.
Invoke OCI custom function with notification event and publish updated message to LARK collaboration tool
F85963-01
August 2023
Copyright © 2023, Oracle and/or its affiliates.