Collect Akamai Cloud Monitor logs

Supported in:

This document explains how to ingest Akamai Cloud Monitor (Load Balancer, Traffic Shaper, ADC) logs to Google Security Operations using AWS S3. Akamai pushes JSON events to your HTTPS endpoint; an API Gateway + Lambda receiver writes the events to S3 (JSONL, gz). The parser transforms the JSON logs into UDM. It extracts fields from the JSON payload, performs data type conversions, renames fields to match the UDM schema, and handles specific logic for custom fields and URL construction. It also incorporates error handling and conditional logic based on field presence.

Before you begin

Make sure you have the following prerequisites:

  • Google SecOps instance
  • Privileged access to Akamai Control Center and Property Manager
  • Privileged access to AWS*(S3, IAM, Lambda, API Gateway)

Configure AWS S3 bucket and IAM for Google SecOps

  1. Create Amazon S3 bucket following this user guide: Creating a bucket
  2. Save bucket Name and Region for future reference (for example, akamai-cloud-monitor).
  3. Create a user following this user guide: Creating an IAM user.
  4. Select the created User.
  5. Select the Security credentials tab.
  6. Click Create Access Key in the Access Keys section.
  7. Select Third-party service as the Use case.
  8. Click Next.
  9. Optional: add a description tag.
  10. Click Create access key.
  11. Click Download CSV file to save the Access Key and Secret Access Key for later use.
  12. Click Done.
  13. Select the Permissions tab.
  14. Click Add permissions in the Permissions policies section.
  15. Select Add permissions.
  16. Select Attach policies directly
  17. Search for and select the AmazonS3FullAccess policy.
  18. Click Next.
  19. Click Add permissions.

Configure the IAM policy and role for S3 uploads (Lambda)

  1. In the AWS Console, go to IAM > Policies > Create policy > JSON and paste the policy below.
  2. JSON Policy (replace akamai-cloud-monitor with your S3 bucket name):

    {
    "Version": "2012-10-17",
    "Statement": [
        {
        "Sid": "AllowPutAkamaiObjects",
        "Effect": "Allow",
        "Action": ["s3:PutObject"],
        "Resource": "arn:aws:s3:::akamai-cloud-monitor/*"
        }
    ]
    }
    
  3. Click Next > Create policy.

  4. Go to IAM > Roles > Create role > AWS service > Lambda.

  5. Attach the JSON policy.

  6. Name the role WriteAkamaiCMToS3Role and click Create role.

Create the Lambda function

