Skip to main content
Service equivalents define how Tensor9 maps cloud-specific managed services in your origin stack to their functional equivalents in the target appliance’s environment. During compilation, Tensor9 maps resources from your origin stack to corresponding services in AWS, Google Cloud, Azure, Digital Ocean, or private environments.

Overview

Cloud providers offer similar services under different names and APIs:
  • AWS RDS PostgreSQL vs. Google Cloud SQL vs. Azure Database for PostgreSQL
  • AWS S3 vs. Google Cloud Storage vs. Azure Blob Storage
  • AWS ElastiCache vs. Google Memorystore vs. Azure Cache for Redis
Tensor9 handles the mapping between these provider-specific implementations. You define your infrastructure using one cloud provider’s resources, and Tensor9 maps them to the target environment.

How service equivalents work

When you create a release for a specific appliance, Tensor9 compiles your origin stack through several steps:
1

Analyze origin stack

Tensor9 identifies all cloud-specific resources in your origin stack.
2

Map to functional equivalents

Each resource is mapped to its functional equivalent based on the target form factor.
3

Map resource definitions

The resource definition is mapped to use the target cloud’s provider and API.
4

Preserve configuration semantics

Configuration semantics (storage size, instance type, performance characteristics) are preserved in the mapping.
5

Generate deployment stack

A deployment stack ready to deploy in the target environment is generated.
This process is driven by Tensor9’s service equivalents registry, which maintains mappings between functionally equivalent services across cloud providers and deployment environments.

Service equivalents registry

Tensor9 groups services into equivalent sets based on their function. When compiling for a specific form factor, Tensor9 automatically selects the appropriate equivalent from the target environment.
Service CategoryAWSGoogle CloudAzureDigitalOceanOn-Prem
ContainersEKS, ECSGKEAKSDOKKubernetes
FunctionsLambdaCloud FunctionsAzure FunctionsFunctionsKnative (unmanaged)
NetworkingVPCVPCVNet--
Load balancingLoad BalancerLoad BalancerLoad BalancerLoad BalancerCloudflare (optional)
DNSRoute 53Cloud DNSAzure DNSDigitalOcean DNSCloudflare (optional)
Identity and access managementIAMIAMIAM--
Object storageS3Cloud Storage (GCS)Azure Blob StorageSpacesBackblaze B2, MinIO (unmanaged)
Databases (PostgreSQL)RDS Aurora PostgreSQL, RDS PostgreSQLCloud SQL PostgreSQLAzure Database for PostgreSQLManaged PostgreSQLNeon, CloudNative PostgreSQL (unmanaged)
Databases (MySQL)RDS Aurora MySQL, RDS MySQLCloud SQL MySQLAzure Database for MySQLManaged MySQLPlanetScale, MySQL (unmanaged)
Databases (MongoDB)DocumentDBAtlas MongoDBCosmos DB (MongoDB API)Managed MongoDBMongoDB Atlas, MongoDB (unmanaged)
CachingElastiCacheMemorystoreAzure Cache for RedisManaged RedisRedis Enterprise Cloud, Redis (unmanaged)
Message streamingMSK (Managed Streaming for Kafka)Confluent Cloud, Kafka (unmanaged)Event Hubs (Kafka compatible)Confluent Cloud, Kafka (unmanaged)Confluent Cloud, Kafka (unmanaged)
SearchOpenSearch ServiceOpenSearch (unmanaged)OpenSearch (unmanaged)OpenSearch (unmanaged)OpenSearch (unmanaged)
WorkflowMWAA (Managed Airflow)Cloud ComposerAzure Data FactoryAstronomer, Airflow (unmanaged)Astronomer, Airflow (unmanaged)
AnalyticsAmazon AthenaBigQueryAzure Synapse AnalyticsPresto (unmanaged)Presto (unmanaged)
Third-party managed equivalents (Backblaze B2, Neon, PlanetScale, MongoDB Atlas, Redis Enterprise Cloud, Confluent Cloud, Astronomer) require your customers to bring their own credentials and accounts with these services.

Mapping examples

Here are concrete examples of how Tensor9 maps services during compilation.

Example 1: AWS to Google Cloud

When compiling an AWS-based origin stack for a Google Cloud appliance: Origin stack (AWS):
resource "aws_db_instance" "postgres" {
  engine         = "postgres"
  instance_class = "db.t3.micro"
  allocated_storage = 20
}

resource "aws_s3_bucket" "data" {
  bucket = "my-app-data-${var.instance_id}"
}

resource "aws_elasticache_cluster" "redis" {
  engine               = "redis"
  node_type            = "cache.t3.micro"
  num_cache_nodes      = 1
}
Compiled deployment stack (Google Cloud):
The deployment stack below is for illustration purposes only. An actual deployment stack is a lot more complicated, and does not simply contain resources as is shown below.
resource "google_sql_database_instance" "postgres" {
  database_version = "POSTGRES_15"
  tier            = "db-f1-micro"

  settings {
    disk_size = 20
  }
}

