<?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>IaC Archives - OVHcloud Blog</title>
	<atom:link href="https://blog.ovhcloud.com/tag/iac/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.ovhcloud.com/tag/iac/</link>
	<description>Innovation for Freedom</description>
	<lastBuildDate>Fri, 06 Feb 2026 15:22:57 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://blog.ovhcloud.com/wp-content/uploads/2019/07/cropped-cropped-nouveau-logo-ovh-rebranding-32x32.gif</url>
	<title>IaC Archives - OVHcloud Blog</title>
	<link>https://blog.ovhcloud.com/tag/iac/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Using OVHcloud S3-compatible Object Storage as Terraform Backend to store your Terraform/OpenTofu states</title>
		<link>https://blog.ovhcloud.com/using-ovhcloud-s3-compatible-object-storage-as-terraform-backend-to-store-your-terraform-opentofu-states/</link>
		
		<dc:creator><![CDATA[Aurélie Vache]]></dc:creator>
		<pubDate>Mon, 07 Jul 2025 06:27:02 +0000</pubDate>
				<category><![CDATA[OVHcloud Engineering]]></category>
		<category><![CDATA[Tranches de Tech & co]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[Object Storage]]></category>
		<category><![CDATA[Public Cloud]]></category>
		<guid isPermaLink="false">https://blog.ovhcloud.com/?p=29299</guid>

					<description><![CDATA[When working on Infrastructure as Code projects, with Terraform or OpenTofu, Terraform States files are created and modified locally in a terraform.tfstate file. It&#8217;s a common usage and practice but not convenient when working as a team. Do you know that you can configure Terraform to store data remotely on OVHcloud S3-compatible Object Storage? OVHcloud [&#8230;]<img src="//blog.ovhcloud.com/wp-content/plugins/matomo/app/matomo.php?idsite=1&amp;rec=1&amp;url=https%3A%2F%2Fblog.ovhcloud.com%2Fusing-ovhcloud-s3-compatible-object-storage-as-terraform-backend-to-store-your-terraform-opentofu-states%2F&amp;action_name=Using%20OVHcloud%20S3-compatible%20Object%20Storage%20as%20Terraform%20Backend%20to%20store%20your%20Terraform%2FOpenTofu%20states&amp;urlref=https%3A%2F%2Fblog.ovhcloud.com%2Ffeed%2F" style="border:0;width:0;height:0" width="0" height="0" alt="" />]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image aligncenter size-full is-resized"><img fetchpriority="high" decoding="async" width="1023" height="1022" src="https://blog.ovhcloud.com/wp-content/uploads/2025/07/ovh-object-storage-remote-backend-terraform-1.png" alt="" class="wp-image-29352" style="width:586px;height:auto" srcset="https://blog.ovhcloud.com/wp-content/uploads/2025/07/ovh-object-storage-remote-backend-terraform-1.png 1023w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/ovh-object-storage-remote-backend-terraform-1-300x300.png 300w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/ovh-object-storage-remote-backend-terraform-1-150x150.png 150w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/ovh-object-storage-remote-backend-terraform-1-768x767.png 768w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/ovh-object-storage-remote-backend-terraform-1-70x70.png 70w" sizes="(max-width: 1023px) 100vw, 1023px" /></figure>



<p class="wp-block-paragraph">When working on Infrastructure as Code projects, with Terraform or OpenTofu, Terraform States files are created and modified locally in a <code>terraform.tfstate</code> file. It&#8217;s a common usage and practice but not convenient when working as a team.</p>



<p class="wp-block-paragraph">Do you know that you can configure Terraform to store data remotely on OVHcloud S3-compatible Object Storage?</p>



<h3 class="wp-block-heading">OVHcloud Terraform/OpenTofu provider</h3>



<p class="wp-block-paragraph">To easily provision your infrastructures, OVHcloud provides a&nbsp;<a href="https://registry.terraform.io/providers/ovh/ovh/latest" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">Terraform provider</a>&nbsp;which is available in the <a href="https://registry.terraform.io/providers/ovh/ovh/latest/docs" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">official Terraform registry</a>.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="346" src="https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-1-1024x346.png" alt="" class="wp-image-29302" srcset="https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-1-1024x346.png 1024w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-1-300x102.png 300w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-1-768x260.png 768w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-1-1536x520.png 1536w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-1-2048x693.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">The provider is synchronized in the <a href="https://search.opentofu.org/provider/opentofu/ovh/latest" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer">OpenTofu registry</a> also:</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="370" src="https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-2-1024x370.png" alt="" class="wp-image-29322" srcset="https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-2-1024x370.png 1024w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-2-300x108.png 300w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-2-768x277.png 768w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-2-1536x555.png 1536w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-2-2048x740.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Read the <a href="https://blog.ovhcloud.com/infrastructure-as-code-iac-on-ovhcloud-part-1-terraform-opentofu/" data-wpel-link="internal">Infrastructure as Code (IaC) on OVHcloud – part 1: Terraform / OpenTofu</a> blog post to have more information about the provider and IaC on OVHcloud.</p>



<p class="wp-block-paragraph">Note that in the rest of the blog post we will be using <code>terraform</code> CLI and talking about Terraform, but you can also follow the blog post if you are using OpenTofu and <code>tofu</code> CLI instead 😉.</p>



<h3 class="wp-block-heading">How to</h3>



<p class="wp-block-paragraph">In this blog post we will handle two projects:</p>



<ul class="wp-block-list">
<li><code>object-storage-tf</code>: creation of an OVHcloud S3-compatible Object Sorage and an user and necessary policies</li>



<li><code>my-app</code>: usage of a <code>backend.tf</code> file that store and get TF states in your newly created S3-compatible bucket</li>
</ul>



<p class="wp-block-paragraph">Note that all the following source code are available on the <a href="https://github.com/ovh/public-cloud-examples/tree/main/use-cases/create-and-use-object-storage-as-tf-backend" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">OVHcloud Public Cloud examples</a> GitHub repository.</p>



<h4 class="wp-block-heading">Prerequisites:</h4>



<ul class="wp-block-list">
<li>Install the <a href="https://www.terraform.io/downloads.html" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer">Terraform</a> CLI</li>



<li>For non Linux users, install gettext (that included `envsubst` command)</li>
</ul>



<pre class="wp-block-code"><code class="">$ brew install gettext

$ brew link --force gettext</code></pre>



<ul class="wp-block-list">
<li><a href="https://docs.ovh.com/gb/en/customer/first-steps-with-ovh-api/" data-wpel-link="exclude">Get the credentials</a> from the OVHCloud Public Cloud project</li>
</ul>



<h3 class="wp-block-heading">Let&#8217;s create an Object Storage with Terraform</h3>



<p class="wp-block-paragraph">Create a new folder, named <code>object-storage-tf</code>, for example and go into it.</p>



<p class="wp-block-paragraph">Create a <code>provider.tf</code> file:</p>



<pre class="wp-block-code"><code class="">terraform {
  required_providers {
    ovh = {
      source  = "ovh/ovh"
    }
    
    random = {
      source  = "hashicorp/random"
      version = "3.6.3"
    }
  }
}

provider "ovh" {
}</code></pre>



<p class="wp-block-paragraph">The OVHcloud Terraform provider need the endpoint, the secret keys and the Public Cloud ID that needs to be retrieved from your environment variables:</p>



<ul class="wp-block-list">
<li><code>OVH_ENDPOINT</code></li>



<li><code>OVH_APPLICATION_KEY</code></li>



<li><code>OVH_APPLICATION_SECRET</code></li>



<li><code>OVH_CONSUMER_KEY</code></li>



<li><code>OVH_CLOUD_PROJECT_SERVICE</code></li>
</ul>



<p class="wp-block-paragraph">Then, create a <code>variables.tf.template</code> file with the following content:</p>



<pre class="wp-block-code"><code class="">variable "service_name" {
  default = "$OVH_CLOUD_PROJECT_SERVICE"
}


variable bucket_name {
  type        = string
}

variable bucket_region {
  type        = string
  default     = "GRA"
}</code></pre>



<p class="wp-block-paragraph">Replace the value of your <code>OVH_CLOUD_PROJECT_SERVICE</code> environment variable in the <code>variables.tf</code> file (in the service_name variable):</p>



<pre class="wp-block-code"><code class="">$ envsubst &lt; variables.tf.template &gt; variables.tf</code></pre>



<p class="wp-block-paragraph">Define the resources you want to create in a new file called <code>s3.tf</code>:</p>



<pre class="wp-block-code"><code class="">resource "random_string" "bucket_name_suffix" {
  length  = 16
  special = false
  lower   = true
  upper   = false
}

resource "ovh_cloud_project_storage" "s3_bucket" {
  service_name = var.service_name
  region_name = var.bucket_region
  name = "${var.bucket_name}-${random_string.bucket_name_suffix.result}" # the name must be unique within OVHcloud
}

resource "ovh_cloud_project_user" "s3_user" {
  description	= "${var.bucket_name}-${random_string.bucket_name_suffix.result}"
  role_name	= "objectstore_operator"
}

resource "ovh_cloud_project_user_s3_credential" "s3_user_cred" {
  user_id	= ovh_cloud_project_user.s3_user.id
}

resource "ovh_cloud_project_user_s3_policy" "s3_user_policy" {
  service_name = var.service_name
  user_id      = ovh_cloud_project_user.s3_user.id
  policy = jsonencode({
    "Statement": [{
      "Action": ["s3:*"],
      "Effect": "Allow",
      "Resource": ["arn:aws:s3:::${ovh_cloud_project_storage.s3_bucket.name}","arn:aws:s3:::${ovh_cloud_project_storage.s3_bucket.name}/*"],
      "Sid": "AdminContainer"
    }]
  })
}</code></pre>



<p class="wp-block-paragraph">In this file we defined that we want to create a S3-compatible Object Storage bucket and an user (with its credentials) that will have the rights (policies) to do actions on this bucket.</p>



<p class="wp-block-paragraph">Define the information that you want to get after the creation of the resources, in an <code>output.tf</code> file:</p>



<pre class="wp-block-code"><code class="">output "s3_bucket" {
  value = "${ovh_cloud_project_storage.s3_bucket.name}"
}

output "access_key_id" {
    value = ovh_cloud_project_user_s3_credential.s3_user_cred.access_key_id
}

output "secret_access_key" {
    value = ovh_cloud_project_user_s3_credential.s3_user_cred.secret_access_key
    sensitive = true
}</code></pre>



<p class="wp-block-paragraph">Now we need to initialise Terraform:</p>



<pre class="wp-block-code"><code class="">$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/random versions matching "3.6.3"...
- Reusing previous version of ovh/ovh from the dependency lock file
- Installing hashicorp/random v3.6.3...
- Installed hashicorp/random v3.6.3 (signed by HashiCorp)
- Using previously-installed ovh/ovh v2.5.0

Terraform has made some changes to the provider dependency selections recorded
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.</code></pre>



<p class="wp-block-paragraph">Generate the plan and apply it:</p>



<pre class="wp-block-code"><code class="">$ terraform apply -var bucket_name=my-bucket

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

Terraform will perform the following actions:

  # ovh_cloud_project_storage.s3_bucket will be created
  + resource "ovh_cloud_project_storage" "s3_bucket" {
      + created_at    = (known after apply)
      + encryption    = (known after apply)
      + limit         = (known after apply)
      + marker        = (known after apply)
      + name          = (known after apply)
      + objects       = (known after apply)
      + objects_count = (known after apply)
      + objects_size  = (known after apply)
      + owner_id      = (known after apply)
      + prefix        = (known after apply)
      + region        = (known after apply)
      + region_name   = "GRA"
      + replication   = (known after apply)
      + service_name  = "xxxxxxxxxxx"
      + versioning    = (known after apply)
      + virtual_host  = (known after apply)
    }

  # ovh_cloud_project_user.s3_user will be created
  + resource "ovh_cloud_project_user" "s3_user" {
      + creation_date = (known after apply)
      + description   = (known after apply)
      + id            = (known after apply)
      + openstack_rc  = (known after apply)
      + password      = (sensitive value)
      + role_name     = "objectstore_operator"
      + roles         = (known after apply)
      + service_name  = "xxxxxxxxxxx"
      + status        = (known after apply)
      + username      = (known after apply)
    }

  # ovh_cloud_project_user_s3_credential.s3_user_cred will be created
  + resource "ovh_cloud_project_user_s3_credential" "s3_user_cred" {
      + access_key_id     = (known after apply)
      + id                = (known after apply)
      + internal_user_id  = (known after apply)
      + secret_access_key = (sensitive value)
      + service_name      = "xxxxxxxxx"
      + user_id           = (known after apply)
    }

  # ovh_cloud_project_user_s3_policy.s3_user_policy will be created
  + resource "ovh_cloud_project_user_s3_policy" "s3_user_policy" {
      + id           = (known after apply)
      + policy       = (known after apply)
      + service_name = "xxxxxxxx"
      + user_id      = (known after apply)
    }

  # random_string.bucket_name_suffix will be created
  + resource "random_string" "bucket_name_suffix" {
      + id          = (known after apply)
      + length      = 16
      + lower       = true
      + min_lower   = 0
      + min_numeric = 0
      + min_special = 0
      + min_upper   = 0
      + number      = true
      + numeric     = true
      + result      = (known after apply)
      + special     = false
      + upper       = false
    }

Plan: 5 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + access_key_id     = (known after apply)
  + s3_bucket         = (known after apply)
  + secret_access_key = (sensitive value)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

random_string.bucket_name_suffix: Creating...
random_string.bucket_name_suffix: Creation complete after 0s [id=4qiyj7ywrt2sspfe]
ovh_cloud_project_user.s3_user: Creating...
ovh_cloud_project_storage.s3_bucket: Creating...
ovh_cloud_project_storage.s3_bucket: Creation complete after 1s [name=my-bucket-4qiyj7ywrt2sspfe]
ovh_cloud_project_user.s3_user: Still creating... [10s elapsed]
ovh_cloud_project_user.s3_user: Creation complete after 20s [id=535967]
ovh_cloud_project_user_s3_credential.s3_user_cred: Creating...
ovh_cloud_project_user_s3_policy.s3_user_policy: Creating...
ovh_cloud_project_user_s3_credential.s3_user_cred: Creation complete after 0s [id=5ab69860beb34575acb42c7ba8553884]
ovh_cloud_project_user_s3_policy.s3_user_policy: Creation complete after 0s [id=xxxxxxxxxxx/535967]

Apply complete! Resources: 5 added, 0 changed, 0 destroyed.

Outputs:

access_key_id = "5ab69860beb34575acb42c7ba8553884"
s3_bucket = "my-bucket-4qiyj7ywrt2sspfe"
secret_access_key = &lt;sensitive&gt;</code></pre>



<p class="wp-block-paragraph">🎉</p>



<p class="wp-block-paragraph"> Save the s3 user credentials in environment variables (mandatory for the following section):</p>



<pre class="wp-block-code"><code class="">
$ export AWS_ACCESS_KEY_ID=$(terraform output -raw access_key_id)
$ export AWS_SECRET_ACCESS_KEY=$(terraform output -raw secret_access_key)</code></pre>



<h3 class="wp-block-heading">Let&#8217;s configure an OVHcloud S3-compatible Object Storage as Terraform Backend</h3>



<p class="wp-block-paragraph">Create a new folder, named <code>my-app</code>, and go into it.</p>



<p class="wp-block-paragraph">Create a <code>backend.tf</code> file with the following content:</p>



<p class="wp-block-paragraph">⚠️ If you have a <code>terraform version</code> before 1.6.0:</p>



<pre class="wp-block-code"><code class="">terraform {
    backend "s3" {
      bucket = "&lt;my-bucket&gt;"
      key    = "my-app.tfstate"
      region = "gra"
      endpoint = "s3.gra.io.cloud.ovh.net"
      skip_credentials_validation = true
      skip_region_validation      = true
    }
}</code></pre>



<p class="wp-block-paragraph">⚠️ Since Terraform version 1.6.0:</p>



<pre class="wp-block-code"><code class="">terraform {
    backend "s3" {
      bucket = "&lt;my-bucket&gt;"
      key    = "my-app.tfstate"
      region = "gra"
      endpoints = {
        s3 = "https://s3.gra.io.cloud.ovh.net/"
      }
      skip_credentials_validation = true
      skip_region_validation      = true
      skip_requesting_account_id  = true
      skip_s3_checksum            = true
    }
}</code></pre>



<p class="wp-block-paragraph">You can replace <code>&lt;my-bucket&gt;</code> with the newly created bucket or with an existing bucket you created.</p>



<p class="wp-block-paragraph">Initialise Terraform:</p>



<pre class="wp-block-code"><code class="">$ terraform init

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Finding latest version of ovh/ovh...
- Installing ovh/ovh v2.5.0...
- Installed ovh/ovh v2.5.0 (signed by a HashiCorp partner, key ID F56D1A6CBDAAADA5)

...</code></pre>



<p class="wp-block-paragraph">As you can see, now, terraform is using &#8220;s3&#8221; backend! 💪</p>



<h3 class="wp-block-heading">Want to go further?</h3>



<p class="wp-block-paragraph">In this blog post, we created an S3-compatible Object Storage with basic configuration but be aware that <a href="https://registry.terraform.io/providers/ovh/ovh/latest/docs/resources/cloud_project_storage" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer">you can configure a S3-compatible bucket with encryption, versioning and more</a>:</p>



<figure class="wp-block-image aligncenter size-large is-resized"><img loading="lazy" decoding="async" width="1024" height="531" src="https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-3-1024x531.png" alt="" class="wp-image-29341" style="width:469px;height:auto" srcset="https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-3-1024x531.png 1024w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-3-300x156.png 300w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-3-768x398.png 768w, https://blog.ovhcloud.com/wp-content/uploads/2025/07/image-3.png 1326w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">💡 Terraform States are not encrypted at rest when stored by Terraform so we recommend to enable the encryption the OVHcloud S3-compatible Object Storage bucket 🙂.</p>
<img loading="lazy" decoding="async" src="//blog.ovhcloud.com/wp-content/plugins/matomo/app/matomo.php?idsite=1&amp;rec=1&amp;url=https%3A%2F%2Fblog.ovhcloud.com%2Fusing-ovhcloud-s3-compatible-object-storage-as-terraform-backend-to-store-your-terraform-opentofu-states%2F&amp;action_name=Using%20OVHcloud%20S3-compatible%20Object%20Storage%20as%20Terraform%20Backend%20to%20store%20your%20Terraform%2FOpenTofu%20states&amp;urlref=https%3A%2F%2Fblog.ovhcloud.com%2Ffeed%2F" style="border:0;width:0;height:0" width="0" height="0" alt="" />]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Infrastructure as Code (IaC) on OVHcloud – part 2: Pulumi</title>
		<link>https://blog.ovhcloud.com/infrastructure-as-code-iac-on-ovhcloud-part-2-pulumi/</link>
		
		<dc:creator><![CDATA[Aurélie Vache]]></dc:creator>
		<pubDate>Mon, 09 Dec 2024 08:23:58 +0000</pubDate>
				<category><![CDATA[OVHcloud Engineering]]></category>
		<category><![CDATA[Tranches de Tech & co]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[OVHcloud]]></category>
		<category><![CDATA[Public Cloud]]></category>
		<guid isPermaLink="false">https://blog.ovhcloud.com/?p=27726</guid>

					<description><![CDATA[In our previous article, Infrastructure as Code (IaC) on OVHcloud – part 1: Terraform / OpenTofu, we saw that deploying manually take time and it&#8217;s complicated. That why Infrastructure as Code (IaC) is powerful and allow you to automate infrastructure provisioning. But do you know that Terraform/OpenTofu is not the only tool that exists? In [&#8230;]<img src="//blog.ovhcloud.com/wp-content/plugins/matomo/app/matomo.php?idsite=1&amp;rec=1&amp;url=https%3A%2F%2Fblog.ovhcloud.com%2Finfrastructure-as-code-iac-on-ovhcloud-part-2-pulumi%2F&amp;action_name=Infrastructure%20as%20Code%20%28IaC%29%20on%20OVHcloud%20%E2%80%93%20part%202%3A%20Pulumi&amp;urlref=https%3A%2F%2Fblog.ovhcloud.com%2Ffeed%2F" style="border:0;width:0;height:0" width="0" height="0" alt="" />]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In our previous article, <a href="https://blog.ovhcloud.com/infrastructure-as-code-iac-on-ovhcloud-part-1-terraform-opentofu/" data-wpel-link="internal">Infrastructure as Code (IaC) on OVHcloud – part 1: Terraform / OpenTofu</a>, we saw that deploying manually take time and it&#8217;s complicated. That why <strong>Infrastructure</strong> <strong>as</strong> <strong>Code</strong> (<strong>IaC</strong>) is powerful and allow you to <strong>automate</strong> <strong>infrastructure</strong> <strong>provisioning</strong>. But do you know that Terraform/OpenTofu is not the only tool that exists?<br><br>In this blog post we will take a look at Pulumi.</p>



<h2 class="wp-block-heading">Pulumi</h2>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="446" height="146" src="https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-3.png" alt="" class="wp-image-27728" srcset="https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-3.png 446w, https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-3-300x98.png 300w" sizes="auto, (max-width: 446px) 100vw, 446px" /></figure>



<p class="wp-block-paragraph"><a href="https://www.pulumi.com/" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">Pulumi</a> is an Infrastructure as code (IaC) tool, open-sourced in 2018, that allows you to <strong>build your infrastructures with a programming language</strong>. Unlike Terraform which is more Ops oriented, Pulumi is more Developer oriented.<br>It supports a variety of programming languages: Go, Python, Node.js, Java, .Net&#8230;</p>



<p class="wp-block-paragraph"><strong>How Pulumi is working?</strong></p>



<p class="wp-block-paragraph">Concretely, in a Pulumi program, users defined the desired state and Pulumi create the desired resources for a stack (environment).</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="692" src="https://blog.ovhcloud.com/wp-content/uploads/2024/12/Gribouillis-2024-11-21-11.23.32.0472-1024x692.png" alt="" class="wp-image-27854" srcset="https://blog.ovhcloud.com/wp-content/uploads/2024/12/Gribouillis-2024-11-21-11.23.32.0472-1024x692.png 1024w, https://blog.ovhcloud.com/wp-content/uploads/2024/12/Gribouillis-2024-11-21-11.23.32.0472-300x203.png 300w, https://blog.ovhcloud.com/wp-content/uploads/2024/12/Gribouillis-2024-11-21-11.23.32.0472-768x519.png 768w, https://blog.ovhcloud.com/wp-content/uploads/2024/12/Gribouillis-2024-11-21-11.23.32.0472-1536x1039.png 1536w, https://blog.ovhcloud.com/wp-content/uploads/2024/12/Gribouillis-2024-11-21-11.23.32.0472.png 1841w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">To know more about <a href="https://www.pulumi.com/docs/iac/concepts/how-pulumi-works/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer">how Pulumi works</a>, you can go to the official documentation.</p>



<p class="wp-block-paragraph">To provision, update or delete infrastructures, Pulumi have an intuitive Command Line Interface (CLI). If you are familiar with Docker Compose CLI and Terraform CLI, you will adopt <a href="https://www.pulumi.com/docs/cli/" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">Pulumi CLI</a> too.</p>



<h2 class="wp-block-heading">OVHcloud Pulumi provider</h2>



<p class="wp-block-paragraph">To easily provision your infrastructures, OVHcloud provides a&nbsp;<a href="https://www.pulumi.com/registry/packages/ovh/" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">Pulumi provider</a>&nbsp;which is available in the&nbsp;<a href="https://registry.terraform.io/providers/ovh/ovh/latest/docs" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">official Pulumi registry</a>.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="565" src="https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-4-1024x565.png" alt="" class="wp-image-27731" srcset="https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-4-1024x565.png 1024w, https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-4-300x166.png 300w, https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-4-768x424.png 768w, https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-4-1536x848.png 1536w, https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-4-2048x1130.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">The OVHcloud Pulumi provider is a sync of the existing OVHcloud Terraform provider. So it means that when we published a new release of the Terraform&#8217;s one, in the coming hours or days we publish a new version to the Pulumi&#8217;s one.</p>



<p class="wp-block-paragraph">The Pulumi provider is synced to the Terraform provider so that means that with the OVHcloud Pulumi provider you can manage and deploy infrastructures for Public Cloud but also Bare Metal Cloud, Web Cloud and IAM. Depending on your needs, you can use a combinaison of several providers.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="738" src="https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-5-1024x738.png" alt="" class="wp-image-27732" srcset="https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-5-1024x738.png 1024w, https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-5-300x216.png 300w, https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-5-768x553.png 768w, https://blog.ovhcloud.com/wp-content/uploads/2024/11/image-5.png 1252w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">The provider is evolving every months, depending on the OVHcloud Terraform provider, so check the new releases changelog:&nbsp;<a href="https://github.com/ovh/terraform-provider-ovh/releases" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">https://github.com/ovh/pulumi-ovh/releases</a></p>



<p class="wp-block-paragraph">The&nbsp;<a href="https://github.com/ovh/pulumi-ovh/" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">OVHcloud Pulumi provider is open source and available on GitHub</a>, feel free to create issues or pull requests!</p>



<h2 class="wp-block-heading">OVHcloud Pulumi templates</h2>



<p class="wp-block-paragraph">To help the users writing Pulumi programs, several existing templates exists. It&#8217;s the case for the programming language supported by Pulumi but also for Cloud providers.</p>



<p class="wp-block-paragraph">To list the existing templates you can use, execute the following command:</p>



<pre class="wp-block-code"><code lang="" class="">$ pulumi new -l<code>
Available Templates:
aiven-go A minimal Aiven Go Pulumi program
aiven-python A minimal Aiven Python Pulumi program
...
kubernetes-ovh-csharp A C# program to deploy a Kubernetes cluster on OVHcloud
kubernetes-ovh-go A Go program to deploy a Kubernetes cluster on OVHcloud
kubernetes-ovh-java A Java program to deploy a Kubernetes cluster on OVHcloud
kubernetes-ovh-python A Python program to deploy a Kubernetes cluster on OVHcloud
kubernetes-ovh-typescript A TypeScript program to deploy a Kubernetes cluster on OVHcloud
...
ovh-csharp A minimal OVHcloud C# Pulumi program
ovh-go A minimal OVHcloud Go Pulumi program
ovh-java A minimal OVHcloud Java Pulumi program
ovh-python A minimal OVHcloud Python Pulumi program
ovh-typescript A minimal OVHcloud TypeScript Pulumi program
...</code></code></pre>



<p class="wp-block-paragraph">As you can see, at OVHcloud we created several templates to help you to start your Pulumi journey in an easy way.</p>



<p class="wp-block-paragraph">You can find all the templates listed with <code>pulumi new -l</code> command in the <a href="https://github.com/pulumi/templates/ " data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer">https://github.com/pulumi/templates/ </a>GitHub repository.</p>



<h2 class="wp-block-heading">Deploy a Managed Private Registry with Pulumi</h2>



<p class="wp-block-paragraph">To show you that you can deploy your infrastructures on OVHcloud with Pulumi, we will deploy a concrete example. In this blog post we will deploy a&nbsp;<a href="https://www.ovhcloud.com/en/public-cloud/kubernetes/" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">Managed Private Registry</a>&nbsp;and configure it with all the minimum requirements:</p>



<ul class="wp-block-list">
<li>a user</li>



<li>a (public) project</li>
</ul>



<h3 class="wp-block-heading">Prerequisites</h3>



<p class="wp-block-paragraph">You should have installed Pulumi CLI, on your machine. You can install it by following&nbsp;<a href="https://www.pulumi.com/docs/iac/download-install/" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">detailed installation instructions</a>.</p>



<p class="wp-block-paragraph"><a href="https://docs.ovh.com/gb/en/customer/first-steps-with-ovh-api/" data-wpel-link="exclude">Get the credentials</a>&nbsp;from the OVHCloud Public Cloud project:</p>



<ul class="wp-block-list">
<li><code>application_key</code></li>



<li><code>application_secret</code></li>



<li><code>consumer_key</code></li>
</ul>



<p class="wp-block-paragraph">Get the&nbsp;<code>service_name</code>&nbsp;(Public Cloud project ID)</p>



<p class="wp-block-paragraph">Export the OVHcloud credentials in environment variables:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ export OVH_ENDPOINT="ovh-eu" # or ovh-ca or ovh-us
$ export <span style="background-color: initial; font-family: inherit; font-size: inherit; font-weight: inherit;">OVH_APPLICATION_KEY="xxx"</span>
$ export OVH_APPLICATION_SECRET="xxx"
$ export OVH_CONSUMER_KEY="xxx"</code></pre>



<h3 class="wp-block-heading">Let’s deploy our Private Registry!</h3>



<p class="wp-block-paragraph">Note that all the following source code are available on the&nbsp;<a href="https://github.com/ovh/public-cloud-examples" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external">OVHcloud Public Cloud examples</a>&nbsp;GitHub repository.</p>



<p class="wp-block-paragraph">In this blog post we will create an OVHcloud Managed Private Registry in Go/Golang programming language but if you want you can do the same things in Python, TypeScript, Java or C#.</p>



<p class="wp-block-paragraph">Create a new folder, named <code>ovh-registry-go</code> which represents our project and go into it:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ mkdir ovh-registry-go
$ cd ovh-registry-go</code></pre>



<p class="wp-block-paragraph">Log-in to Pulumi and store the state locally:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ pulumi login --local

Logged in to xxxxxxxx as avache (file://~)</code></pre>



<p class="wp-block-paragraph">Initialize our project (with an existing template):</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ <span style="background-color: initial; font-family: inherit; font-size: inherit; font-weight: inherit;">pulumi new ovh-go</span>
<code>
This command will walk you through creating a new Pulumi project.

Enter a value or leave blank to accept the (default), and press .
Press ^C at any time to quit.

Project name (ovh-registry-go):
Project description (A minimal OVHcloud Go Pulumi program):
Created project 'ovh-registry-go'

Stack name (dev):
Created stack 'dev'
Enter your passphrase to protect config/secrets:
Re-enter your passphrase to confirm:

The OVHcloud region to deploy into (ovhRegion) (GRA):
The OVHcloud Public Cloud Project to deploy into (ovhServiceName): xxxxxxxxxxxxx
Name of the plan (planName) (SMALL):
Name of the private registry (registryName) (my-registry):
Email of the user (registryUserEmail) (myuser@ovh.com):
Login of the user (registryUserLogin) (myuser):
Name of the user (registryUserName) (user):
Saved config

Installing dependencies…
Finished installing dependencies
Your new project is ready to go! ✨</code>

<code>To perform an initial deployment, run pulumi up</code></code></pre>



<p class="wp-block-paragraph">This command displays a prompt that ask you several questions to initialize the project.<br>You can press the &#8220;Enter&#8221; button for every questions except the one asking you the ovhServiceName :).</p>



<p class="wp-block-paragraph">The command creates a&nbsp;<code>dev</code>&nbsp;stack and the code organization of your project:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ tree
.
├── Pulumi.dev.yaml
├── Pulumi.yaml
├── go.mod
├── go.sum
└── main.go</code></pre>



<p class="wp-block-paragraph">Let&#8217;s take a look at the generated <code>main.go</code> file:</p>



<pre class="wp-block-code"><code lang="go" class="language-go">package main

import (
	"github.com/ovh/pulumi-ovh/sdk/go/ovh/cloudproject"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Get some configuration values or use defaults
		cfg := config.New(ctx, "")
		ovhServiceName := cfg.Require("ovhServiceName")
		ovhRegion, err := cfg.Try("ovhRegion")
		if err != nil {
			ovhRegion = "GRA"
		}

		planName, err := cfg.Try("planName")
		if err != nil {
			planName = "SMALL"
		}

		registryName, err := cfg.Try("registryName")
		if err != nil {
			registryName = "my-registry"
		}

		registryUserName, err := cfg.Try("registryUserName")
		if err != nil {
			registryUserName = "user"
		}

		registryUserEmail, err := cfg.Try("registryUserEmail")
		if err != nil {
			registryUserEmail = "myuser@ovh.com"
		}

		registryUserLogin, err := cfg.Try("registryUserLogin")
		if err != nil {
			registryUserLogin = "myuser"
		}

		// Initiate the configuration of the registry
		regcap, err := cloudproject.GetCapabilitiesContainerFilter(ctx, &amp;cloudproject.GetCapabilitiesContainerFilterArgs{
			ServiceName: ovhServiceName,
			PlanName:    planName,
			Region:      ovhRegion,
		}, nil)
		if err != nil {
			return err
		}

		// Deploy a new Managed private registry
		myRegistry, err := cloudproject.NewContainerRegistry(ctx, registryName, &amp;cloudproject.ContainerRegistryArgs{
			ServiceName: pulumi.String(regcap.ServiceName),
			PlanId:      pulumi.String(regcap.Id),
			Region:      pulumi.String(regcap.Region),
		})
		if err != nil {
			return err
		}

		// Create a Private Registry User
		myRegistryUser, err := cloudproject.NewContainerRegistryUser(ctx, registryUserName, &amp;cloudproject.ContainerRegistryUserArgs{
			ServiceName: pulumi.String(regcap.ServiceName),
			RegistryId:  myRegistry.ID(),
			Email:       pulumi.String(registryUserEmail),
			Login:       pulumi.String(registryUserLogin),
		})
		if err != nil {
			return err
		}

		// Add as an output registry information
		ctx.Export("registryURL", myRegistry.Url)
		ctx.Export("registryUser", myRegistryUser.User)
		ctx.Export("registryPassword", myRegistryUser.Password)

		return nil
	})
}</code></pre>



<p class="wp-block-paragraph">The code above:<br>&#8211; initiate several variables (serviceName, region, registry&#8217;s information and registryUser&#8217;s informations)<br>&#8211; create an OVHcloud Managed Private Registry (based on Harbor)<br>&#8211; create a registry user<br>&#8211; display the useful informations to connect to the newly created private registry</p>



<h3 class="wp-block-heading">Let&#8217;s add a public project</h3>



<p class="wp-block-paragraph">Before applying our Go program, we will add some lines of code to our base template in order to create a public project in the private registry using the Harbor.</p>



<p class="wp-block-paragraph">Open and edit the <code>main.go</code> file.</p>



<p class="wp-block-paragraph">In order to use the Harbor Pulumi provider, add the new dependency in the &#8220;import&#8221; block:</p>



<pre class="wp-block-code"><code lang="go" class="language-go">"github.com/pulumiverse/pulumi-harbor/sdk/v3/go/harbor"</code></pre>



<p class="wp-block-paragraph">And before the <code>return nil</code> line, add the following block code:</p>



<pre class="wp-block-code"><code lang="go" class="language-go">	//Use the created regitry to initiate the Harbor provider
        harborProvider, err := harbor.NewProvider(ctx, "harbor", &amp;harbor.ProviderArgs{
            Username: myRegistryUser.User,
            Password: myRegistryUser.Password,
            Url:      myRegistry.Url,
        }, pulumi.DependsOn([]pulumi.Resource{myRegistry, myRegistryUser}))
        if err != nil {
            return err
        }

        // Create a public project in your harbor registry
        project, err := harbor.NewProject(ctx, "project", &amp;harbor.ProjectArgs{
            Name:   pulumi.String("my-new-project"),
	    Public: pulumi.Bool(true),
        }, pulumi.Provider(harborProvider))
        if err != nil {
            return err
        }

        ctx.Export("project", project.Name)</code></pre>



<p class="wp-block-paragraph">Run the&nbsp;<code>go mod tidy</code>&nbsp;command to ask Go to download and install the necessary Go providers and dependencies.</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ go mod tidy

go: finding module for package github.com/pulumiverse/pulumi-harbor/sdk/v3/go/harbor
go: downloading github.com/pulumiverse/pulumi-harbor v3.10.15+incompatible
go: downloading github.com/pulumiverse/pulumi-harbor/sdk/v3 v3.10.15
go: found github.com/pulumiverse/pulumi-harbor/sdk/v3/go/harbor in github.com/pulumiverse/pulumi-harbor/sdk/v3 v3.10.15</code></pre>



<p class="wp-block-paragraph">Preview/dry-run before applying the changes:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ pulumi preview

Previewing update (dev):
     Type                                       Name                 Plan
 +   pulumi:pulumi:Stack                        ovh-registry-go-dev  create
 +   ├─ ovh:CloudProject:ContainerRegistry      my-registry          create
 +   ├─ ovh:CloudProject:ContainerRegistryUser  user                 create
 +   ├─ pulumi:providers:harbor                 harbor               create
 +   └─ harbor:index:Project                    project              create

Outputs:
    project         : output&lt;string&gt;
    registryPassword: output&lt;string&gt;
    registryURL     : output&lt;string&gt;
    registryUser    : output&lt;string&gt;

Resources:
    + 5 to create</code></pre>



<p class="wp-block-paragraph">Now let&#8217;s deploy our OVHcloud Managed Private Registry with Pulumi:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ pulumi up

Previewing update (dev):
     Type                                       Name                 Plan
 +   pulumi:pulumi:Stack                        ovh-registry-go-dev  create
 +   ├─ ovh:CloudProject:ContainerRegistry      my-registry          create
 +   ├─ ovh:CloudProject:ContainerRegistryUser  user                 create
 +   ├─ pulumi:providers:harbor                 harbor               create
 +   └─ harbor:index:Project                    project              create

Outputs:
    project         : output&lt;string&gt;
    registryPassword: output&lt;string&gt;
    registryURL     : output&lt;string&gt;
    registryUser    : output&lt;string&gt;

Resources:
    + 5 to create

Do you want to perform this update? yes
Updating (dev):
     Type                                       Name                 Status
 +   pulumi:pulumi:Stack                        ovh-registry-go-dev  created (164s)
 +   ├─ ovh:CloudProject:ContainerRegistry      my-registry          created (162s)
 +   ├─ ovh:CloudProject:ContainerRegistryUser  user                 created (0.53s)
 +   ├─ pulumi:providers:harbor                 harbor               created (0.15s)
 +   └─ harbor:index:Project                    project              created (0.41s)

Outputs:
    project         : "my-new-project"
    registryPassword: [secret]
    registryURL     : "https://xxxxxxxx.xx.gra9.container-registry.ovh.net"
    registryUser    : "myuser"

Resources:
    + 5 created

Duration: 2m49s</code></pre>



<p class="wp-block-paragraph">Your private registry has now been deployed.</p>



<h3 class="wp-block-heading" id="connect-to-the-registry">Access and connect to the Private Registry (Harbor UI)</h3>



<p class="wp-block-paragraph">Your registry have been created with a user, so you have all the information to connect to it.</p>



<p class="wp-block-paragraph">In order to do this, retrieve the registry URL, login and password from the&nbsp;<code>dev</code>&nbsp;Pulumi stack:</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">$ pulumi stack output registryURL -s dev
$ pulumi stack output registryUser -s dev
$ pulumi stack output registryPassword --show-secrets -s dev</code></pre>



<p class="wp-block-paragraph">Now you can use these informations to connect to the Managed Private Registry.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">As you have seen in this blog post, thanks to the OVHcloud Pulumi provider  you can automatise the deployment of your infrastructures. The provider is evolving so feel free to take a look to the new releases changelog. You can also quickly deploy several resources like an OVHcloud Managed Kubernetes cluster and a Private Registry thanks to the Pulumi official templates.</p>



<p class="wp-block-paragraph">We used the Go programming language but you can do the same things in TypeScript, Python, Java and C#.</p>



<p class="wp-block-paragraph">In the next blog post of this series, we will see the usage on OVHcloud of another IaC tool (yes!).</p>
<img loading="lazy" decoding="async" src="//blog.ovhcloud.com/wp-content/plugins/matomo/app/matomo.php?idsite=1&amp;rec=1&amp;url=https%3A%2F%2Fblog.ovhcloud.com%2Finfrastructure-as-code-iac-on-ovhcloud-part-2-pulumi%2F&amp;action_name=Infrastructure%20as%20Code%20%28IaC%29%20on%20OVHcloud%20%E2%80%93%20part%202%3A%20Pulumi&amp;urlref=https%3A%2F%2Fblog.ovhcloud.com%2Ffeed%2F" style="border:0;width:0;height:0" width="0" height="0" alt="" />]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
