Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 90 additions & 9 deletions content/gads/terraform/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,23 @@ aliases:

heading: "Terraform Alternative"
subheading: |
Use the programming languages your team already knows. Pulumi is free, open source,
and works with your existing Terraform. Migrate at your pace.
No 500-resource cap. Real programming languages. Free migration tools.
Keep your existing Terraform running while you migrate at your pace.

dki_template: "{keyword} Alternative"

hero_cta:
primary:
label: "Sign up free"
href: "https://app.pulumi.com/signup"
secondary:
label: "Talk to an expert"
href: "/contact/?form=sales"
microcopy: "Free forever • Open source • No credit card required"

cta_microcopy: "Free forever • Open source • No credit card required"

social_proof_position: top

customer_quote:
text: "What used to take a week and a half now, with Pulumi, took under a day."
Expand All @@ -19,17 +34,83 @@ customer_quote:
link: /case-studies/snowflake

overview:
title: Real Languages. No Resource Caps.<br/>Migrate at Your Pace.
description: |
Looking for <span id="dki-placeholder" style="font-weight: bold;">a Terraform alternative</span>? HCP Terraform's free tier caps you at 500 managed resources. Pulumi Cloud has no resource caps. Write infrastructure in Python, TypeScript, Go, or C# with full IDE support, testing, and 170+ cloud providers. Free tf2pulumi migration tool included.
title: Real languages. No resource caps. Migrate at your pace.
bullets:
- title: "No 500-resource cap."
description: "HCP Terraform's free tier limits you to 500 managed resources. Pulumi Cloud's free tier has no cap."
- title: "Real programming languages."
description: "Write infrastructure in Python, TypeScript, Go, C#, or Java — with full IDE support, testing, and 170+ cloud providers."
- title: "Migrate on your timeline."
description: "Import existing state with `pulumi import` and keep current infrastructure running. No forced deadlines, no resource caps. <span id=\"dki-placeholder\" style=\"display:none;\"></span>"

comparison_table:
title: "Pulumi vs. Terraform at a glance"
description: "What you get when you switch — without giving up the Terraform ecosystem you already know."
rows:
- feature: "Free tier resource cap"
pulumi: "**No cap**"
competitor: "500 managed resources"
- feature: "Languages"
pulumi: "Python, TypeScript, Go, C#, Java, YAML"
competitor: "HCL only"
- feature: "IDE support (autocomplete, refactoring)"
pulumi: "Full, native"
competitor: "Limited (HCL-only tooling)"
- feature: "Unit and integration testing"
pulumi: "Standard test frameworks (Jest, pytest, Go test, xUnit)"
competitor: "Limited"
- feature: "Reusable components"
pulumi: "Real classes and functions across languages"
competitor: "Modules (HCL)"
- feature: "Existing Terraform state"
pulumi: "Import directly with `pulumi import`"
competitor: "—"
- feature: "Cloud providers"
pulumi: "170+ providers, same-day updates"
competitor: "Comparable coverage"
- feature: "License"
pulumi: "Apache 2.0 (open source)"
competitor: "BSL (source-available)"

key_features_above:
items:
- title: "Switch from HCL to real languages"
sub_title: "Pulumi Infrastructure as Code Engine"
description:
Stop writing HCL. Author infrastructure as code using programming languages you already know, including Python, TypeScript, Go, C#, Java, and YAML. Use the free tf2pulumi converter to migrate your existing Terraform files. Deploy to 170+ providers.
image: "/images/product/pulumi-iac-code.png"
Stop writing HCL. Author infrastructure in Python, TypeScript, Go, C#, Java, or YAML. Use the free tf2pulumi converter to migrate your existing `.tf` files. Deploy to 170+ providers.
before_after:
before:
title: "Before — Terraform (HCL)"
language: "hcl"
code: |
resource "aws_s3_bucket" "site" {
bucket = "my-static-site"
}

resource "aws_s3_bucket_website_configuration" "site" {
bucket = aws_s3_bucket.site.id

index_document {
suffix = "index.html"
}
}

output "bucket_name" {
value = aws_s3_bucket.site.id
}
after:
title: "After — Pulumi (TypeScript)"
language: "typescript"
code: |
import * as aws from "@pulumi/aws";

const site = new aws.s3.Bucket("site", {
bucket: "my-static-site",
website: { indexDocument: "index.html" },
});

