CloudConnector -- Quick Start v1.0.0

From nothing to a working AWS-to-Google Cloud pipe, verified end to end, and back to nothing.

This walkthrough builds CloudConnector's evaluation shape from one Terraform root: a new AWS VPC and a new Google Cloud network, a relay fleet in each, and the controller that joins them. You then send test traffic through the pipe, confirm that the source address survives, and remove everything. Nothing you already run is touched. Most of the time is spent waiting for instances to boot.

Evaluation shape. The root used here creates its own networks and generates every credential in Terraform state. For production -- a pipe between networks you already own, with credentials you supply -- deploy the controller and the pipe roots separately, as described in the User Guide.

What you will build

WhereResources
AWS, the pipe's end (aws_region, default us-east-1)A VPC (10.60.0.0/16) with one public subnet in zone <region>a and an internet gateway; the relay fleet -- a launch template, one Auto Scaling group per zone, a Gateway Load Balancer with a GENEVE target group, a VPC endpoint service and one GWLB endpoint per zone, and a security group; an IAM role and instance profile for the relays.
AWS, the controllerA separate VPC (10.240.0.0/16) with two public subnets; an Auto Scaling group of one instance; an Elastic IP; a DynamoDB table; an IAM role and a security group.
Google Cloud (gcp_region, default us-central1)A network and a dual-stack subnetwork (10.70.0.0/16) with Cloud Router and Cloud NAT; the relay fleet -- an instance template, a regional managed instance group in zone <region>-a, a health check, an internal passthrough load balancer, firewall rules; and a route that sends the AWS range to the load balancer for VMs carrying the client tag.
The controller's storeThe pipe configuration: side aws advertises 10.60.0.0/16, side gcp advertises 10.70.0.0/16.

Before you begin

You needDetails
An AWS accountCredentials that can create VPC networking, launch templates and Auto Scaling groups, a Gateway Load Balancer and target group, a VPC endpoint service and endpoints, IAM roles and instance profiles, an Elastic IP and a DynamoDB table.
A Google Cloud projectThe Compute Engine API enabled, and credentials that can create networks, subnetworks, Cloud Router and Cloud NAT, firewall rules, instance templates, regional managed instance groups, health checks, internal load balancers and routes. The relays run as the project's default Compute Engine service account (<project-number>-compute@developer.gserviceaccount.com), which must exist.
A Cloud Spectra licence keyBoth fleets run under it. This walkthrough runs one relay per end: 2 vCPUs on AWS and 4 on Google Cloud, 6 raw vCPUs in all, inside the Developer lane's 16-vCPU account limit. Its 2-vCPU AWS relay is below the Enterprise lane's 4-vCPU minimum, so use a Developer-lane licence for it.
Terraform 1.5 or laterterraform version to check. terraform init installs the cloudspectra/gateway, hashicorp/aws, hashicorp/google and hashicorp/random providers.
The AWS CLI and the Google Cloud CLIFor the routing and verification steps.
The CloudConnector configurationSee Step 1.

Step 1 -- Get the configuration

CloudConnector is deployed from the cloudconnector example root, which calls the cloudspectra-relay-deploy composition by relative path (../../terraform/cloudspectra-relay-deploy). Keep the directory layout exactly as delivered, or the module path breaks:

<workdir>/
  examples/cloudconnector/          main.tf  variables.tf  outputs.tf  plan-only/
  terraform/cloudspectra-relay-deploy/
  terraform/cloudspectra-relay-controller/
  terraform/cloudspectra-relay-pipe/
  terraform/cloudspectra-relay/modules/

To obtain the configuration for your release, contact support@cloudspectra.ai.

Step 2 -- Provide credentials

The deployment needs three credentials: AWS, Google Cloud and your licence. Authenticate the two clouds the standard way, so that every module in the configuration picks them up:

# AWS: a named profile, environment variables or SSO -- whatever the AWS CLI uses
export AWS_PROFILE=my-profile
aws sts get-caller-identity

# Google Cloud: application-default credentials, and the project to build in
gcloud auth application-default login
export GOOGLE_PROJECT=my-project-id

The root also reads ~/.secrets/.aws.credentials (an AWS shared-credentials file) and ~/.secrets/.gcp.credentials (a service-account key, whose own project it then uses) when they exist, for its own lookups. The modules it calls authenticate through the standard chains, so configure those in any case.

Place the licence key where the deployment reads it:

mkdir -p ~/.secrets && chmod 700 ~/.secrets
printf '%s\n' '<your licence key>' > ~/.secrets/.cloudspectra.relay.credentials
chmod 600 ~/.secrets/.cloudspectra.relay.credentials
The licence is written to Terraform state. The relays receive it in their instance user data, and Terraform records user data. Keep the state file off shared storage and encrypted at rest.

Step 3 -- Choose the inputs