Setting Value
Name akamai_cloud_monitor_to_s3
Runtime Python 3.13
Architecture x86_64
Execution role WriteAkamaiCMToS3Role
  1. After the function is created, open the Code tab, delete the stub and enter the following code (akamai_cloud_monitor_to_s3.py):

    #!/usr/bin/env python3
    # Lambda: Receive Akamai Cloud Monitor POST, write JSONL (gz) to S3
    
    import os, json, gzip, io, uuid, base64, datetime as dt
    import boto3
    
    S3_BUCKET  = os.environ["S3_BUCKET_NAME"]
    S3_PREFIX  = os.environ.get("S3_PREFIX", "akamai/cloud-monitor/json/").strip("/") + "/"
    INGEST_TOKEN = os.environ.get("INGEST_TOKEN")  # optional shared secret in URL query (?token=...)
    
    s3 = boto3.client("s3")
    
    def _write_jsonl_gz(objs: list) -> str:
        key = f"{dt.datetime.utcnow():%Y/%m/%d}/akamai-cloud-monitor-{uuid.uuid4()}.json.gz"
        buf = io.BytesIO()
        with gzip.GzipFile(fileobj=buf, mode="w") as gz:
            for o in objs:
                gz.write((json.dumps(o, separators=(",", ":")) + "n").encode())
        buf.seek(0)
        s3.upload_fileobj(
            buf,
            S3_BUCKET,
            f"{S3_PREFIX}{key}",
            ExtraArgs={
                "ContentType": "application/json",
                "ContentEncoding": "gzip",
            },
        )
        return f"s3://{S3_BUCKET}/{S3_PREFIX}{key}"
    
    def _parse_records_from_event(event) -> list:
        # HTTP API (Lambda proxy) event: body is a JSON string
        body = event.get("body") or ""
        if event.get("isBase64Encoded"):
            body = base64.b64decode(body).decode("utf-8", "replace")
        try:
            data = json.loads(body)
        except Exception:
            # accept line-delimited JSON as pass-through
            try:
                return [json.loads(line) for line in body.splitlines() if line.strip()]
            except Exception:
                return []
        if isinstance(data, list):
            return data
        if isinstance(data, dict):
            return [data]
        return []
    
    def lambda_handler(event, context=None):
        # Optional shared-secret verification via query parameter (?token=...)
        if INGEST_TOKEN:
            qs = event.get("queryStringParameters") or {}
            token = qs.get("token")
            if token != INGEST_TOKEN:
                return {"statusCode": 403, "body": "forbidden"}
    
        records = _parse_records_from_event(event)
        if not records:
            return {"statusCode": 204, "body": "no content"}
    
        key = _write_jsonl_gz(records)
        return {
            "statusCode": 200,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps({"ok": True, "s3_key": key, "count": len(records)}),
        }
    
  2. Go to Configuration > Environment variables > Edit.

  3. Click Add new environment variable and set the following values:

    Environment variables

    Key Example
    S3_BUCKET_NAME akamai-cloud-monitor
    S3_PREFIX akamai/cloud-monitor/json/
    INGEST_TOKEN random-shared-secret
  4. Go to Configuration > General configuration.

  5. Click Edit and set Timeout to 5 minutes (300 seconds).

  6. Click Save.

