<?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>Rémi Collignon-Ducret, Author at Clever Cloud</title>
	<atom:link href="https://www.clever.cloud/blog/author/remi-collignon-ducretclever-cloud-com/feed/" rel="self" type="application/rss+xml" />
	<link></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>Rémi Collignon-Ducret, Author at Clever Cloud</title>
	<link></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>
	</channel>
</rss>
