diff --git a/index.ts b/index.ts index bb15ec39..ddd44274 100644 --- a/index.ts +++ b/index.ts @@ -50,13 +50,14 @@ function findPlatforms(): Platform[] { const platformDir: string = path.join(repoRoot, dir.name); const platformLogo = getPlatformLogoOrThrow(platformDir, dir.name); const platformReadme = getPlatformReadmeOrThrow(platformDir); - const { name, description, content } = extractReadmeFrontMatter(platformReadme); + const { name, description, category, content } = extractReadmeFrontMatter(platformReadme); const terraformSnippet = getTerraformSnippet(platformDir); return { platformType: dir.name, name, description, + category, logo: platformLogo, readme: content, terraformSnippet @@ -81,11 +82,11 @@ function getPlatformReadmeOrThrow(platformDir: string) { try { return fs.readFileSync(path.join(platformDir, "README.md"), "utf-8"); } catch { - throw new Error('Platform README.md not found. Each platform should have a README.md file.'); + throw new Error(`Platform README.md not found for ${platformDir}. Each platform should have a README.md file.`); } } -function extractReadmeFrontMatter(platformReadme: string): { name: string; description: string; content: string } { +function extractReadmeFrontMatter(platformReadme: string): { name: string; description: string; category?: string; content: string } { const { data, content } = matter(platformReadme); const name = data.name; @@ -98,10 +99,13 @@ function extractReadmeFrontMatter(platformReadme: string): { name: string; descr throw new Error('Property "description" is missing in the front matter of the platform README.md. Each platform README.md should have a description defined in the front matter.'); } + const category = data.category; + return { name, description, - content + content, + category } } @@ -169,12 +173,16 @@ function parseReadme(filePath) { ? getBuildingBlockFolderUrl(backplaneDir) : null; + const terraformSnippetDir = path.join(buildingBlockDir, ".."); + const terraformSnippet = getTerraformSnippet(terraformSnippetDir); + return { id, platformType: platform, logo: buildingBlockLogoPath, buildingBlockUrl, backplaneUrl, + terraformSnippet, ...data, howToUse: extractSection(/## How to Use([\s\S]*?)(##|$)/), resources: parseTable(body.match(/## Resources([\s\S]*)/)), @@ -224,5 +232,6 @@ export interface Platform { description: string; logo: string; readme: string; + category?: string; terraformSnippet?: string; } diff --git a/modules/azure/storage-account/meshstack_integration.tf b/modules/azure/storage-account/meshstack_integration.tf new file mode 100644 index 00000000..0c735d39 --- /dev/null +++ b/modules/azure/storage-account/meshstack_integration.tf @@ -0,0 +1,66 @@ +# TODO: this is actual not a correct file but just acts as an example for now + +locals { + name = "azure-storage-account" + scope = "/subscriptions/00000000-0000-0000-0000-000000000000" + existing_principal_ids = [ + "00000000-0000-0000-0000-000000000000" + ] + service_principal_name = "storage-account-deployer" + + workspace_identifier = "my-workspace" +} + +provider "meshstack" { + # Configure meshStack API credentials here or use environment variables. + # endpoint = "https://api.my.meshstack.io" + # apikey = "00000000-0000-0000-0000-000000000000" + # apisecret = "uFOu4OjbE4JiewPxezDuemSP3DUrCYmw" +} + +provider "azurerm" { + features {} +} + +# Import the backplane module to get IAM and other required outputs +module "backplane" { + source = "./backplane" + name = local.name + scope = local.scope + existing_principal_ids = local.existing_principal_ids + create_service_principal_name = local.service_principal_name + workload_identity_federation = {} # TODO this should come from data_source +} + +# Import the building block definition into meshStack +# NOTE: meshstack_buildingblock_definition is a placeholder for demonstration. Replace with the actual resource if available. +resource "meshstack_buildingblock_definition" "storage_account" { + metadata = { + name = "azure-storage-account" + owned_by_workspace = local.workspace_identifier + } + + spec = { + display_name = "Azure Storage Account" + description = "Provision Azure Storage Accounts with encryption and access control" + + supported_platforms = ["azure"] + + source = { + git = { + url = "https://github.com/meshcloud/meshstack-hub.git" + ref = "main" + path = "modules/azure/storage-account/buildingblock" + } + } + + implementation_type = "Terraform" + # Pass IAM outputs as inputs if required by your building block + role_definition_id = module.backplane.role_definition_id + role_assignment_ids = module.backplane.role_assignment_ids + principal_ids = module.backplane.role_assignment_principal_ids + service_principal = module.backplane.created_service_principal + application = module.backplane.created_application + scope = module.backplane.scope + } +} diff --git a/modules/meshstack/README.md b/modules/meshstack/README.md new file mode 100644 index 00000000..8a67a3c1 --- /dev/null +++ b/modules/meshstack/README.md @@ -0,0 +1,6 @@ +--- +name: meshStack +description: meshStack is a cloud management platform that provides a unified interface for managing and governing cloud environments +--- + + diff --git a/website/src/app/core/template.ts b/website/src/app/core/template.ts index 91d7ace8..38b47a3c 100644 --- a/website/src/app/core/template.ts +++ b/website/src/app/core/template.ts @@ -8,4 +8,5 @@ export interface Template { buildingBlockUrl: string; backplaneUrl: string | null; supportedPlatforms: string[]; + terraformSnippet?: string; } diff --git a/website/src/app/features/template-details/template-details.component.html b/website/src/app/features/template-details/template-details.component.html index c3b88bb6..743d9520 100644 --- a/website/src/app/features/template-details/template-details.component.html +++ b/website/src/app/features/template-details/template-details.component.html @@ -1,138 +1,203 @@ -
- {{ template.description }}
-
+ {{ template.description }}
+ + Prefer UI over code? You can also import this building block via the meshStack UI instead of using Terraform. +
++ Use this Terraform configuration to create the building block definition directly in meshStack. +
Add to your meshStack
-Click the button to import this building block directly into your meshStack instance.
+Set up infrastructure (optional)
-- Run the - - backplane Terraform files - - to prepare your cloud environment. -
+Configure and deploy
-Fill in required inputs and secrets, then deploy to your platforms.
+- - Prefer manual setup? -
-- Copy the Terraform files from the repository above into your own repo, then create a building block definition in meshStack pointing to your repository. -
-{{ template.terraformSnippet }}
+
+ Quick and easy setup directly from meshStack
+Import this building block into your meshStack instance
For users who prefer to own the Terraform code
+This building block does not exist or has been removed.
+ + Back to Home +
+
+
{{ card.description }}
diff --git a/website/src/app/features/template-gallery/platform-cards/platform-cards.component.ts b/website/src/app/features/template-gallery/platform-cards/platform-cards.component.ts index 06666cbe..be0831cd 100644 --- a/website/src/app/features/template-gallery/platform-cards/platform-cards.component.ts +++ b/website/src/app/features/template-gallery/platform-cards/platform-cards.component.ts @@ -15,6 +15,25 @@ export class PlatformCardsComponent { @Input() public cards!: PlatformCard[]; + /** + * Define your custom order here. Use the property that uniquely identifies the platform (e.g., title or id). + * Example: ['Azure', 'AWS', 'GCP'] + */ + public customOrder: string[] = ['Microsoft Azure', 'Amazon Web Services', 'Google Cloud Platform', 'Azure Kubernetes Service', 'STACKIT']; // <-- customize as needed + + /** + * Returns the cards sorted by customOrder, with others following in original order. + */ + public get sortedCards(): PlatformCard[] { + console.log(this.cards); + if (!this.cards) return []; + const order = this.customOrder; + return [ + ...this.cards.filter(card => order.includes(card.title)).sort((a, b) => order.indexOf(a.title) - order.indexOf(b.title)), + ...this.cards.filter(card => !order.includes(card.title)) + ]; + } + public logoBackgroundColors: { [key: string]: string } = {}; public onBackgroundColorExtracted(cardTitle: string, color: string): void {