resource "google_storage_bucket" "data" {
  name     = "my-app-data-${var.instance_id}"
  location = "US"
}

resource "google_redis_instance" "redis" {
  tier           = "BASIC"
  memory_size_gb = 1
}
Tensor9 automatically:
  • Replaced aws_db_instance with google_sql_database_instance
  • Replaced aws_s3_bucket with google_storage_bucket
  • Replaced aws_elasticache_cluster with google_redis_instance
  • Preserved configuration (storage size, memory, instance tiers)
  • Maintained the instance_id variable for resource uniqueness
  • Injected replacement service endpoints into application containers (no code changes required)

Example 2: AWS to private Kubernetes

When compiling an AWS-based origin stack for a private Kubernetes appliance: Origin stack (AWS):
resource "aws_eks_cluster" "app" {
  name = "my-app-cluster-${var.instance_id}"

  vpc_config {
    subnet_ids = var.subnet_ids
  }
}

resource "aws_db_instance" "postgres" {
  engine         = "postgres"
  instance_class = "db.t3.medium"
  allocated_storage = 100
}

resource "aws_s3_bucket" "artifacts" {
  bucket = "my-app-artifacts-${var.instance_id}"
}
Compiled deployment stack (private Kubernetes):
The deployment stack below is for illustration purposes only. An actual deployment stack is a lot more complicated, and does not simply contain resources as is shown below.
resource "kubernetes_namespace" "app" {
  metadata {
    name = "my-app-${var.instance_id}"
  }
}

# CloudNative PostgreSQL operator
resource "kubectl_manifest" "postgres" {
  yaml_body = <<-YAML
    apiVersion: postgresql.cnpg.io/v1
    kind: Cluster
    metadata:
      name: postgres
      namespace: my-app-${var.instance_id}
    spec:
      instances: 3
      storage:
        size: 100Gi
      postgresql:
        parameters:
          max_connections: "200"
  YAML
}

# MinIO for object storage
resource "helm_release" "minio" {
  name       = "minio"
  repository = "https://charts.min.io/"
  chart      = "minio"
  namespace  = "my-app-${var.instance_id}"

  set {
    name  = "persistence.size"
    value = "100Gi"
  }
}
Tensor9 automatically:
  • Replaced the EKS cluster with a Kubernetes namespace
  • Replaced AWS RDS with CloudNative PostgreSQL (CNPG) operator
  • Replaced S3 with MinIO for private object storage
  • Adapted configurations to Kubernetes-native patterns

Example 3: AWS to Azure

When compiling an AWS-based origin stack for an Azure appliance: Origin stack (AWS):
resource "aws_lambda_function" "api" {
  function_name = "api-handler-${var.instance_id}"
  runtime       = "python3.11"
  handler       = "index.handler"

  environment {
    variables = {
      DATABASE_URL = aws_db_instance.postgres.endpoint
    }
  }
}

resource "aws_db_instance" "postgres" {
  engine         = "postgres"
  instance_class = "db.t3.small"
}

resource "aws_s3_bucket" "uploads" {
  bucket = "user-uploads-${var.instance_id}"
}
Compiled deployment stack (Azure):
The deployment stack below is for illustration purposes only. An actual deployment stack is a lot more complicated, and does not simply contain resources as is shown below.
resource "azurerm_linux_function_app" "api" {
  name                = "api-handler-${var.instance_id}"
  resource_group_name = var.resource_group_name
  location            = var.location

  site_config {
    application_stack {
      python_version = "3.11"
    }
  }

  app_settings = {
    DATABASE_URL = azurerm_postgresql_server.postgres.fqdn
  }
}

resource "azurerm_postgresql_server" "postgres" {
  name                = "postgres-${var.instance_id}"
  resource_group_name = var.resource_group_name
  location            = var.location

  sku_name = "B_Gen5_1"
}

resource "azurerm_storage_account" "uploads" {
  name                = "uploads${var.instance_id}"
  resource_group_name = var.resource_group_name
  location            = var.location
}

resource "azurerm_storage_container" "uploads" {
  name                  = "user-uploads"
  storage_account_name  = azurerm_storage_account.uploads.name
}
Tensor9 automatically:
  • Replaced Lambda with Azure Functions
  • Replaced RDS PostgreSQL with Azure Database for PostgreSQL
  • Replaced S3 with Azure Blob Storage (storage account + container)
  • Preserved environment variables and references between resources
  • Injected replacement service endpoints into application containers (no code changes required)

Wire compatibility and endpoint injection