export const bucketName = site.id;
caption: "Same resources. Real types, IDE autocomplete, and standard test frameworks."
features:
- title: Code faster
description: |
Expand All @@ -47,14 +128,14 @@ key_features_above:
Generate Pulumi code from natural language or convert existing Terraform with Pulumi AI and Neo.
icon: lightning
color: yellow

key_features:
title: Key features
items:
- title: "Migrate from Terraform in minutes"
sub_title: "Free Migration Tools"
description: |
Use tf2pulumi to convert your existing Terraform HCL to Python, TypeScript, Go, or C#. Import existing state with pulumi import. Keep your current infrastructure running while you migrate at your own pace. No forced deadlines. No resource caps.
Use tf2pulumi to convert your existing Terraform HCL to Python, TypeScript, Go, or C#. Import existing state with `pulumi import`. Keep your current infrastructure running while you migrate at your own pace. No forced deadlines. No resource caps.
image: "/images/product/pulumi-iac-code.png"
features:
- title: Convert HCL to real code
Expand Down
174 changes: 140 additions & 34 deletions layouts/gads/gads-template.html
Original file line number Diff line number Diff line change
@@ -1,51 +1,67 @@
{{ define "hero" }}
{{ partial "gads-hero" (dict "title" .Params.heading) }}
{{ partial "gads-hero" (dict "title" .Params.heading "subtitle" .Params.subheading "dki_template" .Params.dki_template) }}
{{ with .Params.hero_cta }}
<div class="container mx-auto text-center pt-6 pb-2">
{{ with .primary }}
<a href="{{ .href }}" class="btn-primary block sm:inline mb-2 sm:mb-0 sm:mr-3" data-role="cta-get-started">{{ .label }}</a>
{{ end }}
{{ with .secondary }}
<a href="{{ .href }}" class="btn-secondary block sm:inline" data-role="cta-secondary">{{ .label }}</a>
{{ end }}
{{ with .microcopy }}
<p class="text-sm text-gray-500 mt-3">{{ . | markdownify }}</p>
{{ end }}
</div>
{{ end }}
{{ end }}

{{ define "main" }}
{{ $cta_href := site.Params.cta.primary.href }}
{{ $cta_label := .Params.cta_label | default site.Params.cta.primary.label }}
{{ $cta_href := .Params.cta_href | default site.Params.cta.primary.href }}
{{ $cta_microcopy := .Params.cta_microcopy }}

{{/* Social proof can opt to render directly under the hero by setting `social_proof_position: top` */}}
{{ $proofTop := eq .Params.social_proof_position "top" }}

{{ if $proofTop }}
{{ partial "gads-social-proof.html" . }}
{{ end }}

<section id="overview" class="my-8 relative">
<div class="container mx-auto text-center">
<div class="w-full p-4">
<h2>{{ .Params.overview.title | markdownify }}</h2>
</div>
</div>
<div class="container mx-auto">
<p id="dynamic-description" class="mx-auto text-center text-gray-600 w-full px-4 lg:w-6/12">
{{ .Params.overview.description | markdownify }}
</p>
</div>
{{ with .Params.overview.bullets }}
<div class="container mx-auto">
<ul class="mx-auto text-left text-gray-700 w-full px-4 lg:w-6/12 space-y-3 list-disc list-inside">
{{ range . }}
<li class="text-lg"><strong>{{ .title }}</strong> {{ .description | markdownify }}</li>
{{ end }}
</ul>
</div>
{{ else }}
<div class="container mx-auto">
<p id="dynamic-description" class="mx-auto text-center text-gray-600 w-full px-4 lg:w-6/12">
{{ .Params.overview.description | markdownify }}
</p>
</div>
{{ end }}
<div class="container mx-auto text-center pt-8 w-9/12">
{{ if in .RelPermalink "/gads/neo" }}
<a href="https://app.pulumi.com/signup" class="btn-primary">Try Neo for FREE</a>
{{ else }}
{{ partial "cta-get-started" (dict "class" "btn-primary") }}
<a href="{{ $cta_href }}" class="btn-primary" data-role="cta-get-started">{{ $cta_label }}</a>
{{ end }}
</div>
</section>

