Skip to content
Merged
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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,10 @@ not repeat the same scenario.
## Terraform Modules

Open `/terraform-modules/` to explore public AWS Terraform modules
maintained by the native-cube GitHub organization. The catalogue covers Amazon
EKS, EKS Auto Mode, managed node groups, Fargate profiles, KMS, and VPC Flow
Logs, with direct links to each GitHub repository and Terraform Registry page.
maintained by the native-cube GitHub organization. The catalogue covers
Kubernetes platforms, security, networking, messaging, databases, caching, and
object storage, with direct links to each GitHub repository and Terraform
Registry page.

Module names, descriptions, categories, complete-example paths, and visual
treatments are maintained in `terraform-modules/modules.json`. Registry
Expand Down
Binary file modified assets/social/terraform-modules.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions assets/social/terraform-modules.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,8 @@ <h2>YAML &amp; JSON Formatter</h2>
</svg>
<h2>Terraform Modules</h2>
<p>
Explore reusable AWS modules for EKS, KMS, and network observability.
Explore reusable AWS Terraform modules for Kubernetes, security,
networking, messaging, databases, caching, and storage.
</p>
<span class="arrow" aria-hidden="true">↗</span>
</a>
Expand Down
75 changes: 44 additions & 31 deletions scripts/sync-terraform-modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ def render_card(catalogue, module):
input_markup = render_required_inputs(metadata["required_inputs"])
icon = ICONS[module["icon"]]
published_date = metadata["published_at"].split("T", 1)[0]
category_slug = re.sub(r"[^a-z0-9]+", "-", module["category"].lower()).strip("-")

license_label = metadata["license"] or "Not declared"
license_link = (
Expand All @@ -504,7 +505,11 @@ def render_card(catalogue, module):
else ""
)