Service equivalents in Tensor9 are wire compatible, meaning your application code doesn’t need to change when deploying across different cloud providers. Tensor9 achieves this through endpoint injection:

No code changes required

When services are functionally equivalent, they speak the same protocol:
  • PostgreSQL databases all use the PostgreSQL wire protocol, whether running on AWS RDS, Google Cloud SQL, or Azure Database for PostgreSQL
  • Redis caches all use the Redis protocol, whether running on AWS ElastiCache, Google Memorystore, or Azure Cache for Redis
  • Object storage services all support S3-compatible APIs, whether AWS S3, Google Cloud Storage, Azure Blob Storage, or MinIO
  • Functions all support the same invocation patterns, whether AWS Lambda, Google Cloud Functions, or Azure Functions
Your application code connects to these services using standard client libraries (e.g., pg for PostgreSQL, redis for Redis, boto3 for S3-compatible storage). These libraries work the same regardless of which cloud provider hosts the underlying service.
Emulation for certain services: For services like object storage and functions, Tensor9 provides an emulation layer to compensate for a lack of wire compatibility. For example, Azure Blob Storage doesn’t natively support the S3 API, so Tensor9 deploys an S3-compatible emulation layer that translates S3 API calls to the native cloud storage API. Similarly, function invocation patterns are normalized across Lambda, Cloud Functions, and Azure Functions. This emulation is transparent to your application code.

Endpoint injection

What changes between cloud providers is the service endpoint - the hostname and port your application uses to connect to the service. Tensor9 automatically injects the correct endpoints into your application containers: Origin stack definition:
resource "aws_db_instance" "postgres" {
  engine         = "postgres"
  instance_class = "db.t3.micro"
}

resource "aws_lambda_function" "api" {
  environment {
    variables = {
      DATABASE_HOST = aws_db_instance.postgres.endpoint
    }
  }
}
Compiled for AWS (unchanged):
resource "aws_db_instance" "postgres" {
  engine         = "postgres"
  instance_class = "db.t3.micro"
}

# DATABASE_HOST = postgres.abc123.us-east-1.rds.amazonaws.com:5432
Compiled for Google Cloud:
resource "google_sql_database_instance" "postgres" {
  database_version = "POSTGRES_15"
  tier            = "db-f1-micro"
}

# DATABASE_HOST = 10.1.2.3:5432 (Cloud SQL private IP)
Compiled for Azure:
resource "azurerm_postgresql_server" "postgres" {
  sku_name = "B_Gen5_1"
}

# DATABASE_HOST = postgres-abc123.postgres.database.azure.com:5432
Tensor9 rewrites environment variables, configuration files, and Kubernetes ConfigMaps to inject the correct endpoints for the target cloud provider. Your application code simply reads DATABASE_HOST and connects - it doesn’t need to know whether it’s running on AWS, Google Cloud, or Azure.

What your code sees

From your application’s perspective, connecting to a PostgreSQL database looks identical across all clouds:
# This code works the same on AWS, Google Cloud, Azure, or private Kubernetes
import psycopg2
import os

conn = psycopg2.connect(
    host=os.environ['DATABASE_HOST'],
    database=os.environ['DATABASE_NAME'],
    user=os.environ['DATABASE_USER'],
    password=os.environ['DATABASE_PASSWORD']
)
Tensor9 ensures the environment variables contain the correct values for each deployment environment. Your code remains unchanged.

Configuration mapping

When mapping services, Tensor9 preserves the semantic meaning of your configuration:
Configuration TypeHow Tensor9 Maps It
Storage and capacityDisk sizes are mapped to equivalent tiers in the target cloud. Memory allocations are mapped to appropriate instance types. Performance characteristics (IOPS, throughput) are matched where possible.
NetworkingVPC configurations are mapped to equivalent network isolation constructs. Load balancer types (internal vs. external) are preserved. Security group rules are mapped to cloud-specific firewall rules.
High availability and resilienceMulti-AZ deployments are mapped to equivalent availability zone configurations. Read replicas and standby instances are configured appropriately. Backup and retention policies are maintained.
Access controlIAM roles and service accounts are created with equivalent permissions. Encryption settings (at-rest, in-transit) are preserved. Authentication mechanisms are adapted to target cloud patterns.

Managed vs. unmanaged services

The service equivalents registry includes both managed and unmanaged service options. The choice depends on the target form factor:

Managed services

When available in the target environment, Tensor9 uses cloud provider-managed versions of services (databases, caches, message queues). Managed services include built-in backups, monitoring, and updates handled by the cloud provider.

Third-party services

Third-party managed services (e.g., MongoDB Atlas, Confluent Cloud) are available across all deployment environments, including private. These services are hosted externally and accessed over the network.

Unmanaged services

