<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>terraform Archives | Clever Cloud</title>
	<atom:link href="https://www.clever.cloud/blog/tag/terraform/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.clever.cloud/blog/tag/terraform/</link>
	<description>From Code to Product</description>
	<lastBuildDate>Tue, 03 Nov 2026 14:00:36 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://cdn.clever-cloud.com/uploads/2023/03/cropped-cropped-favicon-32x32.png</url>
	<title>terraform Archives | Clever Cloud</title>
	<link>https://www.clever.cloud/blog/tag/terraform/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>From the Console to Terraform: import your existing Clever Cloud infrastructure as code (IaC)</title>
		<link>https://www.clever.cloud/blog/engineering/2026/11/03/terraform-import-blocks-existing-infrastructure/</link>
		
		<dc:creator><![CDATA[Rémi Collignon-Ducret]]></dc:creator>
		<pubDate>Tue, 03 Nov 2026 13:59:05 +0000</pubDate>
				<category><![CDATA[Engineering]]></category>
		<category><![CDATA[terraform]]></category>
		<guid isPermaLink="false">https://www.clever.cloud/?p=25954</guid>

					<description><![CDATA[<p><img width="2400" height="1065" src="https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="2026.11.03 Clever Cloud Bannière Blog Import Terraform EN" decoding="async" fetchpriority="high" srcset="https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en.png 2400w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-300x133.png 300w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-1024x454.png 1024w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-768x341.png 768w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-1536x682.png 1536w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-2048x909.png 2048w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-1368x607.png 1368w" sizes="(max-width: 2400px) 100vw, 2400px" /></p><!-- wp:paragraph -->
<p>Until now, catching up was expensive: one <code>terraform import</code> command per resource, an HCL block written by hand for each one, attribute by attribute, and a plan that offers to destroy everything at the slightest missing line. Forty resources, a day gone. Clever Tools 5.3 and version 3.0 of <a href="https://www.clever.cloud/product/terraform/">our Terraform provider</a> bring the operation down to three commands: <code>clever features</code>, <code>clever terraform generate</code> and <code>terraform plan</code>. Here is how they fit together and, more importantly, what to check before you apply.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">What you need</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>You need three tools: Terraform 1.5 or later (or <a href="https://www.clever.cloud/blog/engineering/2026/03/25/opentofu-the-open-source-terraform-fork/">OpenTofu</a> 1.6 or later, the Terraform fork, which implements the same import blocks), Clever Tools 5.3 and version 3.0 of the Clever Cloud provider. The command we need is still in beta, so you have to enable it explicitly:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>npm install -g clever-tools@latest
clever login
clever features enable terraform</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">A word about our provider</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Open source from day one and published on both the Terraform and OpenTofu registries, the provider now covers most of the platform: runtimes, managed databases and add-ons, <a href="https://www.clever.cloud/product/cellar-object-storage/">object storage</a> and FS Buckets, log drains, <a href="https://www.clever.cloud/product/network-groups/">Network Groups</a>, <a href="https://www.clever.cloud/clever-kubernetes-engine/">Kubernetes clusters</a> and even OAuth consumers. Beyond provisioning, it can deploy your code from a Git repository and pin a commit, link an application to its add-ons the way <code>clever service link-addon</code> does, read the load balancer address for your DNS records, or browse a database’s backups. Since version 3.0, it accepts an API token for CI integration, and with Terraform 1.14 and later, its actions restart an application, run a SQL query against PostgreSQL or upload files to an FS Bucket without leaving <code>terraform apply</code>. Forty-eight resource types, two data sources and three actions: enough to describe an entire organisation, not just import it.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 1: declare the Clever Cloud Terraform provider</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In an empty directory, create a <code>provider.tf</code> file:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform {
  required_providers {
    clevercloud = {
      source  = "CleverCloud/clevercloud"
      version = "~&gt; 3.0"
    }
  }
}

provider "clevercloud" {}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The provider block stays empty: after <code>clever login</code>, the provider reuses the credentials of the active Clever Tools profile. All it still needs is the organisation to target, passed through an environment variable (a <code>user_...</code> identifier also works for a personal space):</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>export CC_ORGANISATION="orga_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
terraform init</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 2: generate import blocks with <code>clever terraform generate</code></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>This is where the new feature in Clever Tools 5.3 comes in. The <code>clever terraform generate</code> command goes through every application and add-on in an organisation and writes the import block Terraform expects for each one:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>clever terraform generate --org orga_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx &gt; imports.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The resulting file looks like this:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code># my-api
import {
  to = clevercloud_nodejs.my-api
  id = "app_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

# my-db
import {
  to = clevercloud_postgresql.my-db
  id = "postgresql_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Each application is mapped to the resource type for its runtime (<code>clevercloud_nodejs</code>, <code>clevercloud_php</code>, <code>clevercloud_docker</code>, <code>clevercloud_java_maven</code>...), and each add-on to its own (<code>clevercloud_postgresql</code>, <code>clevercloud_redis</code>, <code>clevercloud_cellar</code>...).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The command also filters out what you should not import. A Keycloak, Matomo, Metabase or Otoroshi add-on provisions its own application and databases, and an Elasticsearch add-on comes with its own Kibana. These resources show up in your organisation like any other, but they belong to the add-on that manages them. Importing them would hand them over to Terraform, which would then start changing them alongside the add-on. <code>clever terraform generate</code> skips them and reports it on standard error, just as it reports the few runtimes that have no equivalent in the provider yet. Nothing is dropped silently.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The names in the Terraform addresses are derived from your application and add-on names: lowercased, stripped of special characters, and prefixed with <code>_</code> if they start with a digit. Two resources with the same type and name, two Node.js applications both called “demo”, for example, cannot coexist in one configuration. The second one gets a suffix taken from its identifier (<code>demo-11ced66e</code>), and standard error reports it (<code>Name collision on nodejs.demo: suffixing with 11ced66e</code>). These names are only labels in your code: rename them as you like in <code>imports.tf</code> before step 3, and Terraform will generate the configuration under the new name.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>To adopt Terraform gradually, you can restrict generation to a single application, or to resources with a given tag:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>clever terraform generate --app my-api &gt; imports.tf
clever terraform generate --tag env:staging &gt; imports.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 3: generate the HCL configuration with <code>terraform plan</code></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Since version 1.5, Terraform can write the configuration for the resources targeted by import blocks on its own:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform plan -generate-config-out=generated.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Terraform reads each resource from the platform, writes its HCL block to <code>generated.tf</code> (the file must not already exist) and displays the corresponding plan, without writing anything to the state. The file contains the actual values: name, region, minimum and maximum instance counts, flavors, the add-on’s <code>plan</code> and dependencies.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>One point needs explaining. On an application, <code>min_instance_count</code>, <code>max_instance_count</code>, <code>smallest_flavor</code> and <code>biggest_flavor</code> are required, as is <code>plan</code> on an add-on, and they have no default: the provider will never fill them in on its own. These attributes determine what the resource costs, and we refuse to let a configuration stay silent about them and quietly adopt whatever the platform happens to be running. This is also the reason to prefer import blocks over the <code>terraform import</code> command, which writes directly to the state and requires you to write the full resource block yourself beforehand, or face a <code>Missing required argument</code> error on the next plan.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Secrets and environment variables: the Terraform import trap</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>This is the step where you need to slow down. Terraform never copies a value that the provider declares sensitive into a generated configuration, and the provider declares an application’s environment variables sensitive. For each application, the generated file therefore contains:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>resource "clevercloud_nodejs" "my-api" {
  name               = "my-api"
  region             = "par"
  min_instance_count = 1
  max_instance_count = 2
  smallest_flavor    = "XS"
  biggest_flavor     = "M"
  environment        = null # sensitive
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p><strong>Applying this file as is would delete all of the application’s environment variables.</strong> The provider offers an environment variable that works around this behaviour while you generate the configuration:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>BYPASS_SENSITIVE_IMPORTS=true terraform plan -generate-config-out=generated.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The provider then stops reporting these attributes as sensitive, so Terraform writes them out like any other, and <code>environment</code> contains all your variables in plain text. The file is not the only place: as long as the variable is set, the output of <code>terraform plan</code> also shows these values in plain text. Run this step from your own machine, never in a CI pipeline whose logs are kept. That is exactly what we wanted, and it is also why you need to be careful: API keys, external database passwords, third-party service tokens, everything you stored with <code>clever env set</code> ends up in a <code>.tf</code> file that you are about to commit.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Before the first commit, review these variables. Ordinary configuration values (<code>NODE_ENV</code>, a port, a public URL) can stay in the code. Secrets have to move out, to whatever tool your team already uses: HashiCorp Vault or OpenBao, your CI’s protected variables, a file encrypted with SOPS, or at the very least Terraform variables marked <code>sensitive</code> and supplied at run time:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>variable "stripe_api_key" {
  type      = string
  sensitive = true
}

resource "clevercloud_nodejs" "my-api" {
  # ...
  environment = {
    NODE_ENV       = "production"
    STRIPE_API_KEY = var.stripe_api_key
  }
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>This change shows up on the next plan, as an in-place update to <code>environment</code> even though the value has not changed: Terraform only records in the state that this value is now sensitive. The provider compares values, not their status, so identical values do not trigger an application restart.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Remove <code>BYPASS_SENSITIVE_IMPORTS</code> as soon as <code>generated.tf</code> is written. The configuration remains valid without it, and the provider displays the warning <code>Sensitive attributes are not redacted</code> on every command for as long as the variable is set. As for the Terraform state, it always contains these values, whatever you choose, so storing it deserves the same care as the secrets themselves.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 4: check the plan, then apply</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Once you have reviewed and cleaned up <code>generated.tf</code>, running <code>terraform plan</code> without the bypass variable should show one import per resource, and no change other than the <code>environment</code> attributes whose secrets you have just marked as sensitive:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform plan
# Plan: 2 to import, 0 to add, 1 to change, 0 to destroy.
# ~ environment = (sensitive value) value unchanged, sensitive status recorded
terraform apply</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Any other change means that an attribute in the configuration differs from what the platform is running: read it before confirming.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The apply then records the resources in the state, along with the sensitivity of the secrets passed as variables. It does not touch anything on the platform. When a final <code>terraform plan</code> reports <code>No changes.</code>, your infrastructure is described in code and you can remove the import blocks.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Code deployment stays your call</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>An import never fills in an application’s <code>deployment</code> block. The provider cannot guess whether you want Terraform to manage your deployments as well, and adding this block triggers a deployment on the first apply.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>You have three options. The first is to add nothing: Terraform manages the configuration, you keep deploying with <code>git push</code> or <code>clever deploy</code>, and a change to an environment variable restarts the application, just as it does today. The second is to set <code>commit = "github_hook"</code> in the <code>deployment</code> block if the application is linked to a GitHub repository: deployments remain driven by GitHub, and the provider never pushes code. The third is to hand deployment over to Terraform with <code>repository</code> and, if you wish, a specific <code>commit</code>. In that case, an API token (<code>clever tokens create</code>) is not enough, because it cannot push to Git, so the provider needs the OAuth credentials of the Clever Tools profile.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">After the import</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The <code>generated.tf</code> file is a starting point, not a finished configuration. A few changes make it maintainable: split the file by service or by environment, replace the hard-coded identifiers in the <code>dependencies</code> attribute with references (<code>clevercloud_postgresql.my-db.id</code>), and move whatever differs between staging and production into variables. And since the command is in beta, your feedback is welcome on the <a href="https://github.com/CleverCloud/clever-tools/issues">Clever Tools GitHub repository</a>: that is how it will move out of beta.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>That leaves the question of the state, which you currently keep in an S3 bucket or a third-party backend. We will not say more for now, except that it may soon no longer need to leave Clever Cloud.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">FAQ</h2>
<!-- /wp:heading -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Does the import modify my applications?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>No. Import blocks only write to the state. The only expected update is to <code>environment</code>, when you have replaced a secret with a <code>sensitive</code> variable: the value is identical and the provider does not restart the application. The only real risk is applying a configuration in which <code>environment</code> is <code>null</code>, which is the whole point of the section on secrets.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Can I import only part of my organisation?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Yes, with <code>--app</code> for a single application or <code>--tag</code> for resources with a given tag. Nothing forces you to migrate everything at once.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Why are some resources missing from the <code>imports.tf</code> file?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Either they are managed by an add-on (a Keycloak add-on’s application, an Elasticsearch add-on’s Kibana) and Terraform must not take them over, or their runtime does not have a resource in the provider yet. In both cases, the command says so on standard error.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Does it work with OpenTofu, the Terraform fork?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Yes. OpenTofu has supported import blocks and the <code>-generate-config-out</code> flag since version 1.6, and our provider is available on its registry.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Should I keep the import blocks?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>No. Once the resources are in the state, they no longer serve any purpose and can be deleted.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em>Clever Tools 5.3 is open source on <a href="https://github.com/CleverCloud/clever-tools">GitHub</a>, available on <a href="https://www.npmjs.com/package/clever-tools">npm</a> and in our package repositories. The Clever Cloud provider is published on the <a href="https://registry.terraform.io/providers/CleverCloud/clevercloud/latest">Terraform Registry</a>, its code is on <a href="https://github.com/CleverCloud/terraform-provider-clevercloud">GitHub</a>, and the full documentation is available at <a href="https://www.clever.cloud/developers/doc/tools/terraform/">www.clever.cloud/developers</a>.</em></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>That is exactly when infrastructure as code (IaC, the practice of defining your resources in versioned files that act as the source of truth) becomes the obvious next step, and when you discover that Terraform knows nothing about what already exists.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Until now, catching up was expensive: one <code>terraform import</code> command per resource, an HCL block written by hand for each one, attribute by attribute, and a plan that offers to destroy everything at the slightest missing line. Forty resources, a day gone. Clever Tools 5.3 and version 3.0 of <a href="https://www.clever.cloud/product/terraform/">our Terraform provider</a> bring the operation down to three commands: <code>clever features</code>, <code>clever terraform generate</code> and <code>terraform plan</code>. Here is how they fit together and, more importantly, what to check before you apply.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">What you need</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>You need three tools: Terraform 1.5 or later (or <a href="https://www.clever.cloud/blog/engineering/2026/03/25/opentofu-the-open-source-terraform-fork/">OpenTofu</a> 1.6 or later, the Terraform fork, which implements the same import blocks), Clever Tools 5.3 and version 3.0 of the Clever Cloud provider. The command we need is still in beta, so you have to enable it explicitly:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>npm install -g clever-tools@latest
clever login
clever features enable terraform</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">A word about our provider</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Open source from day one and published on both the Terraform and OpenTofu registries, the provider now covers most of the platform: runtimes, managed databases and add-ons, <a href="https://www.clever.cloud/product/cellar-object-storage/">object storage</a> and FS Buckets, log drains, <a href="https://www.clever.cloud/product/network-groups/">Network Groups</a>, <a href="https://www.clever.cloud/clever-kubernetes-engine/">Kubernetes clusters</a> and even OAuth consumers. Beyond provisioning, it can deploy your code from a Git repository and pin a commit, link an application to its add-ons the way <code>clever service link-addon</code> does, read the load balancer address for your DNS records, or browse a database’s backups. Since version 3.0, it accepts an API token for CI integration, and with Terraform 1.14 and later, its actions restart an application, run a SQL query against PostgreSQL or upload files to an FS Bucket without leaving <code>terraform apply</code>. Forty-eight resource types, two data sources and three actions: enough to describe an entire organisation, not just import it.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 1: declare the Clever Cloud Terraform provider</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In an empty directory, create a <code>provider.tf</code> file:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform {
  required_providers {
    clevercloud = {
      source  = "CleverCloud/clevercloud"
      version = "~&gt; 3.0"
    }
  }
}

provider "clevercloud" {}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The provider block stays empty: after <code>clever login</code>, the provider reuses the credentials of the active Clever Tools profile. All it still needs is the organisation to target, passed through an environment variable (a <code>user_...</code> identifier also works for a personal space):</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>export CC_ORGANISATION="orga_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
terraform init</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 2: generate import blocks with <code>clever terraform generate</code></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>This is where the new feature in Clever Tools 5.3 comes in. The <code>clever terraform generate</code> command goes through every application and add-on in an organisation and writes the import block Terraform expects for each one:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>clever terraform generate --org orga_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx &gt; imports.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The resulting file looks like this:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code># my-api
import {
  to = clevercloud_nodejs.my-api
  id = "app_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

# my-db
import {
  to = clevercloud_postgresql.my-db
  id = "postgresql_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Each application is mapped to the resource type for its runtime (<code>clevercloud_nodejs</code>, <code>clevercloud_php</code>, <code>clevercloud_docker</code>, <code>clevercloud_java_maven</code>...), and each add-on to its own (<code>clevercloud_postgresql</code>, <code>clevercloud_redis</code>, <code>clevercloud_cellar</code>...).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The command also filters out what you should not import. A Keycloak, Matomo, Metabase or Otoroshi add-on provisions its own application and databases, and an Elasticsearch add-on comes with its own Kibana. These resources show up in your organisation like any other, but they belong to the add-on that manages them. Importing them would hand them over to Terraform, which would then start changing them alongside the add-on. <code>clever terraform generate</code> skips them and reports it on standard error, just as it reports the few runtimes that have no equivalent in the provider yet. Nothing is dropped silently.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The names in the Terraform addresses are derived from your application and add-on names: lowercased, stripped of special characters, and prefixed with <code>_</code> if they start with a digit. Two resources with the same type and name, two Node.js applications both called “demo”, for example, cannot coexist in one configuration. The second one gets a suffix taken from its identifier (<code>demo-11ced66e</code>), and standard error reports it (<code>Name collision on nodejs.demo: suffixing with 11ced66e</code>). These names are only labels in your code: rename them as you like in <code>imports.tf</code> before step 3, and Terraform will generate the configuration under the new name.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>To adopt Terraform gradually, you can restrict generation to a single application, or to resources with a given tag:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>clever terraform generate --app my-api &gt; imports.tf
clever terraform generate --tag env:staging &gt; imports.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 3: generate the HCL configuration with <code>terraform plan</code></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Since version 1.5, Terraform can write the configuration for the resources targeted by import blocks on its own:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform plan -generate-config-out=generated.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Terraform reads each resource from the platform, writes its HCL block to <code>generated.tf</code> (the file must not already exist) and displays the corresponding plan, without writing anything to the state. The file contains the actual values: name, region, minimum and maximum instance counts, flavors, the add-on’s <code>plan</code> and dependencies.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>One point needs explaining. On an application, <code>min_instance_count</code>, <code>max_instance_count</code>, <code>smallest_flavor</code> and <code>biggest_flavor</code> are required, as is <code>plan</code> on an add-on, and they have no default: the provider will never fill them in on its own. These attributes determine what the resource costs, and we refuse to let a configuration stay silent about them and quietly adopt whatever the platform happens to be running. This is also the reason to prefer import blocks over the <code>terraform import</code> command, which writes directly to the state and requires you to write the full resource block yourself beforehand, or face a <code>Missing required argument</code> error on the next plan.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Secrets and environment variables: the Terraform import trap</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>This is the step where you need to slow down. Terraform never copies a value that the provider declares sensitive into a generated configuration, and the provider declares an application’s environment variables sensitive. For each application, the generated file therefore contains:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>resource "clevercloud_nodejs" "my-api" {
  name               = "my-api"
  region             = "par"
  min_instance_count = 1
  max_instance_count = 2
  smallest_flavor    = "XS"
  biggest_flavor     = "M"
  environment        = null # sensitive
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p><strong>Applying this file as is would delete all of the application’s environment variables.</strong> The provider offers an environment variable that works around this behaviour while you generate the configuration:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>BYPASS_SENSITIVE_IMPORTS=true terraform plan -generate-config-out=generated.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The provider then stops reporting these attributes as sensitive, so Terraform writes them out like any other, and <code>environment</code> contains all your variables in plain text. The file is not the only place: as long as the variable is set, the output of <code>terraform plan</code> also shows these values in plain text. Run this step from your own machine, never in a CI pipeline whose logs are kept. That is exactly what we wanted, and it is also why you need to be careful: API keys, external database passwords, third-party service tokens, everything you stored with <code>clever env set</code> ends up in a <code>.tf</code> file that you are about to commit.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Before the first commit, review these variables. Ordinary configuration values (<code>NODE_ENV</code>, a port, a public URL) can stay in the code. Secrets have to move out, to whatever tool your team already uses: HashiCorp Vault or OpenBao, your CI’s protected variables, a file encrypted with SOPS, or at the very least Terraform variables marked <code>sensitive</code> and supplied at run time:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>variable "stripe_api_key" {
  type      = string
  sensitive = true
}

resource "clevercloud_nodejs" "my-api" {
  # ...
  environment = {
    NODE_ENV       = "production"
    STRIPE_API_KEY = var.stripe_api_key
  }
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>This change shows up on the next plan, as an in-place update to <code>environment</code> even though the value has not changed: Terraform only records in the state that this value is now sensitive. The provider compares values, not their status, so identical values do not trigger an application restart.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Remove <code>BYPASS_SENSITIVE_IMPORTS</code> as soon as <code>generated.tf</code> is written. The configuration remains valid without it, and the provider displays the warning <code>Sensitive attributes are not redacted</code> on every command for as long as the variable is set. As for the Terraform state, it always contains these values, whatever you choose, so storing it deserves the same care as the secrets themselves.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 4: check the plan, then apply</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Once you have reviewed and cleaned up <code>generated.tf</code>, running <code>terraform plan</code> without the bypass variable should show one import per resource, and no change other than the <code>environment</code> attributes whose secrets you have just marked as sensitive:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform plan
# Plan: 2 to import, 0 to add, 1 to change, 0 to destroy.
# ~ environment = (sensitive value) value unchanged, sensitive status recorded
terraform apply</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Any other change means that an attribute in the configuration differs from what the platform is running: read it before confirming.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The apply then records the resources in the state, along with the sensitivity of the secrets passed as variables. It does not touch anything on the platform. When a final <code>terraform plan</code> reports <code>No changes.</code>, your infrastructure is described in code and you can remove the import blocks.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Code deployment stays your call</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>An import never fills in an application’s <code>deployment</code> block. The provider cannot guess whether you want Terraform to manage your deployments as well, and adding this block triggers a deployment on the first apply.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>You have three options. The first is to add nothing: Terraform manages the configuration, you keep deploying with <code>git push</code> or <code>clever deploy</code>, and a change to an environment variable restarts the application, just as it does today. The second is to set <code>commit = "github_hook"</code> in the <code>deployment</code> block if the application is linked to a GitHub repository: deployments remain driven by GitHub, and the provider never pushes code. The third is to hand deployment over to Terraform with <code>repository</code> and, if you wish, a specific <code>commit</code>. In that case, an API token (<code>clever tokens create</code>) is not enough, because it cannot push to Git, so the provider needs the OAuth credentials of the Clever Tools profile.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">After the import</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The <code>generated.tf</code> file is a starting point, not a finished configuration. A few changes make it maintainable: split the file by service or by environment, replace the hard-coded identifiers in the <code>dependencies</code> attribute with references (<code>clevercloud_postgresql.my-db.id</code>), and move whatever differs between staging and production into variables. And since the command is in beta, your feedback is welcome on the <a href="https://github.com/CleverCloud/clever-tools/issues">Clever Tools GitHub repository</a>: that is how it will move out of beta.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>That leaves the question of the state, which you currently keep in an S3 bucket or a third-party backend. We will not say more for now, except that it may soon no longer need to leave Clever Cloud.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">FAQ</h2>
<!-- /wp:heading -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Does the import modify my applications?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>No. Import blocks only write to the state. The only expected update is to <code>environment</code>, when you have replaced a secret with a <code>sensitive</code> variable: the value is identical and the provider does not restart the application. The only real risk is applying a configuration in which <code>environment</code> is <code>null</code>, which is the whole point of the section on secrets.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Can I import only part of my organisation?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Yes, with <code>--app</code> for a single application or <code>--tag</code> for resources with a given tag. Nothing forces you to migrate everything at once.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Why are some resources missing from the <code>imports.tf</code> file?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Either they are managed by an add-on (a Keycloak add-on’s application, an Elasticsearch add-on’s Kibana) and Terraform must not take them over, or their runtime does not have a resource in the provider yet. In both cases, the command says so on standard error.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Does it work with OpenTofu, the Terraform fork?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Yes. OpenTofu has supported import blocks and the <code>-generate-config-out</code> flag since version 1.6, and our provider is available on its registry.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Should I keep the import blocks?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>No. Once the resources are in the state, they no longer serve any purpose and can be deleted.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em>Clever Tools 5.3 is open source on <a href="https://github.com/CleverCloud/clever-tools">GitHub</a>, available on <a href="https://www.npmjs.com/package/clever-tools">npm</a> and in our package repositories. The Clever Cloud provider is published on the <a href="https://registry.terraform.io/providers/CleverCloud/clevercloud/latest">Terraform Registry</a>, its code is on <a href="https://github.com/CleverCloud/terraform-provider-clevercloud">GitHub</a>, and the full documentation is available at <a href="https://www.clever.cloud/developers/doc/tools/terraform/">www.clever.cloud/developers</a>.</em></p>
<!-- /wp:paragraph -->]]></description>
										<content:encoded><![CDATA[<p><img width="2400" height="1065" src="https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="2026.11.03 Clever Cloud Bannière Blog Import Terraform EN" decoding="async" srcset="https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en.png 2400w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-300x133.png 300w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-1024x454.png 1024w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-768x341.png 768w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-1536x682.png 1536w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-2048x909.png 2048w, https://cdn.clever-cloud.com/uploads/2026/11/2026-11-03-clever-cloud-banniere-blog-import-terraform-en-1368x607.png 1368w" sizes="(max-width: 2400px) 100vw, 2400px" /></p><!-- wp:paragraph -->
<p>Until now, catching up was expensive: one <code>terraform import</code> command per resource, an HCL block written by hand for each one, attribute by attribute, and a plan that offers to destroy everything at the slightest missing line. Forty resources, a day gone. Clever Tools 5.3 and version 3.0 of <a href="https://www.clever.cloud/product/terraform/">our Terraform provider</a> bring the operation down to three commands: <code>clever features</code>, <code>clever terraform generate</code> and <code>terraform plan</code>. Here is how they fit together and, more importantly, what to check before you apply.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">What you need</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>You need three tools: Terraform 1.5 or later (or <a href="https://www.clever.cloud/blog/engineering/2026/03/25/opentofu-the-open-source-terraform-fork/">OpenTofu</a> 1.6 or later, the Terraform fork, which implements the same import blocks), Clever Tools 5.3 and version 3.0 of the Clever Cloud provider. The command we need is still in beta, so you have to enable it explicitly:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>npm install -g clever-tools@latest
clever login
clever features enable terraform</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">A word about our provider</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Open source from day one and published on both the Terraform and OpenTofu registries, the provider now covers most of the platform: runtimes, managed databases and add-ons, <a href="https://www.clever.cloud/product/cellar-object-storage/">object storage</a> and FS Buckets, log drains, <a href="https://www.clever.cloud/product/network-groups/">Network Groups</a>, <a href="https://www.clever.cloud/clever-kubernetes-engine/">Kubernetes clusters</a> and even OAuth consumers. Beyond provisioning, it can deploy your code from a Git repository and pin a commit, link an application to its add-ons the way <code>clever service link-addon</code> does, read the load balancer address for your DNS records, or browse a database’s backups. Since version 3.0, it accepts an API token for CI integration, and with Terraform 1.14 and later, its actions restart an application, run a SQL query against PostgreSQL or upload files to an FS Bucket without leaving <code>terraform apply</code>. Forty-eight resource types, two data sources and three actions: enough to describe an entire organisation, not just import it.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 1: declare the Clever Cloud Terraform provider</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In an empty directory, create a <code>provider.tf</code> file:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform {
  required_providers {
    clevercloud = {
      source  = "CleverCloud/clevercloud"
      version = "~&gt; 3.0"
    }
  }
}

provider "clevercloud" {}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The provider block stays empty: after <code>clever login</code>, the provider reuses the credentials of the active Clever Tools profile. All it still needs is the organisation to target, passed through an environment variable (a <code>user_...</code> identifier also works for a personal space):</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>export CC_ORGANISATION="orga_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
terraform init</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 2: generate import blocks with <code>clever terraform generate</code></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>This is where the new feature in Clever Tools 5.3 comes in. The <code>clever terraform generate</code> command goes through every application and add-on in an organisation and writes the import block Terraform expects for each one:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>clever terraform generate --org orga_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx &gt; imports.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The resulting file looks like this:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code># my-api
import {
  to = clevercloud_nodejs.my-api
  id = "app_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

# my-db
import {
  to = clevercloud_postgresql.my-db
  id = "postgresql_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Each application is mapped to the resource type for its runtime (<code>clevercloud_nodejs</code>, <code>clevercloud_php</code>, <code>clevercloud_docker</code>, <code>clevercloud_java_maven</code>...), and each add-on to its own (<code>clevercloud_postgresql</code>, <code>clevercloud_redis</code>, <code>clevercloud_cellar</code>...).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The command also filters out what you should not import. A Keycloak, Matomo, Metabase or Otoroshi add-on provisions its own application and databases, and an Elasticsearch add-on comes with its own Kibana. These resources show up in your organisation like any other, but they belong to the add-on that manages them. Importing them would hand them over to Terraform, which would then start changing them alongside the add-on. <code>clever terraform generate</code> skips them and reports it on standard error, just as it reports the few runtimes that have no equivalent in the provider yet. Nothing is dropped silently.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The names in the Terraform addresses are derived from your application and add-on names: lowercased, stripped of special characters, and prefixed with <code>_</code> if they start with a digit. Two resources with the same type and name, two Node.js applications both called “demo”, for example, cannot coexist in one configuration. The second one gets a suffix taken from its identifier (<code>demo-11ced66e</code>), and standard error reports it (<code>Name collision on nodejs.demo: suffixing with 11ced66e</code>). These names are only labels in your code: rename them as you like in <code>imports.tf</code> before step 3, and Terraform will generate the configuration under the new name.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>To adopt Terraform gradually, you can restrict generation to a single application, or to resources with a given tag:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>clever terraform generate --app my-api &gt; imports.tf
clever terraform generate --tag env:staging &gt; imports.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 3: generate the HCL configuration with <code>terraform plan</code></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Since version 1.5, Terraform can write the configuration for the resources targeted by import blocks on its own:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform plan -generate-config-out=generated.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Terraform reads each resource from the platform, writes its HCL block to <code>generated.tf</code> (the file must not already exist) and displays the corresponding plan, without writing anything to the state. The file contains the actual values: name, region, minimum and maximum instance counts, flavors, the add-on’s <code>plan</code> and dependencies.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>One point needs explaining. On an application, <code>min_instance_count</code>, <code>max_instance_count</code>, <code>smallest_flavor</code> and <code>biggest_flavor</code> are required, as is <code>plan</code> on an add-on, and they have no default: the provider will never fill them in on its own. These attributes determine what the resource costs, and we refuse to let a configuration stay silent about them and quietly adopt whatever the platform happens to be running. This is also the reason to prefer import blocks over the <code>terraform import</code> command, which writes directly to the state and requires you to write the full resource block yourself beforehand, or face a <code>Missing required argument</code> error on the next plan.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Secrets and environment variables: the Terraform import trap</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>This is the step where you need to slow down. Terraform never copies a value that the provider declares sensitive into a generated configuration, and the provider declares an application’s environment variables sensitive. For each application, the generated file therefore contains:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>resource "clevercloud_nodejs" "my-api" {
  name               = "my-api"
  region             = "par"
  min_instance_count = 1
  max_instance_count = 2
  smallest_flavor    = "XS"
  biggest_flavor     = "M"
  environment        = null # sensitive
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p><strong>Applying this file as is would delete all of the application’s environment variables.</strong> The provider offers an environment variable that works around this behaviour while you generate the configuration:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>BYPASS_SENSITIVE_IMPORTS=true terraform plan -generate-config-out=generated.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The provider then stops reporting these attributes as sensitive, so Terraform writes them out like any other, and <code>environment</code> contains all your variables in plain text. The file is not the only place: as long as the variable is set, the output of <code>terraform plan</code> also shows these values in plain text. Run this step from your own machine, never in a CI pipeline whose logs are kept. That is exactly what we wanted, and it is also why you need to be careful: API keys, external database passwords, third-party service tokens, everything you stored with <code>clever env set</code> ends up in a <code>.tf</code> file that you are about to commit.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Before the first commit, review these variables. Ordinary configuration values (<code>NODE_ENV</code>, a port, a public URL) can stay in the code. Secrets have to move out, to whatever tool your team already uses: HashiCorp Vault or OpenBao, your CI’s protected variables, a file encrypted with SOPS, or at the very least Terraform variables marked <code>sensitive</code> and supplied at run time:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>variable "stripe_api_key" {
  type      = string
  sensitive = true
}

resource "clevercloud_nodejs" "my-api" {
  # ...
  environment = {
    NODE_ENV       = "production"
    STRIPE_API_KEY = var.stripe_api_key
  }
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>This change shows up on the next plan, as an in-place update to <code>environment</code> even though the value has not changed: Terraform only records in the state that this value is now sensitive. The provider compares values, not their status, so identical values do not trigger an application restart.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Remove <code>BYPASS_SENSITIVE_IMPORTS</code> as soon as <code>generated.tf</code> is written. The configuration remains valid without it, and the provider displays the warning <code>Sensitive attributes are not redacted</code> on every command for as long as the variable is set. As for the Terraform state, it always contains these values, whatever you choose, so storing it deserves the same care as the secrets themselves.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 4: check the plan, then apply</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Once you have reviewed and cleaned up <code>generated.tf</code>, running <code>terraform plan</code> without the bypass variable should show one import per resource, and no change other than the <code>environment</code> attributes whose secrets you have just marked as sensitive:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform plan
# Plan: 2 to import, 0 to add, 1 to change, 0 to destroy.
# ~ environment = (sensitive value) value unchanged, sensitive status recorded
terraform apply</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Any other change means that an attribute in the configuration differs from what the platform is running: read it before confirming.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The apply then records the resources in the state, along with the sensitivity of the secrets passed as variables. It does not touch anything on the platform. When a final <code>terraform plan</code> reports <code>No changes.</code>, your infrastructure is described in code and you can remove the import blocks.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Code deployment stays your call</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>An import never fills in an application’s <code>deployment</code> block. The provider cannot guess whether you want Terraform to manage your deployments as well, and adding this block triggers a deployment on the first apply.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>You have three options. The first is to add nothing: Terraform manages the configuration, you keep deploying with <code>git push</code> or <code>clever deploy</code>, and a change to an environment variable restarts the application, just as it does today. The second is to set <code>commit = "github_hook"</code> in the <code>deployment</code> block if the application is linked to a GitHub repository: deployments remain driven by GitHub, and the provider never pushes code. The third is to hand deployment over to Terraform with <code>repository</code> and, if you wish, a specific <code>commit</code>. In that case, an API token (<code>clever tokens create</code>) is not enough, because it cannot push to Git, so the provider needs the OAuth credentials of the Clever Tools profile.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">After the import</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The <code>generated.tf</code> file is a starting point, not a finished configuration. A few changes make it maintainable: split the file by service or by environment, replace the hard-coded identifiers in the <code>dependencies</code> attribute with references (<code>clevercloud_postgresql.my-db.id</code>), and move whatever differs between staging and production into variables. And since the command is in beta, your feedback is welcome on the <a href="https://github.com/CleverCloud/clever-tools/issues">Clever Tools GitHub repository</a>: that is how it will move out of beta.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>That leaves the question of the state, which you currently keep in an S3 bucket or a third-party backend. We will not say more for now, except that it may soon no longer need to leave Clever Cloud.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">FAQ</h2>
<!-- /wp:heading -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Does the import modify my applications?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>No. Import blocks only write to the state. The only expected update is to <code>environment</code>, when you have replaced a secret with a <code>sensitive</code> variable: the value is identical and the provider does not restart the application. The only real risk is applying a configuration in which <code>environment</code> is <code>null</code>, which is the whole point of the section on secrets.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Can I import only part of my organisation?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Yes, with <code>--app</code> for a single application or <code>--tag</code> for resources with a given tag. Nothing forces you to migrate everything at once.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Why are some resources missing from the <code>imports.tf</code> file?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Either they are managed by an add-on (a Keycloak add-on’s application, an Elasticsearch add-on’s Kibana) and Terraform must not take them over, or their runtime does not have a resource in the provider yet. In both cases, the command says so on standard error.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Does it work with OpenTofu, the Terraform fork?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Yes. OpenTofu has supported import blocks and the <code>-generate-config-out</code> flag since version 1.6, and our provider is available on its registry.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Should I keep the import blocks?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>No. Once the resources are in the state, they no longer serve any purpose and can be deleted.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em>Clever Tools 5.3 is open source on <a href="https://github.com/CleverCloud/clever-tools">GitHub</a>, available on <a href="https://www.npmjs.com/package/clever-tools">npm</a> and in our package repositories. The Clever Cloud provider is published on the <a href="https://registry.terraform.io/providers/CleverCloud/clevercloud/latest">Terraform Registry</a>, its code is on <a href="https://github.com/CleverCloud/terraform-provider-clevercloud">GitHub</a>, and the full documentation is available at <a href="https://www.clever.cloud/developers/doc/tools/terraform/">www.clever.cloud/developers</a>.</em></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>That is exactly when infrastructure as code (IaC, the practice of defining your resources in versioned files that act as the source of truth) becomes the obvious next step, and when you discover that Terraform knows nothing about what already exists.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Until now, catching up was expensive: one <code>terraform import</code> command per resource, an HCL block written by hand for each one, attribute by attribute, and a plan that offers to destroy everything at the slightest missing line. Forty resources, a day gone. Clever Tools 5.3 and version 3.0 of <a href="https://www.clever.cloud/product/terraform/">our Terraform provider</a> bring the operation down to three commands: <code>clever features</code>, <code>clever terraform generate</code> and <code>terraform plan</code>. Here is how they fit together and, more importantly, what to check before you apply.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">What you need</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>You need three tools: Terraform 1.5 or later (or <a href="https://www.clever.cloud/blog/engineering/2026/03/25/opentofu-the-open-source-terraform-fork/">OpenTofu</a> 1.6 or later, the Terraform fork, which implements the same import blocks), Clever Tools 5.3 and version 3.0 of the Clever Cloud provider. The command we need is still in beta, so you have to enable it explicitly:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>npm install -g clever-tools@latest
clever login
clever features enable terraform</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">A word about our provider</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Open source from day one and published on both the Terraform and OpenTofu registries, the provider now covers most of the platform: runtimes, managed databases and add-ons, <a href="https://www.clever.cloud/product/cellar-object-storage/">object storage</a> and FS Buckets, log drains, <a href="https://www.clever.cloud/product/network-groups/">Network Groups</a>, <a href="https://www.clever.cloud/clever-kubernetes-engine/">Kubernetes clusters</a> and even OAuth consumers. Beyond provisioning, it can deploy your code from a Git repository and pin a commit, link an application to its add-ons the way <code>clever service link-addon</code> does, read the load balancer address for your DNS records, or browse a database’s backups. Since version 3.0, it accepts an API token for CI integration, and with Terraform 1.14 and later, its actions restart an application, run a SQL query against PostgreSQL or upload files to an FS Bucket without leaving <code>terraform apply</code>. Forty-eight resource types, two data sources and three actions: enough to describe an entire organisation, not just import it.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 1: declare the Clever Cloud Terraform provider</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In an empty directory, create a <code>provider.tf</code> file:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform {
  required_providers {
    clevercloud = {
      source  = "CleverCloud/clevercloud"
      version = "~&gt; 3.0"
    }
  }
}

provider "clevercloud" {}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The provider block stays empty: after <code>clever login</code>, the provider reuses the credentials of the active Clever Tools profile. All it still needs is the organisation to target, passed through an environment variable (a <code>user_...</code> identifier also works for a personal space):</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>export CC_ORGANISATION="orga_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
terraform init</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 2: generate import blocks with <code>clever terraform generate</code></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>This is where the new feature in Clever Tools 5.3 comes in. The <code>clever terraform generate</code> command goes through every application and add-on in an organisation and writes the import block Terraform expects for each one:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>clever terraform generate --org orga_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx &gt; imports.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The resulting file looks like this:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code># my-api
import {
  to = clevercloud_nodejs.my-api
  id = "app_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

# my-db
import {
  to = clevercloud_postgresql.my-db
  id = "postgresql_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Each application is mapped to the resource type for its runtime (<code>clevercloud_nodejs</code>, <code>clevercloud_php</code>, <code>clevercloud_docker</code>, <code>clevercloud_java_maven</code>...), and each add-on to its own (<code>clevercloud_postgresql</code>, <code>clevercloud_redis</code>, <code>clevercloud_cellar</code>...).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The command also filters out what you should not import. A Keycloak, Matomo, Metabase or Otoroshi add-on provisions its own application and databases, and an Elasticsearch add-on comes with its own Kibana. These resources show up in your organisation like any other, but they belong to the add-on that manages them. Importing them would hand them over to Terraform, which would then start changing them alongside the add-on. <code>clever terraform generate</code> skips them and reports it on standard error, just as it reports the few runtimes that have no equivalent in the provider yet. Nothing is dropped silently.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The names in the Terraform addresses are derived from your application and add-on names: lowercased, stripped of special characters, and prefixed with <code>_</code> if they start with a digit. Two resources with the same type and name, two Node.js applications both called “demo”, for example, cannot coexist in one configuration. The second one gets a suffix taken from its identifier (<code>demo-11ced66e</code>), and standard error reports it (<code>Name collision on nodejs.demo: suffixing with 11ced66e</code>). These names are only labels in your code: rename them as you like in <code>imports.tf</code> before step 3, and Terraform will generate the configuration under the new name.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>To adopt Terraform gradually, you can restrict generation to a single application, or to resources with a given tag:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>clever terraform generate --app my-api &gt; imports.tf
clever terraform generate --tag env:staging &gt; imports.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 3: generate the HCL configuration with <code>terraform plan</code></h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Since version 1.5, Terraform can write the configuration for the resources targeted by import blocks on its own:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform plan -generate-config-out=generated.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Terraform reads each resource from the platform, writes its HCL block to <code>generated.tf</code> (the file must not already exist) and displays the corresponding plan, without writing anything to the state. The file contains the actual values: name, region, minimum and maximum instance counts, flavors, the add-on’s <code>plan</code> and dependencies.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>One point needs explaining. On an application, <code>min_instance_count</code>, <code>max_instance_count</code>, <code>smallest_flavor</code> and <code>biggest_flavor</code> are required, as is <code>plan</code> on an add-on, and they have no default: the provider will never fill them in on its own. These attributes determine what the resource costs, and we refuse to let a configuration stay silent about them and quietly adopt whatever the platform happens to be running. This is also the reason to prefer import blocks over the <code>terraform import</code> command, which writes directly to the state and requires you to write the full resource block yourself beforehand, or face a <code>Missing required argument</code> error on the next plan.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Secrets and environment variables: the Terraform import trap</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>This is the step where you need to slow down. Terraform never copies a value that the provider declares sensitive into a generated configuration, and the provider declares an application’s environment variables sensitive. For each application, the generated file therefore contains:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>resource "clevercloud_nodejs" "my-api" {
  name               = "my-api"
  region             = "par"
  min_instance_count = 1
  max_instance_count = 2
  smallest_flavor    = "XS"
  biggest_flavor     = "M"
  environment        = null # sensitive
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p><strong>Applying this file as is would delete all of the application’s environment variables.</strong> The provider offers an environment variable that works around this behaviour while you generate the configuration:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>BYPASS_SENSITIVE_IMPORTS=true terraform plan -generate-config-out=generated.tf</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>The provider then stops reporting these attributes as sensitive, so Terraform writes them out like any other, and <code>environment</code> contains all your variables in plain text. The file is not the only place: as long as the variable is set, the output of <code>terraform plan</code> also shows these values in plain text. Run this step from your own machine, never in a CI pipeline whose logs are kept. That is exactly what we wanted, and it is also why you need to be careful: API keys, external database passwords, third-party service tokens, everything you stored with <code>clever env set</code> ends up in a <code>.tf</code> file that you are about to commit.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Before the first commit, review these variables. Ordinary configuration values (<code>NODE_ENV</code>, a port, a public URL) can stay in the code. Secrets have to move out, to whatever tool your team already uses: HashiCorp Vault or OpenBao, your CI’s protected variables, a file encrypted with SOPS, or at the very least Terraform variables marked <code>sensitive</code> and supplied at run time:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>variable "stripe_api_key" {
  type      = string
  sensitive = true
}

resource "clevercloud_nodejs" "my-api" {
  # ...
  environment = {
    NODE_ENV       = "production"
    STRIPE_API_KEY = var.stripe_api_key
  }
}</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>This change shows up on the next plan, as an in-place update to <code>environment</code> even though the value has not changed: Terraform only records in the state that this value is now sensitive. The provider compares values, not their status, so identical values do not trigger an application restart.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Remove <code>BYPASS_SENSITIVE_IMPORTS</code> as soon as <code>generated.tf</code> is written. The configuration remains valid without it, and the provider displays the warning <code>Sensitive attributes are not redacted</code> on every command for as long as the variable is set. As for the Terraform state, it always contains these values, whatever you choose, so storing it deserves the same care as the secrets themselves.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Step 4: check the plan, then apply</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Once you have reviewed and cleaned up <code>generated.tf</code>, running <code>terraform plan</code> without the bypass variable should show one import per resource, and no change other than the <code>environment</code> attributes whose secrets you have just marked as sensitive:</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<div style="
    max-width: 780px;
    margin: 1.5rem auto;
    padding: 16px 20px;
    background: #0f172a;
    color: #e5eefc;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
      Roboto, 'Helvetica Neue', Arial, sans-serif;
    font-size: 15px;
    line-height: 1.7;
  ">
  <pre style="
      margin: 0;
      overflow-x: auto;
      white-space: pre-wrap;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
        'Liberation Mono', 'Courier New', monospace;
    "><code>terraform plan
# Plan: 2 to import, 0 to add, 1 to change, 0 to destroy.
# ~ environment = (sensitive value) value unchanged, sensitive status recorded
terraform apply</code></pre>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>Any other change means that an attribute in the configuration differs from what the platform is running: read it before confirming.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The apply then records the resources in the state, along with the sensitivity of the secrets passed as variables. It does not touch anything on the platform. When a final <code>terraform plan</code> reports <code>No changes.</code>, your infrastructure is described in code and you can remove the import blocks.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Code deployment stays your call</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>An import never fills in an application’s <code>deployment</code> block. The provider cannot guess whether you want Terraform to manage your deployments as well, and adding this block triggers a deployment on the first apply.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>You have three options. The first is to add nothing: Terraform manages the configuration, you keep deploying with <code>git push</code> or <code>clever deploy</code>, and a change to an environment variable restarts the application, just as it does today. The second is to set <code>commit = "github_hook"</code> in the <code>deployment</code> block if the application is linked to a GitHub repository: deployments remain driven by GitHub, and the provider never pushes code. The third is to hand deployment over to Terraform with <code>repository</code> and, if you wish, a specific <code>commit</code>. In that case, an API token (<code>clever tokens create</code>) is not enough, because it cannot push to Git, so the provider needs the OAuth credentials of the Clever Tools profile.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">After the import</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The <code>generated.tf</code> file is a starting point, not a finished configuration. A few changes make it maintainable: split the file by service or by environment, replace the hard-coded identifiers in the <code>dependencies</code> attribute with references (<code>clevercloud_postgresql.my-db.id</code>), and move whatever differs between staging and production into variables. And since the command is in beta, your feedback is welcome on the <a href="https://github.com/CleverCloud/clever-tools/issues">Clever Tools GitHub repository</a>: that is how it will move out of beta.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>That leaves the question of the state, which you currently keep in an S3 bucket or a third-party backend. We will not say more for now, except that it may soon no longer need to leave Clever Cloud.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">FAQ</h2>
<!-- /wp:heading -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Does the import modify my applications?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>No. Import blocks only write to the state. The only expected update is to <code>environment</code>, when you have replaced a secret with a <code>sensitive</code> variable: the value is identical and the provider does not restart the application. The only real risk is applying a configuration in which <code>environment</code> is <code>null</code>, which is the whole point of the section on secrets.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Can I import only part of my organisation?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Yes, with <code>--app</code> for a single application or <code>--tag</code> for resources with a given tag. Nothing forces you to migrate everything at once.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Why are some resources missing from the <code>imports.tf</code> file?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Either they are managed by an add-on (a Keycloak add-on’s application, an Elasticsearch add-on’s Kibana) and Terraform must not take them over, or their runtime does not have a resource in the provider yet. In both cases, the command says so on standard error.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Does it work with OpenTofu, the Terraform fork?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Yes. OpenTofu has supported import blocks and the <code>-generate-config-out</code> flag since version 1.6, and our provider is available on its registry.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Should I keep the import blocks?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>No. Once the resources are in the state, they no longer serve any purpose and can be deleted.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em>Clever Tools 5.3 is open source on <a href="https://github.com/CleverCloud/clever-tools">GitHub</a>, available on <a href="https://www.npmjs.com/package/clever-tools">npm</a> and in our package repositories. The Clever Cloud provider is published on the <a href="https://registry.terraform.io/providers/CleverCloud/clevercloud/latest">Terraform Registry</a>, its code is on <a href="https://github.com/CleverCloud/terraform-provider-clevercloud">GitHub</a>, and the full documentation is available at <a href="https://www.clever.cloud/developers/doc/tools/terraform/">www.clever.cloud/developers</a>.</em></p>
<!-- /wp:paragraph -->]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OpenTofu: the open-source Terraform fork — natively supported on Clever Cloud</title>
		<link>https://www.clever.cloud/blog/engineering/2026/03/25/opentofu-the-open-source-terraform-fork/</link>
		
		<dc:creator><![CDATA[Carine Guillemet]]></dc:creator>
		<pubDate>Wed, 25 Mar 2026 09:44:14 +0000</pubDate>
				<category><![CDATA[Engineering]]></category>
		<category><![CDATA[Features]]></category>
		<category><![CDATA[opentofu]]></category>
		<category><![CDATA[terraform]]></category>
		<guid isPermaLink="false">https://www.clever.cloud/?p=23944</guid>

					<description><![CDATA[<p><img width="800" height="355" src="https://cdn.clever-cloud.com/uploads/2026/03/2026-03-18-clever-cloud-banniere-blog-paas-secteur-public-eng-2-2.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="2026 03 18 clever cloud banniere blog paas secteur public eng 2 2" decoding="async" srcset="https://cdn.clever-cloud.com/uploads/2026/03/2026-03-18-clever-cloud-banniere-blog-paas-secteur-public-eng-2-2.png 800w, https://cdn.clever-cloud.com/uploads/2026/03/2026-03-18-clever-cloud-banniere-blog-paas-secteur-public-eng-2-2-300x133.png 300w, https://cdn.clever-cloud.com/uploads/2026/03/2026-03-18-clever-cloud-banniere-blog-paas-secteur-public-eng-2-2-768x341.png 768w" sizes="(max-width: 800px) 100vw, 800px" /></p><!-- wp:paragraph -->
<p>BSL is not an open-source license as defined by the Open Source Initiative: it restricts commercial use deemed competitive with HashiCorp's offerings. Overnight, thousands of organizations that had built their infrastructure around Terraform found themselves in uncomfortable legal uncertainty.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The response was swift. Less than a month after the announcement, a manifesto calling for a return to an open-source license had gathered 33,000 GitHub stars and the support of nearly 140 companies. HashiCorp did not budge. The fork was published in September 2023, brought into the Linux Foundation, and renamed OpenTofu. Since January 2024, it has been in stable production. Since April 2025, it has been part of the Cloud Native Computing Foundation (CNCF).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Our <a href="https://www.clever.cloud/product/terraform/" type="link" id="https://www.clever.cloud/product/terraform/">Terraform</a> provider is fully compatible with OpenTofu. No changes to your configurations are needed to use it.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">What is OpenTofu?</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>OpenTofu is an open-source infrastructure as code (IaC) tool, forked from the last MPL 2.0-licensed version of Terraform (1.5.x), distributed under that same Mozilla Public License 2.0. It lets you describe, provision, and manage cloud infrastructure through declarative configuration files written in HashiCorp Configuration Language.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>It is developed under the governance of the Linux Foundation, with a Technical Steering Committee made up of representatives from multiple organizations — Gruntwork, Spacelift, Harness, Env0, Scalr — so that no single company can control its direction. That governance model is precisely what convinced many teams to make the switch: a guarantee that the tool will remain truly open source, regardless of how its contributors evolve commercially.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In practice, OpenTofu works with the same&nbsp;<code>.tf</code>&nbsp;files, the same providers, the same modules, and the same CLI workflow as Terraform —&nbsp;<code>init</code>,&nbsp;<code>plan</code>,&nbsp;<code>apply</code>,&nbsp;<code>destroy</code>. For the vast majority of teams, migrating comes down to replacing the binary.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">What's the actual difference with Terraform?</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The main difference is the license, and everything it implies in the long run.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Terraform is now distributed under the BSL, which restricts commercial uses that could be considered competitive with HashiCorp — a deliberately vague notion that HashiCorp (now a subsidiary of IBM) may interpret differently in the future. OpenTofu is distributed under MPL 2.0, a true open-source license with no commercial restrictions.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Beyond the license, the two projects have started to diverge technically since the fork. The most notable differences to date:&lt;ul&gt; &lt;li&gt;&lt;strong&gt;State encryption:&lt;/strong&gt; OpenTofu natively supports client-side encryption of state files — a feature long requested by the community that Terraform has still not implemented.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p></p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<ul></li> <li><strong>Governance:</strong> OpenTofu is steered by a multi-organization technical committee within the Linux Foundation. Terraform is controlled by IBM via HashiCorp, with a roadmap driven by commercial priorities.</li> <li><strong>Compatibility:</strong> OpenTofu maintains syntactic and functional compatibility with Terraform. Existing providers, modules, and configurations work without modification for the vast majority of use cases.</li> <li><strong>The <code>.tofu</code> extension:</strong> OpenTofu supports this extension alongside <code>.tf</code>, allowing module authors to provide OpenTofu-specific variants while remaining compatible with Terraform.</li> <li><strong>Terraform Actions:</strong> HashiCorp introduced "actions" in Terraform 1.9+, a feature that lets you trigger custom behaviors during resource lifecycle events. OpenTofu does not yet support them — this is the inherent trade-off of any fork: new proprietary Terraform features are not automatically available, and their implementation in OpenTofu depends on the technical committee's priorities.</li> </ul>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Why teams are migrating to OpenTofu</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Three reasons come up consistently.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>Legal risk.</strong> The BSL prohibits using Terraform in products that could compete with HashiCorp's commercial offerings. For teams building internal platforms, cloud products, or <a href="https://www.clever.cloud/blog/company/2025/03/19/managed-services/">managed services</a>, this uncertainty is a real operational risk — especially since the definition of "competitive" belongs to HashiCorp and can evolve. On top of that, there is a less visible but equally significant reality: in the Terraform registry, open-source providers get no special visibility. Getting an "official" badge requires negotiating directly with HashiCorp — and paying for it. In the OpenTofu registry, all providers are on equal footing, regardless of the size or resources of their maintainers.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>Strategic autonomy over tooling.</strong>&nbsp;Your infrastructure as code is the foundation of everything else. Depending on a tool controlled by a US company subject to the Cloud Act means introducing an additional dependency that many organizations would rather avoid. OpenTofu, under Linux Foundation / CNCF governance, offers continuity independent of any commercial actor.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>Features Terraform doesn't ship.</strong>&nbsp;Native state file encryption is the most frequently cited example: requested by the community for years, implemented within months by OpenTofu. It is also a signal about how quickly community-driven governance can respond to real needs, compared to a roadmap driven by commercial interests.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Migrating from Terraform to OpenTofu</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>For the vast majority of teams, migration takes a matter of minutes. All you need to do is make sure the state is clean, install OpenTofu, and replace&nbsp;<code>terraform</code>&nbsp;with&nbsp;<code>tofu</code>&nbsp;in your commands.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p># Make sure your state is clean before migrating&nbsp;terraform plan&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p># → "No changes. Your infrastructure matches the configuration."</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;# Install OpenTofu on macOS&nbsp;brew&nbsp;install&nbsp;opentofu&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p># Install OpenTofu on Linux&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>curl&nbsp;--proto&nbsp;'=https'&nbsp;--tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh&nbsp;|&nbsp;sh&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p># Resume your normal workflow&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>tofu init&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>tofu plan</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>tofu apply</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Existing&nbsp;<code>.tf</code>&nbsp;files do not need to be modified. The OpenTofu registry mirrors all providers available on the Terraform Registry — including the Clever Cloud provider.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">OpenTofu on Clever Cloud: nothing to change</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Our official provider (<code>CleverCloud/clevercloud</code>, currently version 1.10) works identically with both Terraform and OpenTofu. The provider source, HCL syntax, and all available resources are exactly the same. A minimal example to deploy a Node.js application with a PostgreSQL database:</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>terraform {</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;required_providers {</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;&nbsp;&nbsp;clevercloud = {</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;source&nbsp; = "CleverCloud/clevercloud"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;version = "~&gt; 1.10"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>provider "clevercloud" {organisation}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>resource "clevercloud_node" "app" {</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;name &nbsp; &nbsp; &nbsp; = "mon-app-node"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;region &nbsp; &nbsp; = "par"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;min_flavor = "XS"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;max_flavor = "M"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>resource "clevercloud_postgresql" "db" {</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;name &nbsp; = "ma-base-postgres"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;region = "par"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;plan &nbsp; = "dev"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>tofu init</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>tofu plan</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><code>tofu apply</code></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The OpenTofu + Clever Cloud combination is a fully sovereign IaC stack: open-source tooling under Linux Foundation / CNCF governance, infrastructure hosted in our own data centers in France, outside the reach of the US Cloud Act. It is the choice increasingly made by teams who want full control over their entire toolchain — not just their data.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em><em>The Clever Cloud provider is available on the&nbsp;<a href="https://registry.terraform.io/providers/CleverCloud/clevercloud/latest">Terraform Registry</a>&nbsp;and fully open source on&nbsp;<a href="https://github.com/CleverCloud/terraform-provider-clevercloud">GitHub</a>. </em><br></em></p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Q&amp;A</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p><em><br></em><strong>Is OpenTofu compatible with my existing Terraform configurations?</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>For the vast majority of use cases, yes. OpenTofu maintains syntactic and functional compatibility with Terraform. Existing&nbsp;<code>.tf</code>&nbsp;files, providers, and modules work without modification. Testing in a non-production environment before switching critical workloads is recommended.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em><br></em><strong>Is OpenTofu production-ready?</strong>&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Yes. The first stable version was released in January 2024. The project has since surpassed 10 million downloads and is used in production by organizations of all sizes, including Fortune 500 companies. Its integration into the CNCF in April 2025 reinforced its credibility for enterprise environments.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em><br></em><strong>What is the open-source alternative to Terraform?</strong>&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>OpenTofu is the primary open-source alternative to Terraform. Distributed under MPL 2.0, maintained by the Linux Foundation, and a CNCF member, it is the direct continuation of Terraform in its original open-source state.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em><br></em><strong>Is the Clever Cloud Terraform provider compatible with OpenTofu?</strong>&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Yes, with no configuration changes required. Our official provider (<code>CleverCloud/clevercloud</code>) works identically with both Terraform and OpenTofu.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em><br></em><strong>How does OpenTofu work?</strong>&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In the same way as Terraform: you describe the target state of your infrastructure in HCL, OpenTofu computes the necessary changes (<code>tofu plan</code>) and applies them (<code>tofu apply</code>). It manages dependencies between resources, the state, and the full lifecycle of each resource. This is also where IBM's acquisition of HashiCorp introduces additional uncertainty: projects built around Terraform, such as CDK for Terraform, could be deprioritized or discontinued based on IBM's commercial decisions. With OpenTofu, the roadmap is public, discussed through open RFCs, and does not depend on decisions made behind closed doors by a single actor.</p>
<!-- /wp:paragraph -->]]></description>
										<content:encoded><![CDATA[<p><img width="800" height="355" src="https://cdn.clever-cloud.com/uploads/2026/03/2026-03-18-clever-cloud-banniere-blog-paas-secteur-public-eng-2-2.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="2026 03 18 clever cloud banniere blog paas secteur public eng 2 2" decoding="async" loading="lazy" srcset="https://cdn.clever-cloud.com/uploads/2026/03/2026-03-18-clever-cloud-banniere-blog-paas-secteur-public-eng-2-2.png 800w, https://cdn.clever-cloud.com/uploads/2026/03/2026-03-18-clever-cloud-banniere-blog-paas-secteur-public-eng-2-2-300x133.png 300w, https://cdn.clever-cloud.com/uploads/2026/03/2026-03-18-clever-cloud-banniere-blog-paas-secteur-public-eng-2-2-768x341.png 768w" sizes="auto, (max-width: 800px) 100vw, 800px" /></p><!-- wp:paragraph -->
<p>BSL is not an open-source license as defined by the Open Source Initiative: it restricts commercial use deemed competitive with HashiCorp's offerings. Overnight, thousands of organizations that had built their infrastructure around Terraform found themselves in uncomfortable legal uncertainty.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The response was swift. Less than a month after the announcement, a manifesto calling for a return to an open-source license had gathered 33,000 GitHub stars and the support of nearly 140 companies. HashiCorp did not budge. The fork was published in September 2023, brought into the Linux Foundation, and renamed OpenTofu. Since January 2024, it has been in stable production. Since April 2025, it has been part of the Cloud Native Computing Foundation (CNCF).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Our <a href="https://www.clever.cloud/product/terraform/" type="link" id="https://www.clever.cloud/product/terraform/">Terraform</a> provider is fully compatible with OpenTofu. No changes to your configurations are needed to use it.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">What is OpenTofu?</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>OpenTofu is an open-source infrastructure as code (IaC) tool, forked from the last MPL 2.0-licensed version of Terraform (1.5.x), distributed under that same Mozilla Public License 2.0. It lets you describe, provision, and manage cloud infrastructure through declarative configuration files written in HashiCorp Configuration Language.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>It is developed under the governance of the Linux Foundation, with a Technical Steering Committee made up of representatives from multiple organizations — Gruntwork, Spacelift, Harness, Env0, Scalr — so that no single company can control its direction. That governance model is precisely what convinced many teams to make the switch: a guarantee that the tool will remain truly open source, regardless of how its contributors evolve commercially.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In practice, OpenTofu works with the same&nbsp;<code>.tf</code>&nbsp;files, the same providers, the same modules, and the same CLI workflow as Terraform —&nbsp;<code>init</code>,&nbsp;<code>plan</code>,&nbsp;<code>apply</code>,&nbsp;<code>destroy</code>. For the vast majority of teams, migrating comes down to replacing the binary.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">What's the actual difference with Terraform?</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The main difference is the license, and everything it implies in the long run.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Terraform is now distributed under the BSL, which restricts commercial uses that could be considered competitive with HashiCorp — a deliberately vague notion that HashiCorp (now a subsidiary of IBM) may interpret differently in the future. OpenTofu is distributed under MPL 2.0, a true open-source license with no commercial restrictions.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Beyond the license, the two projects have started to diverge technically since the fork. The most notable differences to date:&lt;ul&gt; &lt;li&gt;&lt;strong&gt;State encryption:&lt;/strong&gt; OpenTofu natively supports client-side encryption of state files — a feature long requested by the community that Terraform has still not implemented.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p></p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<ul></li> <li><strong>Governance:</strong> OpenTofu is steered by a multi-organization technical committee within the Linux Foundation. Terraform is controlled by IBM via HashiCorp, with a roadmap driven by commercial priorities.</li> <li><strong>Compatibility:</strong> OpenTofu maintains syntactic and functional compatibility with Terraform. Existing providers, modules, and configurations work without modification for the vast majority of use cases.</li> <li><strong>The <code>.tofu</code> extension:</strong> OpenTofu supports this extension alongside <code>.tf</code>, allowing module authors to provide OpenTofu-specific variants while remaining compatible with Terraform.</li> <li><strong>Terraform Actions:</strong> HashiCorp introduced "actions" in Terraform 1.9+, a feature that lets you trigger custom behaviors during resource lifecycle events. OpenTofu does not yet support them — this is the inherent trade-off of any fork: new proprietary Terraform features are not automatically available, and their implementation in OpenTofu depends on the technical committee's priorities.</li> </ul>
<!-- /wp:html -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Why teams are migrating to OpenTofu</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Three reasons come up consistently.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>Legal risk.</strong> The BSL prohibits using Terraform in products that could compete with HashiCorp's commercial offerings. For teams building internal platforms, cloud products, or <a href="https://www.clever.cloud/blog/company/2025/03/19/managed-services/">managed services</a>, this uncertainty is a real operational risk — especially since the definition of "competitive" belongs to HashiCorp and can evolve. On top of that, there is a less visible but equally significant reality: in the Terraform registry, open-source providers get no special visibility. Getting an "official" badge requires negotiating directly with HashiCorp — and paying for it. In the OpenTofu registry, all providers are on equal footing, regardless of the size or resources of their maintainers.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>Strategic autonomy over tooling.</strong>&nbsp;Your infrastructure as code is the foundation of everything else. Depending on a tool controlled by a US company subject to the Cloud Act means introducing an additional dependency that many organizations would rather avoid. OpenTofu, under Linux Foundation / CNCF governance, offers continuity independent of any commercial actor.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>Features Terraform doesn't ship.</strong>&nbsp;Native state file encryption is the most frequently cited example: requested by the community for years, implemented within months by OpenTofu. It is also a signal about how quickly community-driven governance can respond to real needs, compared to a roadmap driven by commercial interests.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Migrating from Terraform to OpenTofu</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>For the vast majority of teams, migration takes a matter of minutes. All you need to do is make sure the state is clean, install OpenTofu, and replace&nbsp;<code>terraform</code>&nbsp;with&nbsp;<code>tofu</code>&nbsp;in your commands.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p># Make sure your state is clean before migrating&nbsp;terraform plan&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p># → "No changes. Your infrastructure matches the configuration."</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;# Install OpenTofu on macOS&nbsp;brew&nbsp;install&nbsp;opentofu&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p># Install OpenTofu on Linux&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>curl&nbsp;--proto&nbsp;'=https'&nbsp;--tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh&nbsp;|&nbsp;sh&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p># Resume your normal workflow&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>tofu init&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>tofu plan</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>tofu apply</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Existing&nbsp;<code>.tf</code>&nbsp;files do not need to be modified. The OpenTofu registry mirrors all providers available on the Terraform Registry — including the Clever Cloud provider.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">OpenTofu on Clever Cloud: nothing to change</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Our official provider (<code>CleverCloud/clevercloud</code>, currently version 1.10) works identically with both Terraform and OpenTofu. The provider source, HCL syntax, and all available resources are exactly the same. A minimal example to deploy a Node.js application with a PostgreSQL database:</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>terraform {</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;required_providers {</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;&nbsp;&nbsp;clevercloud = {</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;source&nbsp; = "CleverCloud/clevercloud"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;version = "~&gt; 1.10"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>provider "clevercloud" {organisation}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>resource "clevercloud_node" "app" {</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;name &nbsp; &nbsp; &nbsp; = "mon-app-node"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;region &nbsp; &nbsp; = "par"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;min_flavor = "XS"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;max_flavor = "M"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>resource "clevercloud_postgresql" "db" {</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;name &nbsp; = "ma-base-postgres"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;region = "par"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>&nbsp;&nbsp;plan &nbsp; = "dev"</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>}</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>tofu init</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>tofu plan</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><code>tofu apply</code></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The OpenTofu + Clever Cloud combination is a fully sovereign IaC stack: open-source tooling under Linux Foundation / CNCF governance, infrastructure hosted in our own data centers in France, outside the reach of the US Cloud Act. It is the choice increasingly made by teams who want full control over their entire toolchain — not just their data.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em><em>The Clever Cloud provider is available on the&nbsp;<a href="https://registry.terraform.io/providers/CleverCloud/clevercloud/latest">Terraform Registry</a>&nbsp;and fully open source on&nbsp;<a href="https://github.com/CleverCloud/terraform-provider-clevercloud">GitHub</a>. </em><br></em></p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading">Q&amp;A</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p><em><br></em><strong>Is OpenTofu compatible with my existing Terraform configurations?</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>For the vast majority of use cases, yes. OpenTofu maintains syntactic and functional compatibility with Terraform. Existing&nbsp;<code>.tf</code>&nbsp;files, providers, and modules work without modification. Testing in a non-production environment before switching critical workloads is recommended.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em><br></em><strong>Is OpenTofu production-ready?</strong>&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Yes. The first stable version was released in January 2024. The project has since surpassed 10 million downloads and is used in production by organizations of all sizes, including Fortune 500 companies. Its integration into the CNCF in April 2025 reinforced its credibility for enterprise environments.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em><br></em><strong>What is the open-source alternative to Terraform?</strong>&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>OpenTofu is the primary open-source alternative to Terraform. Distributed under MPL 2.0, maintained by the Linux Foundation, and a CNCF member, it is the direct continuation of Terraform in its original open-source state.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em><br></em><strong>Is the Clever Cloud Terraform provider compatible with OpenTofu?</strong>&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Yes, with no configuration changes required. Our official provider (<code>CleverCloud/clevercloud</code>) works identically with both Terraform and OpenTofu.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em><br></em><strong>How does OpenTofu work?</strong>&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In the same way as Terraform: you describe the target state of your infrastructure in HCL, OpenTofu computes the necessary changes (<code>tofu plan</code>) and applies them (<code>tofu apply</code>). It manages dependencies between resources, the state, and the full lifecycle of each resource. This is also where IBM's acquisition of HashiCorp introduces additional uncertainty: projects built around Terraform, such as CDK for Terraform, could be deprioritized or discontinued based on IBM's commercial decisions. With OpenTofu, the roadmap is public, discussed through open RFCs, and does not depend on decisions made behind closed doors by a single actor.</p>
<!-- /wp:paragraph -->]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