Every input of the example has a default. Create examples/cloudconnector/terraform.tfvars:

name        = "cloudconnector"   # prefixes every resource; also the fleet id
release     = "1.0.0"            # the release whose relay images are deployed
aws_region  = "us-east-1"
gcp_region  = "us-central1"
target_gbps = 3                  # capacity to size each end for

target_gbps decides the number of relays per end: one relay per 3 Gbps, rounded up, never fewer than one. The value 3 used here gives one relay per end -- the cheapest pipe, with no redundancy: the plan says so in a note, and losing either relay stops the pipe until it is replaced. The example's own default of 10 builds four relays per end, which is 24 raw vCPUs and more than the Developer lane allows.

So that you can query the controller in Step 7, add two outputs to examples/cloudconnector/outputs.tf:

output "fleet_id" {
  value = module.cloudconnector.fleet_id
}

output "read_token" {
  value     = module.cloudconnector.credentials.read_token
  sensitive = true
}

Step 4 -- Initialize and plan

cd examples/cloudconnector
terraform init
terraform plan

Read the plan's warnings before you apply. Terraform reports them but still lets the apply go ahead:

WarningWhat it means
About this pipeNotes from the product's own sizing rules: that this mode runs a controller in your account and, at one relay per end, that the pipe has no redundancy.
No licence. Looked in: ...No licence was found in the files from Step 2. Relays without a licence come up and forward nothing.
No AMI for release ... / No GCP image for release ...The image manifest for that release and architecture is empty. (If the manifest does not exist at all, the plan fails while reading it.)
tunnel_members (2) is below what the AWS autoscaler may reachThe AWS Auto Scaling groups may grow to four relays per zone, beyond the two-port WireGuard band opened by default. Harmless while each end runs one or two relays; see The bond ceiling before you grow.

Step 5 -- Apply

terraform apply

When the apply finishes it prints:

OutputWhat it is
controller_urlThe controller both ends register with, http://<elastic-ip>:8080.
aws_endpoint_serviceThe Gateway Load Balancer endpoint service on the AWS end. Its endpoints are the targets of your AWS routes.
gcp_ilb_ipThe internal load balancer address on the Google Cloud end.
wg_port_rangeThe UDP band both ends listen on, 28302-28303 by default. Anything between the two sides must admit it.
relay_target_per_end, relay_count_aws, relay_count_gcp, relay_countRelays per end that target_gbps asked for, what each end actually runs, and the total that is metered.

The relays need a few minutes after the apply to boot, fetch their datapath, register with the controller and bond with each other.

Step 6 -- Route traffic into the pipe

Nothing is steered into the pipe until you say so. The steps below create one test host on each side; for your own workloads, apply the same routes and firewall rules to their subnets and VMs.

On AWS: a workload subnet and a route to the GWLB endpoint

Put workloads in their own subnet with their own route table. Do not add the steering route to the relays' route table: the relays must not route the far side's range back into the load balancer.

REGION=us-east-1
VPC=$(aws ec2 describe-vpcs --region $REGION \
  --filters Name=tag:Name,Values=cloudconnector-pipe --query 'Vpcs[0].VpcId' --output text)
IGW=$(aws ec2 describe-internet-gateways --region $REGION \
  --filters Name=attachment.vpc-id,Values=$VPC --query 'InternetGateways[0].InternetGatewayId' --output text)
GWLBE=$(aws ec2 describe-vpc-endpoints --region $REGION \
  --filters Name=service-name,Values=$(terraform output -raw aws_endpoint_service) \
  --query 'VpcEndpoints[0].VpcEndpointId' --output text)

# A subnet for workloads, in the relays' zone, with its own route table
SUBNET=$(aws ec2 create-subnet --region $REGION --vpc-id $VPC \
  --cidr-block 10.60.128.0/24 --availability-zone ${REGION}a --query Subnet.SubnetId --output text)
RTB=$(aws ec2 create-route-table --region $REGION --vpc-id $VPC --query RouteTable.RouteTableId --output text)
aws ec2 associate-route-table --region $REGION --route-table-id $RTB --subnet-id $SUBNET
aws ec2 create-route --region $REGION --route-table-id $RTB \
  --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW

# The steering route: the Google Cloud range goes to this zone's GWLB endpoint
aws ec2 create-route --region $REGION --route-table-id $RTB \
  --destination-cidr-block 10.70.0.0/16 --vpc-endpoint-id $GWLBE

A route can only target the endpoint, never the load balancer or the endpoint service. With relays in several zones, route each workload subnet to the endpoint in its own zone.

Now a test host. Its security group must admit the Google Cloud range, because packets from there arrive with their original source:

SG=$(aws ec2 create-security-group --region $REGION --vpc-id $VPC \
  --group-name cc-test --description "CloudConnector test host" --query GroupId --output text)
