Note:

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

Prerequisites

Task 1: Set up Required OCI IAM Policies and Dynamic Groups

  1. Log in to the OCI Console. For more information, see Log in to Oracle Cloud.

  2. Go to OCI IAM dynamic group and create a new dynamic group with the following rule.

    Create Dynamic Group

  3. 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

  1. 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.

    Create Cost report

  2. Click Scheduled reports and create a new scheduled report with your bucket information. You can schedule this as per your requirement.

    Create Scheduled report

    As per the schedule, report will be automatically saved in given OCI Object Storage bucket.

Task 3: Create and Deploy the Function Code

  1. To create application, go to Developer Services, click Application and create an application in the available network.

  2. 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 and func.py file in OCI Cloud Shell and write the code.

    Create Function in cloud shell and code editor

  3. Copy the following Python code in func.py and text file in requirement.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
      
  4. 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

  1. 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.

    Create Event Rule

  2. Add Action as shown in the following image with function which is created in Task 2.

    Action

    Now, trigger for the function is created.

Task 5: Set up the Email Configuration using OCI Email Delivery or OCI Notifications

Testing

Upload the object or any file into the bucket and automatically function will be triggered using event rule.

Acknowledgments

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.