For environments where cloud provider-managed services aren’t available, Tensor9 can use unmanaged equivalents:
  • Kubernetes operators (e.g., CloudNative PostgreSQL)
  • Self-hosted open-source software (e.g., MinIO, Redis)
  • Containerized versions running on the appliance
Unmanaged services require operational management (backups, updates, scaling) to be handled within the appliance.

Single origin stack for multiple environments

Service equivalents enable you to maintain a single origin stack that deploys to multiple target environments. Changes to your origin stack propagate to all supported form factors during compilation without requiring separate codebases per cloud provider.

Form factor and service availability

Not all services are available in all form factors. The form factor defines which managed services are available in the target environment. For example:
  • AWS: Full access to AWS managed services (RDS, S3, ElastiCache, etc.)
  • Google Cloud: Full access to Google Cloud managed services (Cloud SQL, GCS, Memorystore, etc.)
  • Private Kubernetes: Kubernetes-native services, operators, and third-party managed services (e.g., MongoDB Atlas)
When creating a form factor, you specify which services are available. During compilation, Tensor9 selects the appropriate equivalent from the available options.

Unsupported AWS services

While Tensor9 supports many AWS services, some popular AWS services are not currently supported for cross-cloud deployment:
AWS ServiceService CategoryRecommended Alternative
EC2Compute (VMs)ECS Fargate, EKS, or Lambda
DynamoDBNoSQL DatabaseDocumentDB (MongoDB-compatible)
EFSFile StorageEBS volumes or S3 for object storage
SQSMessage QueueMSK (Kafka) or application-level queuing
SNSPub/Sub MessagingMSK (Kafka) or application-level pub/sub
KinesisStream ProcessingMSK (Kafka)
EventBridgeEvent BusMSK (Kafka) or application-level events
Step FunctionsWorkflow OrchestrationMWAA (Managed Airflow)
API GatewayAPI ManagementALB/NLB with application routing
CognitoAuthenticationApplication-level authentication
AppSyncGraphQL APIApplication-level GraphQL server
RedshiftData WarehouseAmazon Athena or BigQuery
If your origin stack uses these services, you’ll need to refactor to use supported alternatives from the service equivalents registry.
If you need a service that isn’t listed here or in the service equivalents registry, please contact [email protected] to discuss your requirements.

Limitations and considerations

Some popular AWS services are not currently supported for cross-cloud deployment. See Unsupported AWS services for the full list and recommended alternatives.
Some cloud-specific features may not have direct equivalents across all providers. For example:
  • Azure-specific features like Cosmos DB multi-model APIs
  • Google Cloud-specific services like BigQuery ML
In these cases, you may need to:
  • Use a more generic service type that’s available across clouds
  • Include conditional logic in your origin stack
  • Define form factor-specific overrides
While Tensor9 maps configuration semantically, exact performance characteristics may vary between clouds due to differences in:
  • Hardware specifications
  • Network architecture
  • Storage backend implementations
Always test in a test appliance for your target form factor.
Service equivalents are wire compatible, so your application code doesn’t need to change when deploying across different cloud providers. Tensor9 handles endpoint injection and provides emulation layers where necessary (e.g., S3-compatible APIs for non-AWS object storage). Your application code uses standard client libraries that work the same across all clouds.
When deploying to Azure appliances, Tensor9 provides S3-compatible API emulation for Azure Blob Storage. The emulation supports common S3 operations but has limitations compared to native AWS S3:Supported S3 operations:
  • GetObject - Download objects
  • PutObject - Upload objects
  • DeleteObject - Delete objects
  • DeleteBucket - Delete buckets
  • DeleteObjectVersion - Delete object versions
  • ListObjects / ListObjectsV2 - List objects in a bucket
  • ListBuckets - List all buckets
  • ListObjectVersions - List object versions
  • GetPresignedUrl - Generate pre-signed URLs for temporary access
Unsupported operations: Advanced S3 features like lifecycle policies, replication, and bucket policies are not supported in the Azure emulation layer.
Google Cloud Storage and MinIO (used in private/on-prem deployments) provide native S3 compatibility and support a broader range of S3 operations beyond this list.
Some clouds have stricter naming requirements than others. Ensure your origin stack uses the ${var.instance_id} suffix for resource names to maintain uniqueness, and avoid special characters that may not be supported in all clouds.

Best practices

The service equivalents registry defines which services can be mapped across form factors. If your origin stack uses a service not in the registry, that service cannot be mapped and you will need to use an alternative that is supported.
Create test appliances for each form factor you intend to support. This validates that service equivalents work correctly for your specific use case.
Evaluate how your application performs across different form factors using the observability feature.
  • Deployments: Learn how compilation uses service equivalents
  • Form factors: Understand how form factors define available services
  • Origin stacks: Create portable infrastructure code
  • Appliances: Deploy to different cloud environments