Using a DAG to read and write Amazon S3 data with DuckDB
You can use the following code example to write a directed acyclic graph (DAG) that uses DuckDB to generate, check, and summarize sample order data in Amazon S3. The DAG runs on your Amazon MWAA environment's workers and reads and writes files in Amazon S3 as your environment's execution role. The DAG has three tasks:
-
write_ordersgenerates a day of sample orders and writes them to Amazon S3 as a gzip-compressed CSV file, in a folder for the run's logical date. -
check_ordersreads the day's orders directly from Amazon S3 and counts duplicate order IDs and invalid quantities. You don't need to define a table first. -
summarize_orderswrites the day's orders, units, and revenue for each store to Amazon S3 as a Parquet file that you can query with other tools, such as Amazon Athena (Athena).
For more information about the SQL functions that this example uses, such as read_csv and COPY, see the DuckDB documentation
Version
You can use the code example on this page with Apache Airflow v3.3.1 and later in Python 3.12. For information about using DuckDB in an existing Apache Airflow v3.3.1 environment, see Version.
Prerequisites
To use the code example on this page, you need the following:
-
An Amazon MWAA environment using Apache Airflow v3.3.1 or later.
-
An Amazon S3 bucket for your data.
-
Permission to update your environment's execution role. For more information about the execution role, see Amazon MWAA execution role.
This example uses AWS services that might incur charges, including Amazon S3 storage and request costs for the sample orders and reports that the DAG creates.
Permissions
DuckDB uses your environment's execution role to access Amazon S3. To use the code example on this page, the execution role needs s3:GetObject, s3:PutObject, s3:ListBucket, and s3:GetBucketLocation on your data bucket. If your bucket uses an AWS Key Management Service (AWS KMS) key for encryption, the execution role also needs kms:Decrypt and kms:GenerateDataKey on that key.
To add DuckDB permissions to the execution role
-
Choose your environment's execution role, and add the following permissions policy to it. Replace
amzn-s3-demo-bucketwith the name of the bucket that stores your data. For more information about the execution role, see Amazon MWAA execution role.{ "Version": "2012-10-17", "Statement": [ { "Sid": "DuckDBData", "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": [ "arn:aws:s3:::amzn-s3-demo-bucket", "arn:aws:s3:::amzn-s3-demo-bucket/*" ] } ] }DuckDB needs
s3:ListBucketto expand wildcard paths, such asorders/*/*.csv.gz. If your DAGs only read data, you can removes3:PutObject.If the execution role is missing a required permission, the task fails with an HTTP 403 or access denied error. For more information, see Troubleshooting DuckDB tasks.
Dependencies
You don't need additional dependencies to use the code example on this page. Amazon MWAA environments that use Apache Airflow v3.3.1 or later include the DuckDB provider, the duckdb library, and the DuckDB extensions for Amazon S3 access.
Code example
To run the DuckDB Amazon S3 code example
Important
This example stores sample data in Amazon S3, so you incur charges for Amazon S3 storage and requests. The sample data is smaller than 1 MB for each DAG run. For more information, see Amazon S3 pricing
-
Copy the following Python code example, and save it locally as
duckdb_s3.py. Replaceamzn-s3-demo-bucketwith the name of your bucket.import pendulum from airflow.providers.duckdb.operators.duckdb import DuckDBExecuteQueryOperator from airflow.sdk import DAG DATA = "s3://amzn-s3-demo-bucket/duckdb-sample" with DAG( dag_id="duckdb_s3", schedule=None, start_date=pendulum.datetime(2026, 1, 1, tz="UTC"), catchup=False, ): # Generate 10,000 sample orders and write them to S3 as gzip-compressed CSV. write_orders = DuckDBExecuteQueryOperator( task_id="write_orders", sql=f""" COPY ( SELECT i AS order_id, (i % 10) + 1 AS store_id, (i % 5) + 1 AS quantity, round(5 + (i % 50) * 1.5, 2) AS unit_price FROM range(1, 10001) AS t(i) ) TO '{DATA}/orders/order_date={{{{ ds }}}}/orders.csv.gz' (FORMAT csv, HEADER true, COMPRESSION gzip) """, ) # Read the day's orders directly from S3 and check them. check_orders = DuckDBExecuteQueryOperator( task_id="check_orders", sql=f""" SELECT count(*) AS orders, count(*) - count(DISTINCT order_id) AS duplicate_order_ids, count(*) FILTER (WHERE quantity <= 0) AS non_positive_quantities FROM read_csv('{DATA}/orders/*/*.csv.gz', hive_partitioning = true) WHERE order_date = DATE '{{{{ ds }}}}' """, ) # Write a per-store summary of the day's orders to S3 as Parquet. summarize_orders = DuckDBExecuteQueryOperator( task_id="summarize_orders", sql=f""" COPY ( SELECT store_id, count(*) AS orders, sum(quantity) AS units, round(sum(quantity * unit_price), 2) AS revenue FROM read_csv('{DATA}/orders/*/*.csv.gz', hive_partitioning = true) WHERE order_date = DATE '{{{{ ds }}}}' GROUP BY store_id ORDER BY store_id ) TO '{DATA}/reports/daily_store_sales/order_date={{{{ ds }}}}/sales.parquet' (FORMAT parquet) """, ) write_orders >> check_orders >> summarize_ordersThe SQL uses a Python f-string, so the doubled braces (
{{{{ ds }}}}) render as the Apache Airflow template{{ ds }}. Apache Airflow replaces this template with the run's logical date. Withhive_partitioning = true, DuckDB reads theorder_datecolumn from the folder names.If a query fails, for example because of a missing permission or file, the operator raises an error, and Apache Airflow marks the task as failed. The error appears in the task log, and downstream tasks don't run. To retry failed tasks automatically, set
retrieson the task or in the DAG'sdefault_args. -
Upload
duckdb_s3.pyto thedagsfolder in your environment's Amazon S3 bucket. -
Open the Amazon MWAA console
, and choose Open Airflow UI for your environment. -
Trigger the
duckdb_s3DAG. The run might take a few minutes to complete. If the run fails, check the task log. For troubleshooting information, see Troubleshooting DuckDB tasks. -
Verify the results in the Apache Airflow UI after the run completes.
-
Choose the
check_orderstask, and then on the XCom tab, view the results. The results show 10,000 orders, with no duplicate order IDs and no invalid quantities. -
In your data bucket, open the
duckdb-sample/reports/daily_store_sales/folder, and confirm that it contains the Parquet file that thesummarize_orderstask wrote.
-
-
Clean up resources
When you finish, pause the
duckdb_s3DAG, deleteduckdb_s3.pyfrom yourdagsfolder, and delete theduckdb-sample/folder from your data bucket. If your other DAGs don't need them, remove theDuckDBDatapermissions from the execution role. For detailed steps, see Clean up resources.
Clean up resources
When you finish with this example, delete the resources that you created to avoid unwanted DAG runs and ongoing Amazon S3 charges. To complete these steps, you need access to your environment's Amazon S3 bucket, your data bucket, and the execution role. For more information about the DAGs folder, see Adding or updating DAGs.
To clean up resources
-
Open the Amazon MWAA console
, and choose Open Airflow UI for your environment. -
Pause the
duckdb_s3DAG. -
Delete
duckdb_s3.pyfrom thedagsfolder in your environment's Amazon S3 bucket. -
Delete the
duckdb-sample/folder, which contains the sample orders and reports, from your data bucket. -
If your other DAGs don't need them, remove the Amazon S3 permissions that you added to your environment's execution role.