I tried setting up CloudWatch Logs integrated logging for ALB with CloudFormation

I tried setting up CloudWatch Logs integrated logging for ALB with CloudFormation

ALB access logs, connection logs, and health check logs to S3, and access logs to CloudWatch Logs and Amazon Data Firehose — I tried writing the configuration to deliver these in CloudFormation. You can write out the three delivery destinations with nearly identical resource structures. I also verified the behavior when specifying `RecordFields` in the template.
2026.07.28

This page has been translated by machine translation. View original

Introduction

On July 23, 2026, Application Load Balancer (ALB) logs became compatible with CloudWatch Logs integrated logging (Vended Logs). The three supported log types are access logs, connection logs, and health check logs. You can choose from S3, CloudWatch Logs, and Amazon Data Firehose as delivery destinations.

https://aws.amazon.com/jp/about-aws/whats-new/2026/07/amazon-cloudwatch-logs/

https://dev.classmethod.jp/articles/alb-access-logs-cloudwatch-logs-vended/

The third link is the previous article, which introduces the differences from the legacy method (S3 delivery via ALB attributes) and the configuration procedure using CLI.

In this article, the three resources — Delivery Source / Delivery Destination / Delivery — are described in CloudFormation. All three delivery destinations (S3, CloudWatch Logs, and Firehose) are configured simultaneously, and since all fields are required for RecordFields, notes on specifying them are also covered.

Verification Details

Verification Environment

Item Value
Region ap-northeast-1
ALB internet-facing, 2 listeners: HTTP:80 and HTTPS:443
Origin EC2 (Amazon Linux 2023, nginx installed via UserData)
Security Group ALB allows only 1 address from the verification source
Log Types ALB_ACCESS_LOGS / ALB_CONNECTION_LOGS / ALB_HEALTH_CHECK_LOGS

The HTTPS listener was set up to output TLS-related fields in the connection logs.

The legacy S3 delivery via ALB attributes is left disabled for all three, as follows. All log output in this verification goes through integrated logging.

access_logs.s3.enabled        = false
health_check_logs.s3.enabled  = false
connection_logs.s3.enabled    = false

Resources were divided into two templates: a foundation template and a delivery template.

Resource Where Created
ALB / EC2 / Target Group / Listener Foundation template (this article only describes the configuration)
S3 bucket and bucket policy Delivery template
CloudWatch Logs log group Delivery template
Firehose delivery stream and execution role Delivery template
Delivery Source / Destination / Delivery Delivery template
ACM certificate (for HTTPS listener) Pre-created. Passed as a parameter outside the template

The Firehose delivery stream is of type DirectPut, and the backend writes to a different prefix in the same S3 bucket. Buffer is 60 seconds / 5MB, GZIP compression, no record transformation.

Configuring Three Delivery Destinations in CloudFormation

AWS::Logs::DeliverySource represents the log origin (ALB and log type), AWS::Logs::DeliveryDestination represents the delivery destination (S3 bucket, log group, or Firehose stream), and these two are linked via AWS::Logs::Delivery.

Full delivery template
AWSTemplateFormatVersion: '2010-09-09'
Description: ALB CloudWatch Logs integrated logging - delivery to S3 / CloudWatch Logs / Firehose

Parameters:
  LoadBalancerArn:
    Type: String
    Description: ARN of the target ALB
  BucketName:
    Type: String
    Description: S3 bucket that receives the vended logs
  BillingGroupPrefix:
    Type: String
    Default: kiro-alb-cwl
    Description: Prefix of the CmBillingGroup cost allocation tag values

