MakisDSL

Multi-Cloud Support

MakisDSL enables true multi-cloud infrastructure as code. Write your infrastructure once and deploy to AWS, Azure, or GCP using the same DSL syntax.

🔗 Overview

MakisDSL abstracts away cloud provider differences, allowing you to define infrastructure using a unified syntax. The same DSL code can generate:

Single DSL, Multiple Outputs

// One DSL definition works for all providers
val infrastructure = cloudApp(provider = AWS) { // Change provider here
  val storage = objectStorage("my-app-data")
    .withVersioning(true)

  val api = serverlessFunction("my-api")
    .withRuntime("nodejs18.x")
    .withHandler("index.handler")
    .withCode("exports.handler = async () => ({ statusCode: 200, body: 'Hello!' })")
    .build

  val database = noSqlTable("my-data")
    .withHashKey("id", "S")
    .build
}
{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Resources" : {
    "my-app-data" : {
      "Type" : "AWS::S3::Bucket",
      "Properties" : {
        "VersioningConfiguration" : {
          "Status" : "Enabled"
        }
      }
    },
    "my-api" : {
      "Type" : "AWS::Lambda::Function",
      "Properties" : {
        "Runtime" : "nodejs18.x",
        "Handler" : "index.handler",
        "Code" : {
          "ZipFile" : "exports.handler = async () => ({ statusCode: 200, body: 'Hello!' })"
        }
      }
    },
    "my-data" : {
      "Type" : "AWS::DynamoDB::Table",
      "Properties" : {
        "KeySchema" : [ {
          "AttributeName" : "id",
          "KeyType" : "HASH"
        } ],
        "AttributeDefinitions" : [ {
          "AttributeName" : "id",
          "AttributeType" : "S"
        } ],
        "BillingMode" : "PAY_PER_REQUEST"
      }
    }
  }
}
{
  "$schema" : "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion" : "1.0.0.0",
  "resources" : [ {
    "type" : "Microsoft.Storage/storageAccounts",
    "apiVersion" : "2023-01-01",
    "name" : "my-app-data",
    "location" : "[resourceGroup().location]",
    "properties" : {
      "sku" : {
        "name" : "Standard_LRS"
      },
      "kind" : "StorageV2",
      "accessTier" : "Hot",
      "isVersioningEnabled" : true
    }
  }, {
    "type" : "Microsoft.Web/sites",
    "apiVersion" : "2023-01-01",
    "name" : "my-api",
    "location" : "[resourceGroup().location]",
    "properties" : {
      "kind" : "functionapp",
      "siteConfig" : {
        "appSettings" : [ {
          "name" : "FUNCTIONS_WORKER_RUNTIME",
          "value" : "node"
        }, {
          "name" : "WEBSITE_NODE_DEFAULT_VERSION",
          "value" : "~18"
        } ]
      }
    }
  }, {
    "type" : "Microsoft.DocumentDB/databaseAccounts",
    "apiVersion" : "2023-04-15",
    "name" : "my-data",
    "location" : "[resourceGroup().location]",
    "properties" : {
      "databaseAccountOfferType" : "Standard",
      "consistencyPolicy" : {
        "defaultConsistencyLevel" : "Session"
      },
      "locations" : [ {
        "locationName" : "[resourceGroup().location]",
        "failoverPriority" : 0
      } ]
    }
  } ]
}
{
  "resources" : [ {
    "name" : "my-app-data",
    "type" : "storage.v1.bucket",
    "properties" : {
      "location" : "US",
      "storageClass" : "STANDARD",
      "versioning" : {
        "enabled" : true
      }
    }
  }, {
    "name" : "my-api",
    "type" : "cloudfunctions.v1.function",
    "properties" : {
      "location" : "us-central1",
      "runtime" : "nodejs18",
      "entryPoint" : "index",
      "sourceArchiveUrl" : "gs://my-source-bucket/function.zip"
    }
  }, {
    "name" : "my-data",
    "type" : "firestore.v1.database",
    "properties" : {
      "location" : "us-central1",
      "type" : "FIRESTORE_NATIVE"
    }
  } ]
}

🔗 AWS CloudFormation

MakisDSL generates AWS CloudFormation templates that follow AWS best practices and resource naming conventions.

AWS Generator Usage

import cloud.providers.aws.CloudFormationGenerator
import io.circe.parser.*

val awsApp = cloudApp(provider = AWS) {
  // Define your infrastructure
}

val cfTemplate = CloudFormationGenerator.generate(awsApp)
val jsonString = cfTemplate.spaces2

// Save to file or deploy with AWS CLI
println(jsonString)

AWS Resource Types

MakisDSL Resource AWS CloudFormation Type AWS Service
objectStorage AWS::S3::Bucket Amazon S3
serverlessFunction AWS::Lambda::Function AWS Lambda
noSqlTable AWS::DynamoDB::Table Amazon DynamoDB
virtualNetwork AWS::EC2::VPC Amazon VPC
securityGroup AWS::EC2::SecurityGroup Amazon EC2
applicationLoadBalancer AWS::ElasticLoadBalancingV2::LoadBalancer Elastic Load Balancing

