Amazonica
A comprehensive Clojure client for the entire Amazon AWS api.
Install / Use
/learn @mcohen01/AmazonicaREADME

Amazonica
A comprehensive Clojure client for the entire [Amazon AWS API][1].
Installation
Leiningen coordinates:
[amazonica "0.3.169"]
For Maven users:
add the following repository definition to your pom.xml:
<repository>
<id>clojars.org</id>
<url>http://clojars.org/repo</url>
</repository>
and the following dependency:
<dependency>
<groupId>amazonica</groupId>
<artifactId>amazonica</artifactId>
<version>0.3.169</version>
</dependency>
Supported Services
- Amplify
- API Gateway
- AppConfig
- Application Insights
- App Mesh
- Augmented AI
- Autoscaling
- Austocaling Plans
- Backup
- Batch
- Budgets
- Certificate Manager
- CloudDirectory
- CloudFormation
- CloudFront
- CloudSearch
- CloudSearchV2
- CloudSearchDomain
- CloudWatch
- CloudWatchEvents
- CodeBuild
- CodeCommit
- CodeDeploy
- CodePipeline
- CodeStar
- Cognito
- CognitoIdentityProviders
- Comprehend
- Compute Optimizer
- Config
- Connect
- CostAndUsageReport
- CostExplorer
- DatabaseMigrationService
- DataPipeline
- Data Exchange
- Data Sync
- Dax
- Detective
- DeviceFarm
- DirectConnect
- Directory
- DLM
- DocDB
- DynamoDBV2
- EC2
- EC2InstanceConnect
- ECR
- ECS
- ElastiCache
- ElasticBeanstalk
- ElasticFileSystem
- ElasticLoadBalancing
- ElasticMapReduce
- Elasticsearch
- ElasticTranscoder
- Event Bridge
- Forecast
- Fraud Detector
- GameLift
- Glacier
- Global Accelerator
- Glue
- GreenGrass
- Groundstation
- GuardDuty
- IdentityManagement
- Image Builder
- ImportExport
- IoT
- Kafka
- Kendra
- Kinesis
- Kinesis Analytics
- KinesisFirehose
- Kinesis Video Streams with WebRTC (Signaling Channels)
- KMS
- Lake Formation
- Lambda
- Lex
- Lightsail
- Logs
- MachineLearning
- Macie
- Managed Blockcahin
- MechanicalTurk
- MediaConvert
- MediaLive
- MediaPackage
- MediaStore
- MigrationHub
- Mobile
- MQ
- MSK (Managed Kafka)
- OpsWorks
- Personalize
- Pinpoint
- Pricing
- Polly
- QLDB
- Quicksight
- RDS
- Redshift
- Rekognition
- Route53
- Route53Domains
- S3
- Sagemaker
- Secrets Manager
- Security Hub
- Security Token
- ServerMigration
- ServiceCatalog
- Service Discovery
- Shield
- SimpleDB
- SimpleEmail
- SimpleSystemsManager
- SimpleWorkflow
- Snowball
- SNS
- SQS
- StepFunctions
- StorageGateway
- Support
- Textract
- Timestream
- Transcribe
- Transfer
- Translate
- WAF
- Workspaces
- XRay
Documentation
[Minimum Viable Snippet][9]:
(ns com.example
(:use [amazonica.aws.ec2]))
(describe-instances)
(create-snapshot :volume-id "vol-8a4857fa"
:description "my_new_snapshot")
Amazonica reflectively delegates to the Java client library, as such it supports the complete set of remote service calls implemented by each of the service-specific AWS client classes (e.g. AmazonEC2Client, AmazonS3Client, etc.), the documentation for which can be found in the [AWS Javadocs][2]. [cljdoc function references][25] are also available.
Reflection is used to create idiomatically named Clojure Vars in the library namespaces corresponding to the AWS service. camelCase Java methods become lower-case, hyphenated Clojure functions. So for example, if you want to create a snapshot of a running EC2 instance, you'd simply
(create-snapshot :volume-id "vol-8a4857fa"
:description "my_new_snapshot")
which delegates to the [createSnapshot()][3] method of AmazonEC2Client. If the Java method on the Amazon*Client takes a parameter, such as [CreateSnapshotRequest][4] in this case, the bean properties exposed via mutators of the form set* can be supplied as key-value pairs passed as arguments to the Clojure function.
All of the AWS Java apis (except S3) follow this pattern, either having a single implementation method which takes an AWS Java bean as its only argument, or being overloaded and having a no-arg implementation. The corresponding Clojure function will either require key-value pairs as arguments, or be variadic and allow a no-arg invocation.
For example, AmazonEC2Client's [describeImages()][7] method is overloaded, and can be invoked either with no args, or with a [DescribeImagesRequest][8]. So the Clojure invocation would look like
(describe-images)
or
(describe-images :owners ["self"]
:image-ids ["ami-f00f9699" "ami-e0d30c89"])
Conversion of Returned Types
java.util.Collections are converted to the corresponding Clojure collection type. java.util.Maps are converted to clojure.lang.IPersistentMaps, java.util.Lists are converted to clojure.lang.IPersistentVectors, etc.
java.util.Dates are automatically converted to Joda Time DateTime instances.
Amazon AWS object types are returned as Clojure maps, with conversion taking place recursively, so, "Clojure data all the way down."
For example, a call to
(describe-instances)
invokes a Java method on AmazonEC2Client which returns a com.amazonaws.services.ec2.model.DescribeInstancesResult. However, this is recursively converted to Clojure data, yielding a map of Reservations, like so:
{:owner-id "676820690883",
:group-names ["cx"],
:groups [{:group-name "cx", :group-id "sg-38f45150"}],
:instances
[{:instance-type "m1.large",
:kernel-id "aki-825ea7eb",
:hypervisor "xen",
:state {:name "running", :code 16},
:ebs-optimized false,
:public-dns-name "ec2-154-73-176-213.compute-1.amazonaws.com",
:root-device-name "/dev/sda1",
:virtualization-type "paravirtual",
:root-device-type "ebs",
:block-device-mappings
[{:device-name "/dev/sda1",
:ebs
{:status "attached",
:volume-id "vol-b0e519c3",
:attach-time #<DateTime 2013-03-21T22:00:56.000-07:00>,
:delete-on-termination true}}],
:network-interfaces [],
:public-ip-address "164.73.176.213",
:placement
{:availability-zone "us-east-1a",
:group-name "",
:tenancy "default"},
:private-ip-address "10.116.187.19",
:security-groups [{:group-name "cx", :group-id "sg-38f45150"}],
:state-transition-reason "",
:private-dns-name "ip-10-116-187-19.ec2.internal",
:instance-id "i-cefbe7a2",
:key-name "cxci",
:architecture "x86_64",
:client-token "",
:image-id "ami-baba68d3",
:ami-launch-index 0,
:monitoring {:state "disabled"},
:product-codes [],
:launch-time #<DateTime 2013-03-21T22:00:52.000-07:00>,
:tags [{:value "CXCI_nightly", :key "Name"}]}],
:reservation-id "r-8a23d6f7"}
If you look at the Reservation [Javadoc][10] you'll see that getGroups() returns a java.util.List of GroupIdentifiers, which is converted to a vector of maps containing keys :group-name and :group-id, under the :groups key. Ditto for :block-device-mappings and :tags, and so and so on...
Similar in concept to JSON unwrapping in Jackson, Amazonica supports root unwrapping of the returned data. So calling
; dynamodb
(list-tables)
by default would return
{:table-names ["TableOne" "TableTwo" "TableThree"]}
However, if you call
(set-root-unwrapping! true)
then single keyed top level maps will be "unwrapped" like so:
(list-tables)
=> ["TableOne" "TableTwo" "TableThree"]
The returned data can be "round tripped" as well. So the returned Clojure data structures can be supplied as arguments to function calls which delegate to Java methods taking the same object type as an argument. See the section below for more on this.
Argument Coercion
Coercion of any types that are part of the java.lang wrapper classes happens transparently. So for example, Clojure's preferred longs are automatically converted to ints where required.
Clojure data structures automatically participate in the Java Collections abstractions, and so no explicit coercion is necessary. Typically when service calls take collections as parameter arguments, as in the case above, the values in the collections are most often instances of the Java wrapper classes.
When complex objects consisting of types outside of those in the java.lang package are required as argument parameters, smart conversions are attempted based on the argument types of the underlying Java method signature. Methods requiring a java.util.Date argument can take Joda Time org.joda.time.base.AbstractInstants, longs, or Strings (default pattern is "yyyy-MM-dd"), with conversion happening automatically.
(set-date-format! "MM-dd-yyyy")
can be used to set the pattern supplied to the underlying java.text.SimpleDateFormat.
In cases where collection arguments contain instances of AWS "model" classes, Clojure maps will be converted to the appropriate AWS Java bean instance. So for example, [describeAvailabilityZones()][5] can take a [DescribeAvailabilityZonesRequest][6] which itself has a filters property, which is a java.util.List of com.amazonaws.services.ec2.model.Filters. Passing the filters argument would look like:
(describe-availability-zones :filters [{:name "environment"
:values ["de