Create Amazon API Gateway (HTTPS endpoint for Akamai)

  1. In the AWS Console, go to API Gateway > Create API.
  2. Select HTTP API > Build.
  3. Provide the following configuration details:
    • Integrations: Choose Lambda and select akamai_cloud_monitor_to_s3.
    • Routes: Add ANY /{proxy+} or create a specific route (for example, POST /akamai/cloud-monitor).
    • Stages: Create or use $default.
  4. Deploy the API and copy the Invoke URL (for example, https://abc123.execute-api.<region>.amazonaws.com).

Configure Akamai Cloud Monitor to push logs

  1. In Akamai Control Center, open your Property in Property Manager.
  2. Click Add Rule > choose Cloud Management.
  3. Add Cloud Monitor Instrumentation and select required Datasets.
  4. Add Cloud Monitor Data Delivery.
    • Delivery Hostname: enter your API Gateway Invoke URL (for example, abc123.execute-api.<region>.amazonaws.com).
    • Delivery URL Path: your route plus an optional query token, for example: /akamai/cloud-monitor?token=<INGEST_TOKEN>.
  5. Save and Activate the property version.

Configure a feed in Google SecOps to ingest Akamai Cloud Monitor (S3 JSON)

  1. Go to SIEM Settings > Feeds.
  2. Click Add New Feed.
  3. In the Feed name field, enter a name for the feed (for example, Akamai Cloud Monitor — S3).
  4. Select Amazon S3 V2 as the Source type.
  5. Select Akamai Cloud Monitor as the Log type.
  6. Click Next.
  7. Specify values for the following input parameters:
    • S3 URI: s3://akamai-cloud-monitor/akamai/cloud-monitor/json/
    • Source deletion options: Whether to delete files and/or directories after transferring.
    • Maximum File Age: Includes files modified in the last number of days. Default is 180 days.
    • Access Key ID: A 20-character alphanumeric account access key (e.g., AKIAIOSFODNN7EXAMPLE).
    • Secret Access Key: A 40-character alphanumeric account secret access key (e.g., wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY).
    • Asset namespace: akamai.cloud_monitor
    • Ingestion labels: Labels are added to all the events from this feed (for example, source=akamai_cloud_monitor, format=json).
  8. Click Next.
  9. Review your new feed configuration in the Finalize screen, and then click Submit.

UDM Mapping Table

Log Field UDM Mapping Logic
accLang network.http.user_agent Directly mapped if not "-" or empty string.
city principal.location.city Directly mapped if not "-" or empty string.
cliIP principal.ip Directly mapped if not empty string.
country principal.location.country_or_region Directly mapped if not "-" or empty string.
cp additional.fields Mapped as a key-value pair with key "cp".
customField about.ip, about.labels, src.ip Parsed as key-value pairs. Special handling for "eIp" and "pIp" to map to src.ip and about.ip respectively. Other keys are mapped as labels within about.
errorCode security_result.summary, security_result.severity If present, sets security_result.severity to "ERROR" and maps the value to security_result.summary.
geo.city principal.location.city Directly mapped if city is "-" or empty string.
geo.country principal.location.country_or_region Directly mapped if country is "-" or empty string.
geo.lat principal.location.region_latitude Directly mapped, converted to float.
geo.long principal.location.region_longitude Directly mapped, converted to float.
geo.region principal.location.state Directly mapped.
id metadata.product_log_id Directly mapped if not empty string.
message.cliIP principal.ip Directly mapped if cliIP is empty string.
message.fwdHost principal.hostname Directly mapped.
message.reqHost target.hostname, target.url Used to construct target.url and extract target.hostname.
message.reqLen network.sent_bytes Directly mapped, converted to unsigned integer if totalBytes is empty or "-".
message.reqMethod network.http.method Directly mapped if reqMethod is empty string.
message.reqPath target.url Appended to target.url.
message.reqPort target.port Directly mapped, converted to integer if reqPort is empty string.
message.respLen network.received_bytes Directly mapped, converted to unsigned integer.
message.sslVer network.tls.version Directly mapped.
message.status network.http.response_code Directly mapped, converted to integer if statusCode is empty or "-".
message.UA network.http.user_agent Directly mapped if UA is "-" or empty string.
network.asnum additional.fields Mapped as a key-value pair with key "asnum".
network.edgeIP intermediary.ip Directly mapped.
network.network additional.fields Mapped as a key-value pair with key "network".
network.networkType additional.fields Mapped as a key-value pair with key "networkType".
proto network.application_protocol Used to determine network.application_protocol.
queryStr target.url Appended to target.url if not "-" or empty string.
referer network.http.referral_url, about.hostname Directly mapped if not "-". Extracted hostname is mapped to about.hostname.
reqHost target.hostname, target.url Used to construct target.url and extract target.hostname.
reqId metadata.product_log_id, network.session_id Directly mapped if id is empty string. Also mapped to network.session_id.
reqMethod network.http.method Directly mapped if not empty string.
reqPath target.url Appended to target.url if not "-".
reqPort target.port Directly mapped, converted to integer.
reqTimeSec metadata.event_timestamp, timestamp Used to set event timestamp.
start metadata.event_timestamp, timestamp Used to set event timestamp if reqTimeSec is empty string.
statusCode network.http.response_code Directly mapped, converted to integer if not "-" or empty string.
tlsVersion network.tls.version Directly mapped.
totalBytes network.sent_bytes Directly mapped, converted to unsigned integer if not empty or "-".
type metadata.product_event_type Directly mapped.
UA network.http.user_agent Directly mapped if not "-" or empty string.
version metadata.product_version Directly mapped.
xForwardedFor principal.ip Directly mapped if not "-" or empty string.
(Parser Logic) metadata.vendor_name Set to "Akamai".
(Parser Logic) metadata.product_name Set to "DataStream".
(Parser Logic) metadata.event_type Set to "NETWORK_HTTP".
(Parser Logic) metadata.product_version Set to "2" if version is empty string.
(Parser Logic) metadata.log_type Set to "AKAMAI_CLOUD_MONITOR".
(Parser Logic) network.application_protocol Determined from proto or message.proto. Set to "HTTPS" if either contains "HTTPS" (case-insensitive), "HTTP" otherwise.
(Parser Logic) security_result.severity Set to "INFORMATIONAL" if errorCode is "-" or empty string.
(Parser Logic) target.url Constructed from protocol, reqHost (or message.reqHost), reqPath (or message.reqPath), and queryStr.

Need more help? Get answers from Community members and Google SecOps professionals.