AWS-Specific Features

🔗 Azure ARM

MakisDSL generates Azure Resource Manager (ARM) templates compatible with Azure deployment tools.

Azure Generator Usage

import cloud.providers.azure.ARMGenerator
import io.circe.parser.*

val azureApp = cloudApp(provider = Azure) {
  // Same infrastructure definition as AWS
}

val armTemplate = ARMGenerator.generate(azureApp)
val jsonString = armTemplate.spaces2

// Deploy with Azure CLI: az deployment group create --template-file template.json
println(jsonString)

Azure Resource Types

MakisDSL Resource Azure ARM Type Azure Service
objectStorage Microsoft.Storage/storageAccounts Azure Storage
serverlessFunction Microsoft.Web/sites Azure Functions
noSqlTable Microsoft.DocumentDB/databaseAccounts Azure Cosmos DB
virtualNetwork Microsoft.Network/virtualNetworks Azure Virtual Network
securityGroup Microsoft.Network/networkSecurityGroups Azure Network Security Groups
applicationLoadBalancer Microsoft.Network/loadBalancers Azure Load Balancer

Azure-Specific Features

🔗 GCP Deployment Manager

MakisDSL generates Google Cloud Deployment Manager templates for GCP resource provisioning.

GCP Generator Usage

import cloud.providers.gcp.DeploymentManagerGenerator
import io.circe.parser.*

val gcpApp = cloudApp(provider = GCP) {
  // Same infrastructure definition as AWS/Azure
}

val gcpTemplate = DeploymentManagerGenerator.generate(gcpApp)
val jsonString = gcpTemplate.spaces2

// Deploy with gcloud: gcloud deployment-manager deployments create my-app --config template.yaml
println(jsonString)

GCP Resource Types

MakisDSL Resource GCP Type GCP Service
objectStorage storage.v1.bucket Cloud Storage
serverlessFunction cloudfunctions.v1.function Cloud Functions
noSqlTable firestore.v1.database Cloud Firestore
virtualNetwork compute.v1.network VPC Network
securityGroup compute.v1.firewall VPC Firewall
applicationLoadBalancer compute.v1.forwardingRule Cloud Load Balancing

GCP-Specific Features

🔗 Resource Mapping

MakisDSL automatically maps abstract resource concepts to provider-specific implementations:

Storage Mapping Example

// Abstract storage concept
val storage = objectStorage("my-data")
  .withVersioning(true)
// Maps to S3 Bucket with versioning
{
  "Type" : "AWS::S3::Bucket",
  "Properties" : {
    "VersioningConfiguration" : {
      "Status" : "Enabled"
    }
  }
}
// Maps to Storage Account with versioning
{
  "type" : "Microsoft.Storage/storageAccounts",
  "properties" : {
    "sku" : { "name" : "Standard_LRS" },
    "kind" : "StorageV2",
    "isVersioningEnabled" : true
  }
}
// Maps to Cloud Storage bucket with versioning
{
  "type" : "storage.v1.bucket",
  "properties" : {
    "versioning" : {
      "enabled" : true
    }
  }
}

🔗 Provider Differences

While MakisDSL abstracts most differences, some provider-specific behaviors are important to understand:

Compute Runtime Mapping

MakisDSL Runtime AWS Lambda Azure Functions GCP Functions
nodejs18.x nodejs18.x node (~18) nodejs18
python3.9 python3.9 python (~3.9) python39
java11 java11 java (~11) java17

Database Differences

Networking Differences

🔗 Best Practices

Provider-Agnostic Design

Testing Across Providers

class MultiCloudTest extends munit.FunSuite {
  def testProvider(provider: CloudProvider) = {
    val app = cloudApp(provider = provider) {
      val storage = objectStorage("test-bucket")
        .withVersioning(true)
      
      val function = serverlessFunction("test-function")
        .withRuntime("nodejs18.x")
        .withHandler("index.handler")
        .build
    }
    
    // Test that generation works for each provider
    val template = provider match {
      case AWS => CloudFormationGenerator.generate(app)
      case Azure => ARMGenerator.generate(app)
      case GCP => DeploymentManagerGenerator.generate(app)
    }
    
    assert(template != null)
  }
  
  test("AWS generation") { testProvider(AWS) }
  test("Azure generation") { testProvider(Azure) }
  test("GCP generation") { testProvider(GCP) }
}

Environment-Specific Configurations

// Use environment variables for provider-specific settings
val provider = sys.env.getOrElse("CLOUD_PROVIDER", "AWS") match {
  case "AWS" => AWS
  case "Azure" => Azure
  case "GCP" => GCP
}

val app = cloudApp(provider = provider) {
  val bucketName = env("BUCKET_NAME", "default-bucket")
  val storage = objectStorage(bucketName)
    .withVersioning(true)
}