Resources:

  # ---------------------------------------------------------------
  # Destination 1: S3
  # ---------------------------------------------------------------
  LogsBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-s3-bucket

  LogsBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref LogsBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: AWSLogsDeliveryWrite
            Effect: Allow
            Principal:
              Service: delivery.logs.amazonaws.com
            Action: s3:PutObject
            Resource: !Sub arn:aws:s3:::${BucketName}/*
            Condition:
              StringEquals:
                aws:SourceAccount: !Ref AWS::AccountId
              ArnLike:
                aws:SourceArn: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*
          - Sid: AWSLogsDeliveryAclCheck
            Effect: Allow
            Principal:
              Service: delivery.logs.amazonaws.com
            Action: s3:GetBucketAcl
            Resource: !Sub arn:aws:s3:::${BucketName}
            Condition:
              StringEquals:
                aws:SourceAccount: !Ref AWS::AccountId

  S3Destination:
    Type: AWS::Logs::DeliveryDestination
    Properties:
      Name: alb-logs-s3-destination
      DeliveryDestinationType: S3
      OutputFormat: json
      DestinationResourceArn: !GetAtt LogsBucket.Arn
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-dest-s3

  # ---------------------------------------------------------------
  # Destination 2: CloudWatch Logs
  # ---------------------------------------------------------------
  AccessLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: /aws/vendedlogs/alb-access-logs
      RetentionInDays: 1
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-loggroup

  CwlDestination:
    Type: AWS::Logs::DeliveryDestination
    Properties:
      Name: alb-logs-cwl-destination
      DeliveryDestinationType: CWL
      OutputFormat: json
      DestinationResourceArn: !GetAtt AccessLogGroup.Arn
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-dest-cwl

  # ---------------------------------------------------------------
  # Destination 3: Data Firehose
  # ---------------------------------------------------------------
  FirehoseToS3Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: firehose.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: write-to-logs-bucket
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:AbortMultipartUpload
                  - s3:GetBucketLocation
                  - s3:GetObject
                  - s3:ListBucket
                  - s3:ListBucketMultipartUploads
                  - s3:PutObject
                Resource:
                  - !Sub arn:aws:s3:::${BucketName}
                  - !Sub arn:aws:s3:::${BucketName}/*
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-iam

  FirehoseStream:
    Type: AWS::KinesisFirehose::DeliveryStream
    Properties:
      DeliveryStreamName: alb-logs-firehose
      DeliveryStreamType: DirectPut
      ExtendedS3DestinationConfiguration:
        BucketARN: !Sub arn:aws:s3:::${BucketName}
        RoleARN: !GetAtt FirehoseToS3Role.Arn
        Prefix: firehose/access-logs/
        ErrorOutputPrefix: firehose/errors/
        CompressionFormat: GZIP
        BufferingHints:
          IntervalInSeconds: 60
          SizeInMBs: 5
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-firehose

  FirehoseDestination:
    Type: AWS::Logs::DeliveryDestination
    Properties:
      Name: alb-logs-firehose-destination
      DeliveryDestinationType: FH
      OutputFormat: json
      DestinationResourceArn: !GetAtt FirehoseStream.Arn
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-dest-firehose

  # ---------------------------------------------------------------
  # Delivery sources (one per log type)
  # ---------------------------------------------------------------
  AccessLogSource:
    Type: AWS::Logs::DeliverySource
    Properties:
      Name: alb-access-logs
      LogType: ALB_ACCESS_LOGS
      ResourceArn: !Ref LoadBalancerArn
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-source

  ConnectionLogSource:
    Type: AWS::Logs::DeliverySource
    Properties:
      Name: alb-connection-logs
      LogType: ALB_CONNECTION_LOGS
      ResourceArn: !Ref LoadBalancerArn
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-source

  HealthCheckLogSource:
    Type: AWS::Logs::DeliverySource
    Properties:
      Name: alb-health-check-logs
      LogType: ALB_HEALTH_CHECK_LOGS
      ResourceArn: !Ref LoadBalancerArn
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-source

  # ---------------------------------------------------------------
  # Deliveries (source x destination)
  # RecordFields is omitted, so the service default field set and
  # order are used.
  # ---------------------------------------------------------------
  AccessLogToS3:
    Type: AWS::Logs::Delivery
    Properties:
      DeliverySourceName: !Ref AccessLogSource
      DeliveryDestinationArn: !GetAtt S3Destination.Arn
      S3SuffixPath: access-logs/{account-id}/{region}/{yyyy}/{MM}/{dd}/
      S3EnableHiveCompatiblePath: false
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-dlv-s3-access

  ConnectionLogToS3:
    Type: AWS::Logs::Delivery
    Properties:
      DeliverySourceName: !Ref ConnectionLogSource
      DeliveryDestinationArn: !GetAtt S3Destination.Arn
      S3SuffixPath: connection-logs/{account-id}/{region}/{yyyy}/{MM}/{dd}/
      S3EnableHiveCompatiblePath: false
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-dlv-s3-connection

  HealthCheckLogToS3:
    Type: AWS::Logs::Delivery
    Properties:
      DeliverySourceName: !Ref HealthCheckLogSource
      DeliveryDestinationArn: !GetAtt S3Destination.Arn
      S3SuffixPath: health-check-logs/{account-id}/{region}/{yyyy}/{MM}/{dd}/
      S3EnableHiveCompatiblePath: false
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-dlv-s3-healthcheck

  AccessLogToCwl:
    Type: AWS::Logs::Delivery
    Properties:
      DeliverySourceName: !Ref AccessLogSource
      DeliveryDestinationArn: !GetAtt CwlDestination.Arn
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-dlv-cwl-access

  AccessLogToFirehose:
    Type: AWS::Logs::Delivery
    Properties:
      DeliverySourceName: !Ref AccessLogSource
      DeliveryDestinationArn: !GetAtt FirehoseDestination.Arn
      Tags:
        - Key: CmBillingGroup
          Value: !Sub ${BillingGroupPrefix}-dlv-fh-access

Outputs:
  BucketName:
    Value: !Ref LogsBucket
  LogGroupName:
    Value: !Ref AccessLogGroup
  FirehoseStreamName:
    Value: !Ref FirehoseStream

Even with three types of delivery destinations, the way AWS::Logs::DeliveryDestination itself is written is mostly the same. The main properties that differ per destination in this template are DeliveryDestinationType and DestinationResourceArn. Additionally, the surrounding resource configurations differ for S3, CloudWatch Logs, and Firehose.

First, DeliveryDestinationType. The values to specify are S3 for S3, CWL for CloudWatch Logs, and FH for Firehose. Notations such as CloudWatchLogs or Firehose are not used.

  S3Destination:
    Properties:
      DeliveryDestinationType: S3

  CwlDestination:
    Properties:
      DeliveryDestinationType: CWL

  FirehoseDestination:
    Properties:
      DeliveryDestinationType: FH

Next, DestinationResourceArn. Pass the ARN of the S3 bucket, CloudWatch Logs log group, or Firehose delivery stream respectively. Since all are resources created within the same template, they can be referenced using !GetAtt, so there is no need to construct the ARN as a string.

  S3Destination:
    Properties:
      DestinationResourceArn: !GetAtt LogsBucket.Arn

  CwlDestination:
    Properties:
      DestinationResourceArn: !GetAtt AccessLogGroup.Arn

  FirehoseDestination:
    Properties:
      DestinationResourceArn: !GetAtt FirehoseStream.Arn

For S3 destinations, the bucket policy grants delivery.logs.amazonaws.com permission for s3:PutObject and s3:GetBucketAcl. The caller is restricted using aws:SourceAccount and aws:SourceArn.

          - Sid: AWSLogsDeliveryWrite
            Effect: Allow
            Principal:
              Service: delivery.logs.amazonaws.com
            Action: s3:PutObject
            Resource: !Sub arn:aws:s3:::${BucketName}/*
            Condition:
              StringEquals:
                aws:SourceAccount: !Ref AWS::AccountId
              ArnLike:
                aws:SourceArn: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*

For CloudWatch Logs destinations, a resource policy is required on the log group that grants delivery.logs.amazonaws.com permission for logs:CreateLogStream and logs:PutLogEvents. In this template, AWS::Logs::ResourcePolicy is not explicitly defined. If the person configuring it has the necessary permissions, AWS automatically sets this policy when creating the Delivery (Logs sent to CloudWatch Logs).

Note that an execution role for the Firehose delivery stream itself to write to the backend S3 is required, and it is defined as FirehoseToS3Role in the template.

On the other hand, a custom role trusting delivery.logs.amazonaws.com for Firehose destinations was not needed. After removing that role from the template and updating the stack, delivery to all three destinations continued. CloudWatch Logs uses the service-linked role AWSServiceRoleForLogDelivery for direct writes to Firehose (Using service-linked roles for CloudWatch Logs).

Apart from the permissions topic, cost allocation tags can be set on all three resources: Delivery Source, Delivery Destination, and Delivery. With the intent of tracking CloudWatch Logs integration costs, CmBillingGroup was assigned to all resources in the template.

The list of deliveries confirmed with aws logs describe-deliveries after deployment is as follows.

Delivery ID Delivery Source Delivery Destination Type recordFields
aaaa1111bbbb2222 alb-access-logs alb-logs-s3-destination S3 Not specified
cccc3333dddd4444 alb-connection-logs alb-logs-s3-destination S3 Not specified
eeee5555ffff6666 alb-health-check-logs alb-logs-s3-destination S3 Not specified
gggg7777hhhh8888 alb-access-logs alb-logs-cwl-destination CWL Not specified
iiii9999jjjj0000 alb-access-logs alb-logs-firehose-destination FH Not specified

The access log Delivery Source alb-access-logs is linked to all three destinations: S3, CWL, and FH. A single Delivery Source can be linked to multiple delivery destinations and delivered in parallel.

Verifying Logs Delivered to the Three Destinations

Requests were sent to the ALB via HTTP and HTTPS, and the logs delivered to each of the three destinations were verified. OutputFormat is set to json for all.

In S3, objects were created with the following key.

AWSLogs/123456789012/elasticloadbalancing/access-logs/123456789012/ap-northeast-1/2026/07/27/
  123456789012_elasticloadbalancing_ap-northeast-1_app.example-alb.0123456789abcdef_20260727T1450Z_198.51.100.10_0a1b2c3d.log.gz

The AWSLogs/<account ID>/elasticloadbalancing/ prefix at the beginning is added by the service side. Since {account-id} was included in S3SuffixPath, the account ID appears twice in a single key. To avoid duplication, remove {account-id} from S3SuffixPath.

Access log delivered to S3 (HTTPS request)
{"request_line":"GET https://example-alb-1234567890.ap-northeast-1.elb.amazonaws.com:443/secure-1 HTTP/2.0","user_agent":"curl/8.18.0","trace_id":"Root=1-68a1b2c3-4d5e6f708192a3b4c5d6e7f8","domain_name":"example-alb-1234567890.ap-northeast-1.elb.amazonaws.com","chosen_cert_arn":"arn:aws:acm:ap-northeast-1:123456789012:certificate/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee","actions_executed":"forward","redirect_url":"-","error_reason":"-","target_port_list":"10.0.0.100:80","target_status_code_list":"404","classification":"-","classification_reason":"-","transformed_host":"-","transformed_uri":"-","request_transform_status":"-","type":"h2","time":"2026-07-27T14:51:41.522397Z","elb":"app/example-alb/0123456789abcdef","client_port":"203.0.113.10:4985","target_port":"10.0.0.100:80","request_processing_time":"0.002","target_processing_time":"0.002","response_processing_time":"0.000","elb_status_code":"404","target_status_code":"404","received_bytes":"79","sent_bytes":"3565","ssl_cipher":"ECDHE-RSA-AES128-GCM-SHA256","ssl_protocol":"TLSv1.2","target_group_arn":"arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/example-tg/fedcba9876543210","matched_rule_priority":"0","request_creation_time":"2026-07-27T14:51:41.518000Z","conn_trace_id":"TID_a1b2c3d4e5f60718293a4b5c6d7e8f90","ip_address":"198.51.100.10"}

In CloudWatch Logs, records were delivered to the log group /aws/vendedlogs/alb-access-logs created in the template. Each record is stored as one event.

Access log delivered to CloudWatch Logs (HTTP request)
{"request_line":"GET http://example-alb-1234567890.ap-northeast-1.elb.amazonaws.com:80/ HTTP/1.1","user_agent":"curl/8.18.0","trace_id":"Root=1-68a1b2c3-4d5e6f708192a3b4c5d6e7f8","domain_name":"-","chosen_cert_arn":"-","actions_executed":"forward","redirect_url":"-","error_reason":"-","target_port_list":"10.0.0.100:80","target_status_code_list":"200","classification":"-","classification_reason":"-","transformed_host":"-","transformed_uri":"-","request_transform_status":"-","type":"http","time":"2026-07-27T14:47:06.948295Z","elb":"app/example-alb/0123456789abcdef","client_port":"203.0.113.10:4988","target_port":"10.0.0.100:80","request_processing_time":"0.002","target_processing_time":"0.003","response_processing_time":"0.000","elb_status_code":"200","target_status_code":"200","received_bytes":"133","sent_bytes":"296","ssl_cipher":"-","ssl_protocol":"-","target_group_arn":"arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/example-tg/fedcba9876543210","matched_rule_priority":"0","request_creation_time":"2026-07-27T14:47:06.943000Z","conn_trace_id":"TID_a1b2c3d4e5f60718293a4b5c6d7e8f90","ip_address":"198.51.100.11"}

Firehose delivery was confirmed in S3 objects (under firehose/access-logs/2026/07/27/14/) specified as the backend of the stream. The field structure of the records is the same as for other delivery destinations.

Access log delivered to S3 via Firehose (HTTPS request)
{"request_line":"GET https://example-alb-1234567890.ap-northeast-1.elb.amazonaws.com:443/secure-13 HTTP/2.0","user_agent":"curl/8.18.0","trace_id":"Root=1-68a1b2c3-4d5e6f708192a3b4c5d6e7f8","domain_name":"example-alb-1234567890.ap-northeast-1.elb.amazonaws.com","chosen_cert_arn":"arn:aws:acm:ap-northeast-1:123456789012:certificate/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee","actions_executed":"forward","redirect_url":"-","error_reason":"-","target_port_list":"10.0.0.100:80","target_status_code_list":"404","classification":"-","classification_reason":"-","transformed_host":"-","transformed_uri":"-","request_transform_status":"-","type":"h2","time":"2026-07-27T14:51:42.746886Z","elb":"app/example-alb/0123456789abcdef","client_port":"203.0.113.10:4985","target_port":"10.0.0.100:80","request_processing_time":"0.000","target_processing_time":"0.000","response_processing_time":"0.000","elb_status_code":"404","target_status_code":"404","received_bytes":"79","sent_bytes":"3565","ssl_cipher":"ECDHE-RSA-AES128-GCM-SHA256","ssl_protocol":"TLSv1.2","target_group_arn":"arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/example-tg/fedcba9876543210","matched_rule_priority":"0","request_creation_time":"2026-07-27T14:51:42.746000Z","conn_trace_id":"TID_a1b2c3d4e5f60718293a4b5c6d7e8f90","ip_address":"198.51.100.10"}

In the connection logs delivered to S3, records for HTTPS listener destinations (where listener_port is 443) had values in tls_protocol and tls_cipher.

{"leaf_client_cert_subject":"-","time":"2026-07-27T14:47:07.123363Z","client_ip":"203.0.113.10","client_port":"4985","listener_port":"443","tls_protocol":"TLSv1.2","tls_cipher":"ECDHE-RSA-AES128-GCM-SHA256","tls_handshake_latency":"0.015","leaf_client_cert_validity":"-","leaf_client_cert_serial_number":"-","tls_verify_status":"Success","conn_trace_id":"TID_a1b2c3d4e5f60718293a4b5c6d7e8f90","tls_keyexchange":"secp256r1","elb":"app/example-alb/0123456789abcdef","ip_address":"198.51.100.10"}

In this verification, when the target log types for ALB were checked with describe-configuration-templates, the available values for OutputFormat per delivery destination type were as follows.

Destination Available Formats
S3 plain / json / w3c / parquet
CloudWatch Logs plain / json
Firehose plain / json / raw

In this verification, charges may be incurred for the ALB and EC2, the Firehose delivery stream, Vended Logs delivery to CloudWatch Logs and Firehose, and storage in S3 and CloudWatch Logs. The delivery itself from ALB integrated logging to S3 is free. After verification, the stack was deleted.

Notes on Specifying RecordFields

In AWS::Logs::Delivery, RecordFields allows you to specify which fields to output. For the three ALB log types verified in ap-northeast-1 this time, all fields were mandatory. Therefore, in the configuration at this point, RecordFields can only practically be used to control output order. The field definitions confirmed via aws logs describe-configuration-templates for the three ALB log types are as follows.

Log Type Number of Fields Of Which mandatory
ALB_ACCESS_LOGS 34 34
ALB_CONNECTION_LOGS 15 15
ALB_HEALTH_CHECK_LOGS 10 10

All fields in every log type were mandatory: true. In the configuration verified at this point, selecting or excluding fields is not possible.

When actually deploying with only 2 of the 10 ALB_HEALTH_CHECK_LOGS fields specified — time and status_code — the ResourceStatusReason in the stack events showed the following error.

Resource handler returned message: "Mandatory record fields are missing
(Service: CloudWatchLogs, Status Code: 400, Request ID: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee)"
(HandlerErrorCode: InvalidRequest)

Running the same specification with aws logs create-delivery also failed in the same way.

An error occurred (ValidationException) when calling the CreateDelivery operation:
Mandatory record fields are missing

If RecordFields is not written, the output will use the service's default field set and order. Since field selection is not possible in the configuration verified this time, the options in the template are either "enumerate all fields to control the order" or "omit the RecordFields property entirely."

The remaining use case is controlling output order. Separately from the template shown, a Destination with OutputFormat set to plain and a Delivery with RecordFields enumerated were added, delivering the 10 fields of ALB_HEALTH_CHECK_LOGS in the official default order and in a reordered arrangement respectively.

Delivery ID Destination Log Group Format RecordFields
kkkk1111llll2222 /aws/vendedlogs/alb-hc-plain-default plain 10 items, default order
mmmm3333nnnn4444 /aws/vendedlogs/alb-hc-plain-reordered plain 10 items, reordered
oooo5555pppp6666 /aws/vendedlogs/alb-hc-json-reordered json 10 items, reordered

The specified orders are as follows.

Default order:   type, time, latency, target_addr, target_group_id, status, status_code, reason_code, elb, ip_address
Reordered:       status_code, reason_code, status, time, type, latency, target_addr, target_group_id, elb, ip_address

The following are records from the same entry with a matching time.

plain / default order

http 2026-07-27T14:45:17.556303Z 0.00587012 10.0.0.100:80 example-tg PASS 200 - app/example-alb/0123456789abcdef 198.51.100.11

plain / reordered

200 - PASS 2026-07-27T14:45:17.556303Z http 0.00587012 10.0.0.100:80 example-tg app/example-alb/0123456789abcdef 198.51.100.11

json / reordered

{"status_code":"200","reason_code":"-","status":"PASS","time":"2026-07-27T14:45:17.556303Z","type":"http","latency":"0.00587012","target_addr":"10.0.0.100:80","target_group_id":"example-tg","elb":"app/example-alb/0123456789abcdef","ip_address":"198.51.100.11"}

In plain format, the columns are rearranged in the specified order, and the first column changed from http (type) to 200 (status_code). Since plain format has no column names, parsers that read by column number will have mismatched value mappings. If existing parsers read by column number based on the official default order, the order in RecordFields must be aligned with the official documentation or the definitions in describe-configuration-templates.

In the JSON output verified this time as well, keys appeared in the specified order. However, on the consumer side, keys should be referenced by key name rather than by order of appearance. The JSON output in default order is omitted from this post.

Summary

ALB's CloudWatch Logs integrated logging can be managed as IaC by combining three CloudFormation resources. It is an option worth considering when you want to expand delivery destinations for monitoring or downstream processing purposes while maintaining storage to S3. I also plan to separately verify methods for managing ALB CloudWatch Logs integration log settings in a separate stack, as well as cross-account management approaches.

Share this article

AWSのお困り事はクラスメソッドへ