ocaml-awso

awso - OCaml AWS client

Forked from awsm which is Copyright (c) Solvuu, Inc. Released under the MIT license.

Pure OCaml client for AWS. Code is auto-generated for all services based on the API declared in botocore. Higher level functions are sometimes provided on top of this base to (e.g.) support multi-part uploads to S3.

Features

Nearly all of AWS’s 400+ APIs are supported (generated from botocore 1.43.9, freshened 2026-05-17). Three I/O flavors:

There is additionally a command-line tool awso-cli for all APIs composed using Core.Command.

Getting started

opam install awso-eio     # Eio
opam install awso-lwt     # Lwt
opam install awso-async   # Async
opam install awso-sync    # synchronous (blocking)
opam install awso-cli     # umbrella CLI binary

Note: the AWS surface is massive and this package can take a lengthy amount of time and space to build.

Examples

See the examples directory.

Here is a short example that lists all EC2 instances using Eio:

(* ec2_describe_instances.ml *)
module Ec2 = Awso_ec2_eio

let print_row a b c d = Printf.printf "%-16s  %-15s  %-39s  %-20s\n" a b c d

let print_instance instance =
  let name =
    Option.bind instance.Ec2.Instance.tags (fun tags ->
      List.find_map
        (function
          | { Ec2.Tag.key = Some "Name"; value = Some v } -> Some v
          | _ -> None)
        tags)
  in
  let instance_type =
    match instance.instanceType with
    | Some it -> Ec2.InstanceType.to_string it
    | None -> ""
  in
  print_row
    instance_type
    (Option.value instance.publicIpAddress ~default:"")
    (Option.value instance.ipv6Address ~default:"")
    (Option.value name ~default:"")
;;

let main env =
  let cfg = Awso_eio.Cfg.get_exn ~env () in
  match Ec2.describe_instances ~cfg (Ec2.DescribeInstancesRequest.make ()) with
  | Error e ->
    failwith
      (Printf.sprintf
         "Ec2.describe_instances: %s"
         (Yojson.Safe.to_string (Ec2.Ec2_error.to_json e)))
  | Ok { reservations; _ } -> (
    let instances =
      reservations
      |> Option.value ~default:[]
      |> List.concat_map (function
        | { Ec2.Reservation.instances = None; _ } -> []
        | { instances = Some instances; _ } -> instances)
    in
    match instances with
    | [] -> print_endline "no instances"
    | instances ->
      print_row "instance-type" "public ipv4" "public ipv6" "name";
      print_row
        (String.make 16 '-')
        (String.make 15 '-')
        (String.make 39 '-')
        (String.make 20 '-');
      List.iter print_instance instances)
;;

let () = Eio_main.run main ;;
; dune
(executable
 (name ec2_describe_instances)
 (libraries awso-eio.ec2 eio_main))

Make sure to populate ~/.aws/config and ~/.aws/credentials in the usual way, then:

dune exec ./ec2_describe_instances.exe

Repository layout

Path Purpose Shipped to opam?
lib/runtime/awso/ Core runtime: auth, HTTP, config, regions Yes (awso)
lib/runtime/eio/ Eio backend Yes (awso-eio)
lib/runtime/async/ Async backend Yes (awso-async)
lib/runtime/lwt/ Lwt backend Yes (awso-lwt)
lib/runtime/sync/ Synchronous (blocking) backend, libcurl-based Yes (awso-sync)
lib/common/ Jane Street Core compatibility shim Yes (awso-common)
aws/<service>/ Auto-generated per-service bindings (~400 services) Yes (under awso, awso-async, awso-lwt, awso-sync)
awso-cli/ A bit like the Python aws cli Yes (awso-cli)
lib/codegen/ The code generator that produces aws/<service>/ from botocore JSON No: private library
vendor/botocore/ Vendored botocore JSON used by the codegen No
dogfood/ Important maintainer tools that use awso itself No
examples/ Example programs No, not installed

Why is the aws/ tree committed to git?

aws/<service>/ contains roughly 400 services worth of generated OCaml. We commit it on purpose so that opam install awso-async (or any sibling package) never has to run the codegen at install time. End users get a ~25 dependency build instead of ~50+: ppxlib, sedlex, ocamlgraph, and the rest of the codegen toolchain stay private to maintainers. The decision trades repo size for install-time simplicity and predictability.

Regeneration is a maintainer concern, but you can re-generate yourself like so:

make generate-code

After regenerating, commit the resulting diff alongside whatever change prompted it.

Documentation

Generate API docs locally with opam install odoc then make doc.