Skip to content

Instantly share code, notes, and snippets.

@matthewdowney
Created March 11, 2023 01:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewdowney/eb5ae210627e16a6794f6a408e42cc11 to your computer and use it in GitHub Desktop.
Save matthewdowney/eb5ae210627e16a6794f6a408e42cc11 to your computer and use it in GitHub Desktop.
Using the AWS CDK from Clojure
(ns deploy
(:require [clojure.java.io :as io])
(:import (software.amazon.awscdk App CfnOutput$Builder Stack)
(software.amazon.awscdk.services.apigatewayv2.alpha AddRoutesOptions HttpApi$Builder HttpMethod)
(software.amazon.awscdk.services.apigatewayv2.authorizers.alpha HttpIamAuthorizer)
(software.amazon.awscdk.services.apigatewayv2.integrations.alpha HttpAlbIntegration)
(software.amazon.awscdk.services.ec2 Vpc$Builder)
(software.amazon.awscdk.services.ecs Cluster$Builder ContainerImage)
(software.amazon.awscdk.services.ecs.patterns ApplicationLoadBalancedFargateService$Builder ApplicationLoadBalancedTaskImageOptions)
(software.amazon.awscdk.services.iam AnyPrincipal)
(software.amazon.awscdk.services.secretsmanager Secret)))
;; A CDK 'app' is the root of a tree of CDK 'constructs', which can be
;; CloudFormation stacks or (trees of) AWS resources.
;; https://docs.aws.amazon.com/cdk/v2/guide/constructs.html#constructs_tree
(def app (App.))
;; The CloudFormation stack containing the resources. Stateful resources could
;; be separated out into a different stack, to better control possible
;; operations that would destroy existing state (e.g. in a db).
;; https://docs.aws.amazon.com/cdk/v2/guide/best-practices.html#best-practices-apps-separate
(def stack (Stack. app "AccountingStack"))
;;; The resources themselves
(def vpc (-> (Vpc$Builder/create stack "vpc") (.maxAzs 3) (.natGateways 1) .build))
;; ECS cluster (used by fargate)
(def ecs-cluster
(-> (Cluster$Builder/create stack "ecs-cluster") (.vpc vpc) .build))
;; The API keys that the server needs
(def api-conf (read-string (slurp (io/resource "conf.edn"))))
(defn ecs-secret [{:keys [aws-secret]}]
(software.amazon.awscdk.services.ecs.Secret/fromSecretsManager
(Secret/fromSecretNameV2 stack aws-secret aws-secret)))
;; The code under ./server, as a Fargate service.
(def fargate-service
(let [docker-image (-> (ApplicationLoadBalancedTaskImageOptions/builder)
(.image (ContainerImage/fromAsset "server"))
(.secrets (update-vals api-conf ecs-secret))
(.enableLogging true)
.build)]
(-> (ApplicationLoadBalancedFargateService$Builder/create stack "fargate-service")
(.cluster ecs-cluster)
(.taskImageOptions docker-image)
(.desiredCount 1)
(.cpu 256)
(.memoryLimitMiB 1024)
(.publicLoadBalancer false)
.build)))
;;; The API Gateway
(def api-endpoint
(-> (HttpApi$Builder/create stack "HttpProxyPrivateAPI")
(.defaultAuthorizer (HttpIamAuthorizer.))
.build))
(def default-route
(-> (AddRoutesOptions/builder)
(.integration (HttpAlbIntegration. "DefaultIntegration" (.getListener fargate-service)))
(.path "/{proxy+}")
(.methods [(HttpMethod/ANY)])
.build))
;; Add the route to the API and grant access to any IAM users in this account
(let [route (first (.addRoutes api-endpoint default-route))]
(.grantInvoke route (AnyPrincipal.)))
;; Add the URL to the outputs
(-> (CfnOutput$Builder/create stack "apiGatewayURL")
(.value (.getUrl api-endpoint))
(.description "The API gateway URL.")
(.exportName "apiGatewayURL")
.build)
(defn synth [& args]
(println "Synthesized to:" (.getDirectory (.synth app)))
(.getDirectory (.synth app)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment