Collect Terraform Enterprise logs

Supported in:

This document explains how to ingest Terraform Enterprise logs to Google Security Operations using Google Cloud Storage V2.

Terraform Enterprise (TFE) is a self-hosted infrastructure as code platform that generates application logs containing audit events for workspace operations, policy checks, state changes, and user activity. Audit events are embedded within the TFE application logs and marked with the [Audit Log] tag. Because TFE stores these logs locally on the application server, you must export them to a Google Cloud Storage (GCS) bucket and then configure a Google SecOps feed to ingest them.

Before you begin

Make sure you have the following prerequisites:

  • A Google SecOps instance
  • A Google Cloud project with Cloud Storage API enabled
  • Permissions to create and manage GCS buckets
  • Permissions to manage IAM policies on GCS buckets
  • Administrative access to the Terraform Enterprise server (SSH or console access)
  • Access to TFE application logs on the server file system (typically at /var/log/terraform-enterprise/ or accessible via replicatedctl app logs)

Create a Google Cloud Storage bucket

  1. Go to the Google Cloud Console.
  2. Select your project or create a new one.
  3. In the navigation menu, go to Cloud Storage > Buckets.
  4. Click Create bucket.
  5. Provide the following configuration details:

    Setting Value
    Name your bucket Enter a globally unique name (for example, terraform-enterprise-logs)
    Location type Choose based on your needs (Region, Dual-region, Multi-region)
    Location Select the location closest to your Google SecOps instance (for example, us-central1)
    Storage class Standard (recommended for frequently accessed logs)
    Access control Uniform (recommended)
    Protection tools Optional: Enable object versioning or retention policy
  6. Click Create.

Configure automated export of TFE application logs to GCS

Terraform Enterprise embeds audit log entries within its application logs. Audit events are marked with the [Audit Log] string and can be filtered from the application log output. Choose one of the following approaches to automate the export.

Terraform Enterprise supports external log forwarding using a built-in Fluent Bit-based mechanism. You can configure TFE to forward application logs to a GCS bucket.

  1. Access the TFE administration console.
  2. Go to Settings > Logging.
  3. Enable External Logging.
  4. Configure the Fluent Bit output to use the GCS plugin:

    [OUTPUT]
        Name                       gcs
        Match                      *
        Bucket                     terraform-enterprise-logs
        Object_key_format          audit-logs/tfe_logs_%Y%m%d_%H%M%S.json
        Content_Type               application/json
    
  5. Save the configuration and restart the TFE services if prompted.

Option 2: Compute Engine VM with cron (for direct log collection)

