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 Oracle Cloud Infrastructure Functions to Send Cost Usage Report using Email
Introduction
Cost usage report can be customized as per requirement and we can create the scheduled cost usage report. As per the schedule, report will be pulled and saved in the selected Oracle Cloud Infrastructure (OCI) Object Storage bucket.
To send this report using email, we need to deploy OCI Functions. OCI Functions will be triggered based on the event rule created in OCI. Whenever the file gets stored in the bucket, the object upload event triggers the function and OCI Functions will have the automation to create pre authenticated request URL to the object. This URL will be sent through email.
Email can be received either by OCI Email Delivery service or by OCI Notifications services. Both the configurations are explained in this tutorial.
Objectives
- Set up OCI Functions to get the scheduled cost usage report using email.
Prerequisites
-
Access permissions in Oracle Cloud Infrastructure Identity and Access Management (OCI IAM) to create dynamic group, OCI IAM policy, create functions to have the scheduled cost usage report in the bucket.
-
Access to all required components to view their Oracle Cloud Identifiers (OCIDs) and other relevant information.
-
Access to OCI Cloud Shell so that you can create, deploy and invoke the functions.
-
Network resources for OCI Functions needs to available. VCN and subnet configuration.
Task 1: Set up Required OCI IAM Policies and Dynamic Groups
-
Log in to the OCI Console. For more information, see Log in to Oracle Cloud.
-
Go to OCI IAM dynamic group and create a new dynamic group with the following rule.
-
Go to OCI IAM policies and create an OCI IAM policy with the following statements.
allow dynamic-group dynamicgroup_name to manage buckets in compartment CompartmentName where all {target.bucket.name='bucket_name'} allow dynamic-group dynamicgroup_name to manage objects in compartment CompartmentName where all {target.bucket.name='bucket_name'} Allow service metering_overlay to manage objects in compartment CompartmentName where all {target.bucket.name='bucket_name', any {request.permission='OBJECT_CREATE', request.permission='OBJECT_DELETE', request.permission='OBJECT_READ'}}
To send email using OCI Notifications service, the following statement needs to be added in the OCI IAM policy to access OCI Notifications topic.
allow dynamic-group dynamicgroup_name to use ons-topics in compartment CompartmentName
Task 2: Create Scheduled Cost Usage Report
-
Go to Billing and Cost Management and select Cost Management. Create your own customized cost usage report under Cost Analysis and click Apply to save the report.
-
Click Scheduled reports and create a new scheduled report with your bucket information. You can schedule this as per your requirement.
As per the schedule, report will be automatically saved in given OCI Object Storage bucket.
Task 3: Create and Deploy the Function Code
-
To create application, go to Developer Services, click Application and create an application in the available network.
-
Go to functions/applications and create a function. This function will use the OCI IAM dynamic group and policy to access the event. Follow the steps on screen to create the function. We have used Python to code the required logic, but you can use your own language and change the logic as needed. To write the code, open the function
requirement.txt
file andfunc.py
file in OCI Cloud Shell and write the code. -
Copy the following Python code in
func.py
and text file inrequirement.txt
file.-
func.py
:-
Using OCI Email Delivery:
Note: Add respective email ID and Simple Mail Transfer Protocol (SMTP) details.
import oci import json import io import logging import smtplib from os.path import basename from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.utils import COMMASPACE, formatdate from datetime import datetime import ssl import email.utils from email.message import EmailMessage def handler(ctx, data: io.BytesIO = None): funDataStr = data.read().decode('utf-8') #Convert the log data to json funData = json.loads(funDataStr) #Set up OCI configuration using resource principal signer = oci.auth.signers.get_resource_principals_signer() #Create Object Storage client object_storage_client = oci.object_storage.ObjectStorageClient({},signer=signer) # Extract object details from the event object_name = funData['data']['resourceName'] namespace = funData['data']['additionalDetails']['namespace'] bucket_name = funData['data']['additionalDetails']['bucketName'] # Download the object #get_object_response = object_storage_client.get_object(namespace, bucket_name, object_name) #object_content = str(get_object_response.data.text) create_preauthenticated_request_response = object_storage_client.create_preauthenticated_request( namespace_name = namespace, bucket_name = bucket_name, create_preauthenticated_request_details=oci.object_storage.models.CreatePreauthenticatedRequestDetails( name = "prestorage", access_type = "ObjectRead", time_expires = datetime.strptime( "2024-06-05T04:25:22.344Z", "%Y-%m-%dT%H:%M:%S.%fZ"), object_name = object_name)) #logging.getLogger().info(create_preauthenticated_request_response.data) logging.getLogger().info("created pre authenticated url") string = str(create_preauthenticated_request_response.data) #response = create_preauthenticated_request_response.data().decode('utf-8') url = json.loads(string) logging.getLogger().info(url) temporary_url = f"{url['full_path']}" logging.getLogger().info(temporary_url) #Set your email credentials sender_email = "test@domain" sender_name = "Tester" #Set the recipient email address recipient_email = "xyz@oracle.com" USERNAME_SMTP = "SMTPOCID" password_smtp = "************" HOST = "smtp_email" PORT = 587 #Create the email content subject = "Cost Usage Report" body = temporary_url message = EmailMessage() message['From'] = email.utils.formataddr((sender_name, sender_email)) message['To'] = recipient_email message['Subject'] = subject message.set_content(body) try: server = smtplib.SMTP(HOST, PORT) server.ehlo() server.starttls(context=ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=None, capath=None)) server.ehlo() server.login(USERNAME_SMTP, password_smtp) logging.getLogger().info("SMTP server logged in") server.sendmail(sender_email, recipient_email, message.as_string()) #server.send_message(message) server.close() except Exception as e: logging.getLogger().info(f"Error: {e}") else: logging.getLogger().info('Email sent successfully!')
-
Using OCI Notifications:
Note: Update the topic OCID (steps to create notification is explained in Task 5).
import oci import json import io import logging import smtplib from os.path import basename from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.utils import COMMASPACE, formatdate from datetime import datetime import ssl import email.utils from email.message import EmailMessage def handler(ctx, data: io.BytesIO = None): funDataStr = data.read().decode('utf-8') # Convert the log data to json funData = json.loads(funDataStr) # Set up OCI configuration using resource principal signer = oci.auth.signers.get_resource_principals_signer() # Create Object Storage client object_storage_client = oci.object_storage.ObjectStorageClient({},signer=signer) ons_client = oci.ons.NotificationDataPlaneClient({},signer=signer) # Extract object details from the event object_name = funData['data']['resourceName'] namespace = funData['data']['additionalDetails']['namespace'] bucket_name = funData['data']['additionalDetails']['bucketName'] # Download the object create_preauthenticated_request_response = object_storage_client.create_preauthenticated_request( namespace_name = namespace, bucket_name = bucket_name, create_preauthenticated_request_details=oci.object_storage.models.CreatePreauthenticatedRequestDetails( name = "prestorage", access_type = "ObjectRead", time_expires = datetime.strptime( "2024-06-05T04:25:22.344Z", "%Y-%m-%dT%H:%M:%S.%fZ"), object_name = object_name)) #logging.getLogger().info(create_preauthenticated_request_response.data) logging.getLogger().info("created pre authenticated url") string = str(create_preauthenticated_request_response.data) #response = create_preauthenticated_request_response.data().decode('utf-8') url = json.loads(string) logging.getLogger().info(url) temporary_url = f"{url['full_path']}" logging.getLogger().info(temporary_url) #to send mail using notification publish_message_response = ons_client.publish_message( topic_id="ocid1.onstopic.oc1.iad.************", message_details=oci.ons.models.MessageDetails( body=temporary_url, title="Cost-usage report"), message_type="RAW_TEXT") logging.getLogger().info(publish_message_response) logging.getLogger().info('Email sent successfully!')
-
-
requirement.txt
:fdk>=0.1.68 oci
-
-
Update the function in registry and deploy the latest code using the following command on the OCI Cloud Shell.
fn -v deploy — app App_name
Once deployed, your function is ready to be invoked. To trigger the function, event rule needs to be created.
Task 4: Set up the Event Rule
-
Create an event rule in OCI. Under Observability and Management, click Event services, Rules and Create Rule. Enter the matching rule as shown in the following image with attribute as your bucket ID.
Note: Created OCI Object Storage bucket should be enabled with emit object events.
-
Add Action as shown in the following image with function which is created in Task 2.
Now, trigger for the function is created.
Task 5: Set up the Email Configuration using OCI Email Delivery or OCI Notifications
-
Using OCI Email Delivery (This needs public DNS domain):
-
Create an email domain in OCI. Go to Developer Services and select Email Delivery. Create the email domain as per available public DNS. Email domain name should be the same as the public DNS name.
-
Configure the domain keys identified mail (DKIM) and approved senders in the email domain by adding records in your public DNS records. For more information about setting up email domain and configuration, see Step-by-step instructions to send email with OCI Email Delivery.
-
Create the SMTP credentials for your user. Generate the SMTP credential and save it for future use. The same approved email ID and SMTP credentials must be provided in the
func.py
file.
-
-
Using OCI Notifications Service:
-
To create a topic, go to Developer Services. Under Application Integration, click Notifications.
-
In the Topics page, click Create Topic and enter the required information.
-
Once the topic is created, create Subscription. Enter Protocol as
Email
and required email id. -
Email will be received to the endpoint to confirm the subscription.
-
Testing
Upload the object or any file into the bucket and automatically function will be triggered using event rule.
Related Links
Acknowledgments
- Author - Samratha S P (Senior 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 Oracle Cloud Infrastructure Functions to Send Cost Usage Report using Email
F96277-03
December 2024