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.
Set Up Events Based Automated Email Notifications for Cost Analysis Reports Using Oracle Cloud Native Framework
Introduction
Every organization running their workloads on cloud, is very keen on understanding their cost expenditure on cloud, and want to report it in a meaningful way. For this, Oracle Cloud Infrastructure (OCI) provides a functionality using which you can create automated reports based on cost, and then get those reports in OCI Object Storage.
For these cost reports, teams would also like to set up automation in a way that the corresponding team will get the notification as soon as the report is getting generated. With this, they would also like to have the link to the reports as part of the notification. You can set up all of this automation using the Oracle Cloud Native Framework.
Objectives
-
Set up report generation for OCI costs and generate events based on the report generation.
-
Configure all automation using cloud native managed services on OCI.
Prerequisites
-
Required permissions in Oracle Cloud Infrastructure Identity and Access Management (OCI IAM) to create dynamic group, policy, buckets, functions, set up events and notification service.
-
Access to all required components to view their Oracle Cloud Identifiers (OCIDs) and other information.
-
Access to the OCI Cloud Shell to create, deploy and invoke functions.
Task 1: Set up the Required OCI IAM policies and Dynamic Groups
-
Log in to the OCI Console, go to Dynamic Group and create a new dynamic group.
-
Enter the compartment OCID in Rule 1. Your OCI Functions should exist in the same compartment.
-
Go to OCI IAM policies and create a policy with the following statement.
allow dynamic-group dynamic_group_name to manage ons-family in compartment compartment_name allow dynamic-group dynamic_group_name to manage object-family in compartment compartment_name
Note:
-
Please use more specific IAM policy as suitable to your use case. See the example as below:
allow dynamic-group dynamic_group_name to use ons-topics in compartment compartment_name where request.operation = 'PublishMessage'
allow dynamic-group dynamic_group_name to manage buckets in compartment compartment_name where all {request.operation = 'CreatePreauthenticatedRequest', target.bucket.name = 'bucket_name'}
-
Task 2: Set up an OCI Object Storage Bucket and Create a Scheduled Report
-
Create a bucket using OCI Console. We will use this bucket to push our reports. Make sure the bucket you have created is private as this will make sure your content is secure from public access as well as the Emit Object Events is enabled on this bucket, as this will allow events to emit when there is a new object pushed.
-
Go to Cost Management, Scheduled Reports, and create a scheduled report. Enter Name, Description, Start date and Recurrence.
Select the bucket to which you want to publish your reports. To select the bucket from the drop-down menu, you have to add the policy as shown in the following image so that metering service will have access to the bucket created.
-
After the bucket and scheduled report is created, you can check the history of reports executed, and you will also see the reports are published to the selected OCI Object Storage bucket.
Task 3: Create an OCI Notifications Topic and Add a Subscription
-
Create an OCI Notifications topic which is required to send the email notification. Go to Notifications, Topic and create a topic.
-
Create a subscription in the same topic and add an email to which the notification will be sent.
Now, the OCI Notifications topic is configured.
Task 4: Create and Deploy the Function Code
Go to functions/applications and create a function. This function will use the OCI IAM dynamic group and policy to access the OCI Object Storage bucket and perform the required operation (create PAR URL for the scheduled report file, and send notifications with it), when the function is triggered by the event emitted.
There are a wide range of approaches which can be used to develop OCI Functions, with quickstart guides which walk you through setting up a development environment using OCI Cloud Shell, an OCI virtual machine, or running locally. The images in the remainder of this guide will show OCI Cloud Shell, but any of these approaches are valid. We have used Python to code the required logic, but you can use another language and change the logic as needed.
-
To write the code, open the
requirement.txt
andfunc.py
file in the OCI Cloud Shell. -
The following is the function code, it uses the OCI resource principals so that you do not have to keep your OCI credentials. Remember to replace the variables for your creations, like bucket name, topic OCID and so on.
-
requirements.txt
.fdk>=0.1.71 oci>=2.112.0
-
func.yaml
.schema_version: 20180708 name: reportsauto version: 0.0.6 runtime: python build_image: fnproject/python:3.11-dev run_image: fnproject/python:3.11 entrypoint: /python/bin/fdk /function/func.py handler memory: 256
-
func.py
.import io import json import logging import oci from datetime import datetime from fdk import response def handler(ctx, data: io.BytesIO = None): try: body = json.loads(data.getvalue()) logging.getLogger().info(body.get("eventType")) logging.getLogger().info(body.get("data").get("resourceName")) except (Exception, ValueError) as ex: logging.getLogger().info('error parsing json payload: ' + str(ex)) logging.getLogger().info("Inside Python Hello World function") initiateFn(body.get("data").get("resourceName")) return response.Response( ctx, response_data=json.dumps( {"message": "Function Executed!"}), headers={"Content-Type": "application/json"} ) def initiateFn(uploaded_file): logging.getLogger().info("Reached initiate function...") signer = oci.auth.signers.get_resource_principals_signer() object_storage_client = oci.object_storage.ObjectStorageClient(config={}, signer=signer) namespace_name="orasenatdplt01" bucket_name="demobucket" now = datetime.now() par_name = "PAR_Request_" + str(now).split('.')[0].replace(" ", "_").replace(":", "_").replace("-", "_") create_preauthenticated_request_response = object_storage_client.create_preauthenticated_request( namespace_name=namespace_name, bucket_name=bucket_name, create_preauthenticated_request_details=oci.object_storage.models.CreatePreauthenticatedRequestDetails( name=par_name, access_type="ObjectRead", time_expires=datetime.strptime( "2037-06-05T04:25:22.344Z", "%Y-%m-%dT%H:%M:%S.%fZ"), object_name=uploaded_file)) callNotificationAPI(create_preauthenticated_request_response.data) # Function to call Notification API as soon as an object push is detected def callNotificationAPI(parData): logging.getLogger().info("Trigger notification as object push is detected...") topicOcid = "ocid1.onstopic.oc1.iad.amaaaaxxxxxxxxxamn4" signer = oci.auth.signers.get_resource_principals_signer() ons_client = oci.ons.NotificationDataPlaneClient(config={}, signer=signer) publish_message_response = ons_client.publish_message( topic_id=topicOcid, message_details=oci.ons.models.MessageDetails( body="OCI Notification Service - Cost Report File is created: " + str(parData.full_path), title="OCI Notification Service - Cost Report File is created"), message_type="RAW_TEXT") logging.getLogger().info("Triggered notification as object push is detected...")
-
-
Update the function in registry and deploy the latest code using the following command on the OCI Cloud Shell.
fn -v deploy — app reportsauto
-
Once deployed, your function is ready to be invoked. You can try to test using the function invoke command and ensure the function is working as required.
DEBUG=1 fn invoke reportsauto your_app_name
Note:
-
OCI supports resource/instance principals to avoid providing any user related information while executing the function. So this task is needed to make sure the function has the correct permission set to call the APIs, failing which the function will not work properly.
-
(Optional ) You can also check the Debug mode for troubleshooting. For example: run the following command from OCI Cloud Shell to invoke and test the function if it is working as expected.
DEBUG=1 fn invoke e2e-function-demo e2e-fn-streaming
-
Task 5: Set up OCI Events Rules
-
Go to the OCI Console, click Application Integration, Events Service, Rules, and Create event rule. Enter a rule condition and add a function created in Task 4 as the trigger action.
-
Enter Display Name and Description.
-
In the Rule condition section, select first condition as Event Type, Object Storage, Object - Create and another condition as Attribute, bucketName, demoBucket.
-
Connect event with the function through the Actions section. Select Function Compartment, Function Application and the function deployed in Task 4.
-
Click Save changes to save the event rule. This will invoke the function when the condition matches for the emitted event from the OCI Object Storage bucket.
Related Links
Acknowledgments
- Authors - Lovelesh Saxena (Principal Software Engineering Architect), Mike Fung (Master Principal Cloud Architect), Tim Lee (Staff Cloud Engineer)
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.
Set Up Events Based Automated Email Notifications for Cost Analysis Reports Using Oracle Cloud Native Framework
F99112-01
May 2024