return f''' <article id="{escape(module["name"])}" class="module-card module-card--{escape(module["theme"])}">
return f''' <article
id="{escape(module["name"])}"
class="module-card module-card--{escape(module["theme"])}"
data-category="{escape(category_slug)}"
>
<div class="card-topline">
<span class="module-icon" aria-hidden="true">
<svg viewBox="0 0 24 24">
Expand All @@ -516,24 +521,16 @@ def render_card(catalogue, module):
</div>
<h3><a class="module-permalink" href="#{escape(module["name"])}">{escape(module["title"])}<span aria-hidden="true">#</span></a></h3>
<p class="module-description">{escape(module["summary"])}</p>
<dl class="module-facts" aria-label="{escape(module["title"])} module metadata">
<dl class="module-overview" aria-label="{escape(module["title"])} module overview">
<div>
<dt>Latest</dt>
<dt>Version</dt>
<dd>v{escape(metadata["version"])}</dd>
</div>
<div>
<dt>Required</dt>
<dd>{required_label} {required_detail}</dd>
</div>
<div>
<dt>Released</dt>
<dd><time datetime="{published_date}">{display_date(metadata["published_at"])}</time></dd>
</div>
</dl>
<p class="module-requirements">
<span>Terraform {escape(metadata["terraform_requirement"])}</span>
<span>AWS {escape(metadata["provider_requirement"])}</span>
</p>
<div class="source-address">
<code>{escape(urls["source"])}</code>
<button
Expand All @@ -551,15 +548,37 @@ def render_card(catalogue, module):
<rect x="8" y="8" width="10" height="11" rx="2" />
<path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h2" />
</svg>
<span>Copy module block</span>
<span>Copy</span>
</button>
</div>
<details class="usage-disclosure">
<div class="card-actions">
<a class="registry-link" href="{urls["registry"]}" target="_blank" rel="noreferrer">
Registry docs <span aria-hidden="true">↗</span>
</a>
<a href="{urls["github"]}" target="_blank" rel="noreferrer">
GitHub <span aria-hidden="true">↗</span>
</a>
</div>
<details class="module-details">
<summary>
<span>View usage and required inputs</span>
<span>Usage &amp; requirements</span>
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="m5 7.5 5 5 5-5" /></svg>
</summary>
<div class="usage-content">
<div class="details-content">
<dl class="detail-facts">
<div>
<dt>Released</dt>
<dd><time datetime="{published_date}">{display_date(metadata["published_at"])}</time></dd>
</div>
<div>
<dt>Terraform</dt>
<dd>{escape(metadata["terraform_requirement"])}</dd>
</div>
<div>
<dt>AWS provider</dt>
<dd>{escape(metadata["provider_requirement"])}</dd>
</div>
</dl>
<h4>Module block</h4>
<pre><code>{escape(example)}</code></pre>
<button
Expand All @@ -578,24 +597,18 @@ def render_card(catalogue, module):
<template data-starter-example>{escape(starter_example)}</template>
<h4>Required inputs</h4>
{input_markup}
<div class="detail-links">
{example_link}
<a href="{urls["releases"]}" target="_blank" rel="noreferrer">
Releases <span aria-hidden="true">↗</span>
</a>
</div>
<p class="module-trust">
{license_link}
<span>Source updated <time datetime="{metadata["pushed_at"].split("T", 1)[0]}">{display_date(metadata["pushed_at"])}</time></span>
</p>
</div>
</details>
<div class="card-links">
<a href="{urls["registry"]}" target="_blank" rel="noreferrer">
Registry docs <span aria-hidden="true">↗</span>
</a>
<a href="{urls["github"]}" target="_blank" rel="noreferrer">
GitHub source <span aria-hidden="true">↗</span>
</a>
{example_link}
<a href="{urls["releases"]}" target="_blank" rel="noreferrer">
Releases <span aria-hidden="true">↗</span>
</a>
</div>
<p class="module-trust">
{license_link}
<span>Source updated <time datetime="{metadata["pushed_at"].split("T", 1)[0]}">{display_date(metadata["pushed_at"])}</time></span>
</p>
</article>'''


Expand Down
4 changes: 2 additions & 2 deletions sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://native-cube.com/</loc>
<lastmod>2026-08-19</lastmod>
<lastmod>2026-08-28</lastmod>
</url>
<url>
<loc>https://native-cube.com/k8s-manifest-builder/</loc>
Expand Down Expand Up @@ -30,6 +30,6 @@
</url>
<url>
<loc>https://native-cube.com/terraform-modules/</loc>
<lastmod>2026-08-19</lastmod>
<lastmod>2026-08-28</lastmod>
</url>
</urlset>
48 changes: 48 additions & 0 deletions terraform-modules/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,43 @@

const copyButtons = document.querySelectorAll("[data-module-copy]");
const copyStatus = document.querySelector("#copy-status");
const moduleSearch = document.querySelector("#module-search");
const categoryButtons = document.querySelectorAll("[data-category]");
const moduleCards = document.querySelectorAll(".module-card");
const moduleResults = document.querySelector("#module-results");
const noModuleResults = document.querySelector("#no-module-results");
let copyStatusTimer;
let activeCategory = "all";

function normalized(value) {
return value.trim().toLocaleLowerCase();
}

function filterModules() {
const query = normalized(moduleSearch.value);
let visibleCount = 0;

for (const card of moduleCards) {
const matchesCategory =
activeCategory === "all" || card.dataset.category === activeCategory;
const matchesQuery = !query || normalized(card.textContent).includes(query);
const isVisible = matchesCategory && matchesQuery;
card.hidden = !isVisible;
visibleCount += Number(isVisible);
}

const noun = visibleCount === 1 ? "module" : "modules";
moduleResults.textContent = `${visibleCount} ${noun}`;
noModuleResults.hidden = visibleCount !== 0;
}

function selectCategory(button) {
activeCategory = button.dataset.category;
for (const categoryButton of categoryButtons) {
categoryButton.setAttribute("aria-pressed", String(categoryButton === button));
}
filterModules();
}

function showCopyStatus(message, isError = false) {
window.clearTimeout(copyStatusTimer);
Expand Down Expand Up @@ -122,3 +158,15 @@ async function copySource(button) {
for (const button of copyButtons) {
button.addEventListener("click", () => copySource(button));
}

moduleSearch.addEventListener("input", filterModules);
moduleSearch.addEventListener("keydown", (event) => {
if (event.key === "Escape" && moduleSearch.value) {
moduleSearch.value = "";
filterModules();
}
});

for (const button of categoryButtons) {
button.addEventListener("click", () => selectCategory(button));
}
Loading
Loading