{{ with .Params.case_studies.items }}
<section class="container mx-auto text-center px-3 mt-8 mb-4">
<h6 class="text-center text-gray-500 text-sm mb-2">Trusted by innovative companies</h6>
<div class="flex flex-wrap justify-center items-center py-4">
{{ range . }}
<div class="w-28 h-12 mx-6 my-2 flex items-center justify-center">
{{ partial "customer-logo.html" (dict "logo" .logo) }}
</div>
{{ with $cta_microcopy }}
<p class="text-sm text-gray-500 mt-3">{{ . | markdownify }}</p>
{{ end }}
</div>
</section>
{{ end }}

{{ with .Params.customer_quote }}
<section class="w-full md:w-2/3 lg:w-1/2 mx-auto mt-4 mb-8 px-6">
<div class="container text-center mx-auto">
<p class="text-lg text-gray-700 italic">&ldquo;{{ .text }}&rdquo;</p>
<p class="text-base text-gray-600 font-semibold mt-2">{{ .author }}</p>
<div class="h-16 mt-4">{{ partial "customer-logo.html" (dict "logo" .logo) }}</div>
</div>
</section>
{{ if not $proofTop }}
{{ partial "gads-social-proof.html" . }}
{{ end }}

<section id="key-features-above">
Expand All @@ -54,6 +70,24 @@ <h6 class="text-center text-gray-500 text-sm mb-2">Trusted by innovative compani
{{ if eq $index 0 }}
<div class="container mx-auto mt-8 text-center w-11/12 sm:w-9/12">
<h3>{{ $feature.title }}</h3>
{{ if $feature.before_after }}
<div class="max-w-5xl mx-auto mt-8 mb-4 text-left">
<p class="text-lg text-gray-700 px-4 mb-6">{{ $feature.description | markdownify }}</p>
<div class="flex flex-col lg:flex-row gap-4">
<div class="w-full lg:w-1/2 card bg-white shadow-md p-4">
<p class="text-xs uppercase tracking-wide text-gray-500 mb-2">{{ $feature.before_after.before.title | default "Before" }}</p>
{{ partial "code" (dict "lang" $feature.before_after.before.language "code" $feature.before_after.before.code "mode" "light") }}
</div>
<div class="w-full lg:w-1/2 card bg-white shadow-md p-4">
<p class="text-xs uppercase tracking-wide text-gray-500 mb-2">{{ $feature.before_after.after.title | default "After" }}</p>
{{ partial "code" (dict "lang" $feature.before_after.after.language "code" $feature.before_after.after.code "mode" "light") }}
</div>
</div>
{{ with $feature.before_after.caption }}
<p class="text-sm text-gray-500 text-center mt-4">{{ . | markdownify }}</p>
{{ end }}
</div>
{{ else }}
<div class="flex flex-col lg:flex-row">
<div class="w-full my-auto lg:w-1/2 lg:mr-32 z-10 p-8 text-left">
<h4>{{ $feature.sub_title}}</h4>
Expand Down Expand Up @@ -89,6 +123,7 @@ <h4>{{ $feature.sub_title}}</h4>
</div>
</div>
</div>
{{ end }}
</div>
{{ else }}
<div class="container mx-auto mt-16 text-center w-11/12 sm:w-9/12">
Expand Down Expand Up @@ -123,12 +158,52 @@ <h5>{{ $item.title }}</h5>
{{ end }}
</div>
<div class="pt-8 text-center">
<a href="{{ $cta_href }}" class="btn-secondary block sm:inline" data-role="cta-get-started">{{ site.Params.cta.primary.label }}</a>
<a href="{{ $cta_href }}" class="btn-secondary block sm:inline" data-role="cta-get-started">{{ $cta_label }}</a>
{{ with $cta_microcopy }}
<p class="text-sm text-gray-500 mt-3">{{ . | markdownify }}</p>
{{ end }}
</div>
</div>
{{ end }}
</section>

