Aller au contenu
Cloud medium

Terraform against feint: Scaleway, Outscale and the Exoscale refusal

2 min de lecture

Cette page existe aussi en français

Terraform and OpenTofu apply against feint with no cloud account, on two of the three emulated providers. You change one configuration line, the rest is the code you will deploy in production, and a full apply then destroy cycle passes without creating a single billed resource. This page gives the exact provider blocks for Scaleway and Outscale, explains why the Exoscale provider is refused, and covers the root volume case that blocked Scaleway configurations for a long time.

What you will learn

  • Redirect the Scaleway provider with a single setting, api_url.
  • Drive Outscale with its api block, whose full path is a trap.
  • Understand the Exoscale refusal, and what it protects.
  • Write a root_volume the provider finally accepts.

Terraform on Scaleway

The Scaleway provider is redirected with a single setting, api_url. The rest of the configuration stays what you would write for the real provider, which is the whole point: you test the code you will deploy.

terraform {
required_providers {
scaleway = {
source = "scaleway/scaleway"
version = "~> 2.79"
}
}
}
provider "scaleway" {
access_key = "SCWXXXXXXXXXXXXXXXXX"
secret_key = "11111111-1111-1111-1111-111111111111"
project_id = "11111111-1111-1111-1111-111111111111"
organization_id = "99999999-9999-4999-8999-999999999999"
region = "fr-par"
zone = "fr-par-1"
api_url = "http://127.0.0.1:4599"
}
resource "scaleway_vpc" "lab" {
name = "lab-feint"
}
resource "scaleway_instance_server" "web" {
name = "web-terraform"
type = "DEV1-S"
image = "ubuntu_jammy"
}

The apply needs no adaptation:

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
private_network_id = "fr-par/a0fa6f82-2aad-4f82-985a-2abeb65f05f7"
server_id = "fr-par-1/d2303f04-8ab3-4e71-a682-ad91c81286b7"

The returned identifiers carry the region or zone prefix, as Scaleway does. A terraform destroy works symmetrically, which lets you validate a full cycle in CI without creating a single billed resource.

The root volume, and the type that finally plans

Since 0.8.0, root_volume { volume_type = "sbs_volume" } is honoured, and that unblocks a real problem. Before it, the field had no usable value at all: the provider refuses b_ssd outright from 2.79 on, and sbs_volume planned for ever without ever applying.

resource "scaleway_instance_server" "web" {
name = "web-block"
type = "DEV1-S"
image = "ubuntu_jammy"
root_volume {
volume_type = "sbs_volume"
size_in_gb = 20
}
}

The disk is created in Block Storage, and the provider reads it back through the fallback it always used: instance.GetVolume first, then block.GetVolume on a typed 404. The apply completes and the output returns sbs_volume.

Terraform on Outscale

The official outscale/outscale provider drives the emulator end to end, with an api block instead of Scaleway's api_url argument. The difference in form matters: here the address carries the full API path, version segment included.

terraform {
required_providers {
outscale = {
source = "outscale/outscale"
version = "~> 1.7"
}
}
}
provider "outscale" {
access_key_id = "AAAAAAAAAAAAAAAAAAAA"
secret_key_id = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
api {
endpoint = "http://127.0.0.1:4599/api/v1"
region = "eu-west-2"
}
}
resource "outscale_net" "lab" {
ip_range = "10.70.0.0/16"
tags {
key = "name"
value = "lab-feint"
}
}
resource "outscale_subnet" "lab" {
net_id = outscale_net.lab.net_id
ip_range = "10.70.1.0/24"
}
resource "outscale_vm" "web" {
image_id = "ami-12345678"
vm_type = "tinav4.c2r2p1"
subnet_id = outscale_subnet.lab.subnet_id
}

The full cycle passes, including the empty second plan, which is the real test of a correct emulation: if the API returns its resources in a different shape than it accepted them, Terraform proposes a phantom change on every run.

outscale_net.lab: Creation complete after 0s [id=vpc-4e93695a]
outscale_subnet.lab: Creation complete after 1s [id=subnet-348a9987]
outscale_vm.web: Creation complete after 0s [id=i-ef8f6fe1]
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
No changes. Your infrastructure matches the configuration.

Why the Exoscale Terraform provider is refused

feint refuses calls from the Exoscale Terraform provider, and answers 400 with the explanation. This is not a gap, it protects your invoice.

{"message":"the Exoscale Terraform provider only honours EXOSCALE_API_ENDPOINT
for half of its calls: the rest reach the real cloud and create billable
resources. feint refuses rather than serve half an apply. Set
FEINT_EXOSCALE_ALLOW_TERRAFORM=1 if you understand that and want the half it can
serve. See docs/limits.md."}

The cause is measured: that provider honours the endpoint for only part of its calls, and the rest reach the real cloud with whatever credentials the environment holds. An apply would therefore be split between the emulator and a paying account, and a half success is indistinguishable from normal operation until the invoice arrives. Refusing early and loudly is the only honest behaviour.

The escape hatch exists, named rather than hidden, for anyone accepting that split knowingly:

Fenêtre de terminal
FEINT_EXOSCALE_ALLOW_TERRAFORM=1 feint serve

The exo CLI is unaffected: detection matches the Exoscale-Terraform-Provider user agent alone, so it refuses one client, not a provider.

Key points

  • Scaleway is redirected with api_url, Outscale with an api block whose path carries /api/v1.
  • The empty second plan is the real test: without it, emulation returns resources differently than it accepts them.
  • The Exoscale provider is refused with a 400: it would split the apply between the emulator and a billed account.
  • root_volume accepts sbs_volume since 0.8.0, which unblocks configurations that declare it.
  • A full cycle costs nothing: apply, empty plan, destroy, with no billed resource.

Next steps