If the TFE server runs on Google Compute Engine or has network connectivity to GCP, configure a cron job on the TFE server to export filtered audit logs to GCS.

  1. Install the Google Cloud CLI on the TFE server.
  2. Create a service account for the export:

    1. In the GCP Console, go to IAM & Admin > Service Accounts.
    2. Click Create Service Account.
    3. Provide the following configuration details:
      • Service account name: Enter tfe-log-export-sa
      • Service account description: Enter Service account for TFE log export to GCS
    4. Click Create and Continue.
    5. Add the Storage Object Admin role.
    6. Click Done.
    7. Download a JSON key for the service account and copy it to the TFE server.
  3. Create an export script on the TFE server (/opt/tfe-export/export_logs.sh):

    #!/usr/bin/env bash
    set -euo pipefail
    
    BUCKET="terraform-enterprise-logs"
    PREFIX="audit-logs"
    LOG_DIR="/var/log/terraform-enterprise"
    EXPORT_DIR="/tmp/tfe-export"
    TIMESTAMP=$(date -u +%Y%m%d_%H%M%S)
    
    mkdir -p "$EXPORT_DIR"
    
    # Filter audit log entries from TFE application logs
    grep '\[Audit Log\]' "$LOG_DIR"/*.log > "$EXPORT_DIR/tfe_audit_${TIMESTAMP}.json" 2>/dev/null || true
    
    if [ -s "$EXPORT_DIR/tfe_audit_${TIMESTAMP}.json" ]; then
        gcloud storage cp "$EXPORT_DIR/tfe_audit_${TIMESTAMP}.json" \
            "gs://${BUCKET}/${PREFIX}/"
        echo "Uploaded tfe_audit_${TIMESTAMP}.json to gs://${BUCKET}/${PREFIX}/"
    else
        echo "No audit log entries found."
    fi
    
    rm -rf "$EXPORT_DIR"
    
  4. Make the script executable and schedule it with cron:

    chmod +x /opt/tfe-export/export_logs.sh
    (crontab -l ; echo "0 * * * * /opt/tfe-export/export_logs.sh >> /var/log/tfe-export.log 2>&1") | crontab -
    

Option 3: Storage Transfer Service (for file-based log collection)

If TFE logs are written to a file system accessible via a Storage Transfer Agent, use Storage Transfer Service to move them to GCS.

  1. In the GCP Console, go to Storage Transfer Service.
  2. Click Create transfer job.
  3. Select POSIX filesystem as the source.
  4. Follow the instructions to install the Storage Transfer Agent on the TFE server or a machine with access to the TFE log directory.
  5. Configure the transfer job:

    Setting Value
    Source directory Path to TFE log directory (for example, /var/log/terraform-enterprise/)
    Destination bucket terraform-enterprise-logs
    Destination path audit-logs/
    Schedule Set a recurring schedule (for example, every hour)
  6. Click Create.

Retrieve the Google SecOps service account

  1. Go to SIEM Settings > Feeds.
  2. Click Add New Feed.
  3. Click Configure a single feed.
  4. In the Feed name field, enter a name for the feed (for example, Terraform Enterprise Logs).
  5. Select Google Cloud Storage V2 as the Source type.
  6. Select Terraform Enterprise Audit as the Log type.
  7. Click Get Service Account. A unique service account email will be displayed, for example:

    chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com
    
  8. Copy this email address for use in the next step.

Grant IAM permissions to the Google SecOps service account

The Google SecOps service account needs Storage Object Viewer role on your GCS bucket.

  1. Go to Cloud Storage > Buckets.
  2. Click on your bucket name (for example, terraform-enterprise-logs).
  3. Go to the Permissions tab.
  4. Click Grant access.
  5. Provide the following configuration details:
    • Add principals: Paste the Google SecOps service account email (for example, chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com).
    • Assign roles: Select Storage Object Viewer.
  6. Click Save.

Configure the Google SecOps feed

  1. Go to SIEM Settings > Feeds.
  2. Click Add New Feed.
  3. Click Configure a single feed.
  4. In the Feed name field, enter a name for the feed (for example, Terraform Enterprise Logs).
  5. Select Google Cloud Storage V2 as the Source type.
  6. Select Terraform Enterprise Audit as the Log type.
  7. Click Next.
  8. Specify values for the following input parameters:

    • Storage bucket URL: Enter the GCS bucket URI:

      gs://terraform-enterprise-logs/audit-logs/
      
      • Replace terraform-enterprise-logs with your GCS bucket name.
      • Replace audit-logs with your configured prefix path.
    • Source deletion option: Select the deletion option according to your preference:

      • Never: Never deletes any files after transfers (recommended for testing).
      • Delete transferred files and empty directories: Deletes files and empty directories after successful transfer.

    • Maximum File Age: Include files modified in the last number of days (default is 180 days).

    • Asset namespace: The asset namespace.

    • Ingestion labels: The label to be applied to the events from this feed.

  9. Click Next.

  10. Review your new feed configuration in the Finalize screen, and then click Submit.

UDM mapping table

Log Field UDM Mapping Logic
action_label additional.fields Merged
allow_config_generation_label additional.fields Merged
allow_empty_apply_label additional.fields Merged
auto_apply_label additional.fields Merged
can_apply_label additional.fields Merged
can_cancel_label additional.fields Merged
can_comment_label additional.fields Merged
can_discard_label additional.fields Merged
can_force_cancel_label additional.fields Merged
can_force_execute_label additional.fields Merged
can_override_policy_check_label additional.fields Merged
comments_label additional.fields Merged
cost_estimated_at_label additional.fields Merged
cost_estimating_at_label additional.fields Merged
dd_span_id_label additional.fields Merged
dd_trace_id_label additional.fields Merged
has_changes_label additional.fields Merged
is_cancelable_label additional.fields Merged
is_confirmable_label additional.fields Merged
is_destroy_label additional.fields Merged
is_discardable_label additional.fields Merged
is_force_cancelable_label additional.fields Merged
jsonPayload_auth_source_label additional.fields Merged
jsonPayload_db_label additional.fields Merged
jsonPayload_dd_env_label additional.fields Merged
jsonPayload_dd_service_label additional.fields Merged
jsonPayload_dd_span_id_label additional.fields Merged
jsonPayload_dd_trace_id_label additional.fields Merged
jsonPayload_dd_version_label additional.fields Merged
jsonPayload_ddsource_label additional.fields Merged
jsonPayload_duration_label additional.fields Merged
jsonPayload_format_label additional.fields Merged
jsonPayload_node_id_label additional.fields Merged
jsonPayload_selinux_context_label additional.fields Merged
jsonPayload_systemd_cgroup_label additional.fields Merged
jsonPayload_systemd_slice_label additional.fields Merged
jsonPayload_view_label additional.fields Merged
json_image_name_label additional.fields Merged
json_syslog_identifier_label additional.fields Merged
json_transport_label additional.fields Merged
json_uid_label additional.fields Merged
msg_label additional.fields Merged
plan_only_label additional.fields Merged
plan_queueable_at_label additional.fields Merged
plan_queued_at_label additional.fields Merged
planned_and_finished_at_label additional.fields Merged
planned_at_label additional.fields Merged
planning_at_label additional.fields Merged
policychecks_label additional.fields Merged
post_plan_completed_at_label additional.fields Merged
post_plan_running_at_label additional.fields Merged
queuing_at_label additional.fields Merged
refresh_label additional.fields Merged
refresh_only_label additional.fields Merged
resource_label additional.fields Merged
runevents_label additional.fields Merged
save_plan_label additional.fields Merged
source_label additional.fields Merged
source_realtime_timestamp_label additional.fields Merged
taskstages_label additional.fields Merged
trigger_reason_label additional.fields Merged
updated_at_label additional.fields Merged
uuid_label additional.fields Merged
data.attributes.created-at metadata.event_timestamp Parsed as ISO8601
receiveTimestamp metadata.event_timestamp Parsed as ISO8601
time metadata.event_timestamp Parsed as MMM dd HH:mm:ss
time_stamp metadata.event_timestamp Parsed as yyyy-MM-dd HH:mm:ss
time_zone metadata.event_timestamp Parsed as yyyy-MM-dd HH:mm:ss
timestamp metadata.event_timestamp Parsed as yyyy-MM-dd HH:mm:ss.SSSSSS+Z.Z
event_type metadata.event_type Directly mapped
has_principal metadata.event_type Mapped: trueSTATUS_UPDATE, trueNETWORK_HTTP, trueNETWORK_CONNECTION
has_principal_user metadata.event_type Mapped: trueUSER_UNCATEGORIZED
has_user metadata.event_type Mapped: trueUSER_RESOURCE_ACCESS, trueUSER_UNCATEGORIZED
type metadata.product_event_type Directly mapped
id metadata.product_log_id Directly mapped
insertId metadata.product_log_id Directly mapped
data.attributes.terraform-version metadata.product_version Directly mapped
vault_version metadata.product_version Directly mapped
logName metadata.url_back_to_product Directly mapped
method network.http.method Directly mapped
status network.http.response_code Renamed/mapped
user_agent network.http.user_agent Directly mapped
request_id network.session_id Directly mapped
jsonPayload.machine_id principal.asset.asset_id Directly mapped
dd_service principal.asset.hostname Directly mapped
hostname principal.asset.hostname Directly mapped
actor_ip principal.asset.ip Merged
sha principal.file.sha1 Directly mapped
dd_service principal.hostname Directly mapped
hostname principal.hostname Directly mapped
jsonPayload.hostname principal.hostname Directly mapped
src_host principal.hostname Directly mapped
actor_ip principal.ip Merged
remote_ip principal.ip Merged
port principal.port Renamed/mapped
src_port principal.port Renamed/mapped
jsonPayload.cap_effective principal.process.access_mask Renamed/mapped
jsonPayload.cmdline principal.process.command_line Directly mapped
jsonPayload.exe principal.process.file.full_path Directly mapped
jsonPayload.comm principal.process.file.names Merged
jsonPayload.pid principal.process.pid Directly mapped
jsonPayload_container_tag_label principal.resource.attribute.labels Merged
jsonPayload.container_id_full principal.resource.product_object_id Directly mapped
resource_id principal.resource.product_object_id Directly mapped
resource.type principal.resource.resource_subtype Directly mapped
auth_impersonator_id_label principal.user.attribute.labels Merged
client_token_accessor_label principal.user.attribute.labels Merged
client_token_label principal.user.attribute.labels Merged
mount_accessor_label principal.user.attribute.labels Merged
mount_point_label principal.user.attribute.labels Merged
namespace_id_label principal.user.attribute.labels Merged
organization_id_label principal.user.attribute.labels Merged
request_id_label principal.user.attribute.labels Merged
write_timestamp_label principal.user.attribute.labels Merged
role principal.user.attribute.roles Merged
organization principal.user.department Merged
jsonPayload.gid principal.user.group_identifiers Merged
auth.entity_id principal.user.product_object_id Directly mapped
auth.display_name principal.user.user_display_name Directly mapped
actor principal.user.userid Directly mapped
auth.accessor_id principal.user.userid Directly mapped
auth.entity_id principal.user.userid Directly mapped
request.client_id principal.user.userid Directly mapped
user principal.user.userid Directly mapped
record.resource.action security_result.action_details Directly mapped
request.operation security_result.action_details Directly mapped
data.attributes.status security_result.category_details Merged
apply_detection_field_id security_result.detection_fields Merged
apply_detection_field_related security_result.detection_fields Merged
auth_accessor_label security_result.detection_fields Merged
auth_client_token_label security_result.detection_fields Merged
auth_metadata_role_label security_result.detection_fields Merged
auth_policy_results_allowed_label security_result.detection_fields Merged
auth_token_type_label security_result.detection_fields Merged
comments_related_label security_result.detection_fields Merged
configuration_version_detection_field_id security_result.detection_fields Merged
configuration_version_detection_field_related security_result.detection_fields Merged
cost_estimate_detection_field_id security_result.detection_fields Merged
cost_estimate_detection_field_related security_result.detection_fields Merged
data_id_label security_result.detection_fields Merged
data_links_self_label security_result.detection_fields Merged
detection_fields_organization_id security_result.detection_fields Merged
id_label security_result.detection_fields Merged
plan_detection_field_id security_result.detection_fields Merged
plan_detection_field_related security_result.detection_fields Merged
policy_checks_related_label security_result.detection_fields Merged
run_events_related_label security_result.detection_fields Merged
task_stages_related_label security_result.detection_fields Merged
token_ttl_label security_result.detection_fields Merged
workspace_detection_field_id security_result.detection_fields Merged
jsonPayload.priority security_result.priority_details Directly mapped
severity security_result.severity Mapped: INFOINFORMATIONAL
severity_detail security_result.severity Mapped: (?i)InfoINFORMATIONAL, (?i)ErrorERROR, (?i)WarningMEDIUM
request.remote_address target.asset.ip Merged
target_ip target.asset.ip Merged
request.remote_address target.ip Merged
target_ip target.ip Merged
labels_namespace target.namespace Directly mapped
request.remote_port target.port Renamed/mapped
labels_location target.resource.attribute.cloud.availability_zone Directly mapped
region target.resource.attribute.cloud.availability_zone Directly mapped
provider target.resource.attribute.cloud.environment Mapped: awsAMAZON_WEB_SERVICES
cluster_size_label target.resource.attribute.labels Merged
cluster_tier_label target.resource.attribute.labels Merged
hcp_product_label target.resource.attribute.labels Merged
image_tag_label target.resource.attribute.labels Merged
instance-id_label target.resource.attribute.labels Merged
organization_id_label target.resource.attribute.labels Merged
cluster_id target.resource.name Directly mapped
jsonPayload.container_name target.resource.name Directly mapped
name target.resource.name Directly mapped
record.resource.id target.resource.name Directly mapped
project_id target.resource.product_object_id Directly mapped
resource.meta.project_id target.resource.product_object_id Directly mapped
record.resource.type target.resource.resource_subtype Directly mapped
target_resource_ancestors target.resource_ancestors Merged
path target.url Directly mapped
request.path target.url Directly mapped
mount_accessor_label target.user.attribute.labels Merged
mount_class_label target.user.attribute.labels Merged
mount_point_label target.user.attribute.labels Merged
mount_running_plugin_version_label target.user.attribute.labels Merged
mount_type_label target.user.attribute.labels Merged
replication_cluster_label target.user.attribute.labels Merged
N/A metadata.event_type Constant: USER_RESOURCE_ACCESS
N/A metadata.product_name Constant: TERRAFORM_ENTERPRISE
N/A metadata.vendor_name Constant: TERRAFORM_ENTERPRISE
N/A security_result.severity Constant: INFORMATIONAL
N/A target.resource.attribute.cloud.environment Constant: AMAZON_WEB_SERVICES

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