{{ with .Params.comparison_table }}
<section id="comparison" class="mb-16 pt-8 bg-gray-50">
<div class="container mx-auto py-12 w-11/12 sm:w-9/12">
<h3 class="text-center">{{ .title | default "How Pulumi compares" }}</h3>
{{ with .description }}
<p class="text-center text-gray-600 max-w-2xl mx-auto mb-8">{{ . | markdownify }}</p>
{{ end }}
<div class="overflow-x-auto">
<table class="w-full max-w-4xl mx-auto bg-white rounded-lg shadow-sm">
<thead>
<tr class="border-b border-gray-200">
<th class="text-left p-4 text-gray-700">{{ .feature_label | default "Feature" }}</th>
<th class="text-center p-4 text-violet-700">{{ .pulumi_label | default "Pulumi" }}</th>
<th class="text-center p-4 text-gray-500">{{ .competitor_label | default "Competitor" }}</th>
</tr>
</thead>
<tbody>
{{ range .rows }}
<tr class="border-b border-gray-100">
<td class="p-4 font-medium">{{ .feature }}</td>
<td class="p-4 text-center">{{ .pulumi | markdownify }}</td>
<td class="p-4 text-center text-gray-600">{{ .competitor | markdownify }}</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
<div class="text-center pt-8">
<a href="{{ $cta_href }}" class="btn-primary" data-role="cta-get-started">{{ $cta_label }}</a>
{{ with $cta_microcopy }}
<p class="text-sm text-gray-500 mt-3">{{ . | markdownify }}</p>
{{ end }}
</div>
</div>
</section>
{{ end }}

<section id="key-features" class="mb-16 pt-8">
{{ range $item := .Params.key_features.items }}
<div class="container mx-auto text-center pt-20 w-11/12 sm:w-9/12">
Expand Down Expand Up @@ -176,7 +251,10 @@ <h5>{{ $feature.title }}</h5>
</div>
</div>
<div class="pt-8">
<a href="{{ $cta_href }}" class="btn-secondary block sm:inline" data-role="cta-get-started">{{ site.Params.cta.primary.label }}</a>
<a href="{{ $cta_href }}" class="btn-secondary block sm:inline" data-role="cta-get-started">{{ $cta_label }}</a>
{{ with $cta_microcopy }}
<p class="text-sm text-gray-500 mt-3">{{ . | markdownify }}</p>
{{ end }}
</div>
</div>
{{ end }}
Expand Down Expand Up @@ -259,7 +337,10 @@ <h5>{{ $feature.title }}</h5>
</div>
</div>
<div class="pt-8">
<a href="{{ $cta_href }}" class="btn-secondary block sm:inline" data-role="cta-get-started">{{ site.Params.cta.primary.label }}</a>
<a href="{{ $cta_href }}" class="btn-secondary block sm:inline" data-role="cta-get-started">{{ $cta_label }}</a>
{{ with $cta_microcopy }}
<p class="text-sm text-gray-500 mt-3">{{ . | markdownify }}</p>
{{ end }}
</div>
</div>
{{ end }}
Expand All @@ -286,6 +367,18 @@ <h5>{{ .name }}</h5>
</div>
</section>

{{/*
Google Ads Dynamic Keyword Insertion (DKI).

On wake-up, the script below reads `utm_term` from the URL and rewrites
any element with id="dki-placeholder" with the formatted keyword. Pages
opt in by embedding `<span id="dki-placeholder"></span>` somewhere in
their frontmatter (commonly inside an overview bullet or paragraph).
Without that span the script silently no-ops on the body — the script
also updates the H1 if the page sets `dki_template` in frontmatter.
Don't remove either hook unless you've also dropped the corresponding
frontmatter field.
*/}}
<!-- Google Ads Dynamic Keyword Insertion -->
<script>
(function() {
Expand All @@ -297,8 +390,7 @@ <h5>{{ .name }}</h5>
} catch(e) { return ''; }
})();

var placeholder = document.getElementById('dki-placeholder');
if (!placeholder || !keyword) return;
if (!keyword) return;

var formattedKeyword = keyword.replace(/[-_]/g, ' ').trim();

Expand All @@ -317,8 +409,22 @@ <h5>{{ .name }}</h5>
}
return word;
});
var formatted = words.join(' ');

placeholder.innerText = words.join(' ');
var placeholder = document.getElementById('dki-placeholder');
if (placeholder) placeholder.innerText = formatted;

var heading = document.getElementById('dki-heading');
if (heading && heading.dataset.dkiTemplate) {
var rendered = heading.dataset.dkiTemplate.replace('{keyword}', formatted);
var rainbow = heading.querySelector('.rainbow-text');
if (rainbow) {
rainbow.setAttribute('data-text', rendered);
rainbow.innerText = rendered;
} else {
heading.innerText = rendered;
}
}
})();
</script>
{{ end }}
Loading
Loading