aws ec2 authorize-security-group-ingress --region $REGION --group-id $SG \
  --protocol tcp --port 22 --cidr <your-public-ip>/32
aws ec2 authorize-security-group-ingress --region $REGION --group-id $SG \
  --protocol -1 --cidr 10.70.0.0/16

aws ec2 run-instances --region $REGION --instance-type t3.micro \
  --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
  --subnet-id $SUBNET --security-group-ids $SG --associate-public-ip-address \
  --key-name <your-key-pair> \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=cc-test-aws}]'

On Google Cloud: the client tag and two firewall rules

The deployment already created the route that sends 10.60.0.0/16 to the internal load balancer. It applies only to VMs with the network tag <name>-client -- here cloudconnector-client -- and never to the relays themselves (tag cloudconnector-gcp-relay). Add firewall rules so that the AWS range can reach your VMs, and your VMs can reach the relays:

gcloud compute firewall-rules create cc-test-from-aws --network cloudconnector-pipe \
  --source-ranges 10.60.0.0/16 --allow all --target-tags cloudconnector-client
gcloud compute firewall-rules create cc-test-to-relays --network cloudconnector-pipe \
  --source-ranges 10.70.0.0/16 --allow all --target-tags cloudconnector-gcp-relay
gcloud compute firewall-rules create cc-test-ssh --network cloudconnector-pipe \
  --source-ranges <your-public-ip>/32 --allow tcp:22 --target-tags cloudconnector-client

gcloud compute instances create cc-test-gcp --zone us-central1-a --machine-type e2-small \
  --subnet cloudconnector-pipe --tags cloudconnector-client \
  --image-family debian-12 --image-project debian-cloud

To put an existing VM on the pipe, add the tag: gcloud compute instances add-tags <vm> --tags cloudconnector-client --zone <zone>.

Step 7 -- Verify end to end

1Traffic crosses

Note the private address of each test host (aws ec2 describe-instances, gcloud compute instances list). Log in to the AWS host and ping the Google Cloud host's private address:

ssh ec2-user@<aws-test-public-ip>
ping -c 5 <gcp-test-private-ip>

2The source survives

On the Google Cloud host, capture the arriving packets while the ping runs. The source must be the AWS host's private address -- not a relay's, not a NAT address:

gcloud compute ssh cc-test-gcp --zone us-central1-a
sudo tcpdump -ni any icmp and src host <aws-test-private-ip>

3Bulk TCP crosses

Ping proves reachability but not the tunnel MTU path. A TCP transfer does: install iperf3 on both hosts, run iperf3 -s on the Google Cloud host and iperf3 -c <gcp-test-private-ip> -t 10 on the AWS host. Traffic between the clouds is billed as internet egress, so keep test transfers short.

4The controller agrees

From a machine the controller's security group admits, read the pipe's status:

curl -s -H "Authorization: Bearer $(terraform output -raw read_token)" \
  "$(terraform output -raw controller_url)/fleet/v1/pipe/status?fleet=$(terraform output -raw fleet_id)"

A healthy pipe reports "health": "up", at least one live relay on each side, and no unplaced, stranded or problems entries. A relay that has carried nothing for 270 seconds counts as idle, so a pipe with no traffic reads degraded. The fields are described under Monitoring.

Step 8 -- Tear down

Remove what you created by hand first: Terraform cannot delete a VPC or network that still holds resources it does not manage.

# AWS
aws ec2 terminate-instances --region $REGION --instance-ids <cc-test-aws-instance-id>
aws ec2 wait instance-terminated --region $REGION --instance-ids <cc-test-aws-instance-id>
aws ec2 delete-security-group --region $REGION --group-id $SG
aws ec2 delete-subnet --region $REGION --subnet-id $SUBNET
aws ec2 delete-route-table --region $REGION --route-table-id $RTB

# Google Cloud
gcloud compute instances delete cc-test-gcp --zone us-central1-a --quiet
gcloud compute firewall-rules delete cc-test-from-aws cc-test-to-relays cc-test-ssh --quiet

# Everything Terraform built
terraform destroy
If the destroy stops at the controller. The controller protects its Elastic IP and its DynamoDB table against deletion by default (its protect_durable_state input). If terraform destroy refuses to remove them, take the address out of Terraform's management and lift the table's protection, destroy again, then release the address yourself:
terraform state rm 'module.cloudconnector.module.controller.aws_eip.controller[0]'
aws dynamodb update-table --region $REGION --table-name cloudconnector-controller-fleet \
  --no-deletion-protection-enabled
terraform destroy
aws ec2 release-address --region $REGION --allocation-id <controller-eip-allocation-id>

Destroying the deployment does not cancel your Cloud Spectra subscription. Relays are metered for the clock hours they hold, so a relay's last hour bills in full; a relay that stops checking in without a goodbye stops accruing time within 30 minutes.

Next steps