# TeraSky Backstage Plugins TeraSky's Backstage Plugins is an open-source monorepo containing 53 Backstage plugins built and tested against Backstage version 1.50.3. The collection covers Kubernetes resource ingestion, Crossplane and KRO integration, VCF Automation/Operations management, ScaleOps cost optimization, SpectroCloud cluster lifecycle, developer tooling (DevPod, Spring Initializer, Template Builder), authentication modules (VCF SSO, SpectroCloud OIDC), policy enforcement (Kyverno), and AI tooling support via Model Context Protocol (MCP) backends for the Scaffolder, RBAC, and Catalog. Every plugin is published independently to npm under the `@terasky/` namespace, follows Backstage's new frontend system with `/alpha` exports, and ships permission-framework integration out of the box. The repository uses a Yarn 4 workspace monorepo layout — source code lives in `plugins/*`, the host Backstage app lives in `packages/app` and `packages/backend`, and MkDocs-based documentation lives in `site/docs/`. The backbone of the collection is the `@terasky/backstage-plugin-kubernetes-ingestor` backend plugin, which auto-discovers Kubernetes workloads, Crossplane XRDs/Claims, KRO RGDs/Instances, and generic CRDs and registers them as Components, Resources, APIs, and Software Templates in the catalog. Frontend plugins build on top of this foundation to display graphs, YAML viewers, policy reports, cost data, and vendor-specific resource cards, while MCP backend plugins expose catalog, scaffolder, and RBAC capabilities to AI agents. --- ## Plugin Installation and Registration — Backend (New System) All backend plugins are registered by passing a dynamic import to `backend.add()` in `packages/backend/src/index.ts`. No manual provider wiring is needed with the new backend system. ```typescript // packages/backend/src/index.ts import { createBackend } from '@backstage/backend-defaults'; const backend = createBackend(); // Kubernetes resource auto-ingestion backend.add(import('@terasky/backstage-plugin-kubernetes-ingestor')); // Crossplane resource permissions backend backend.add(import('@terasky/backstage-plugin-crossplane-resources-backend')); // KRO resource permissions backend backend.add(import('@terasky/backstage-plugin-kro-resources-backend')); // ScaleOps cost data + MCP actions backend.add(import('@terasky/backstage-plugin-scaleops-backend')); // VCF Automation API proxy + permissions backend.add(import('@terasky/backstage-plugin-vcf-automation-backend')); // VCF Operations metrics backend backend.add(import('@terasky/backstage-plugin-vcf-operations-backend')); // SpectroCloud API proxy + MCP actions backend.add(import('@terasky/backstage-plugin-spectrocloud-backend')); // SpectroCloud catalog ingestor backend.add(import('@terasky/backstage-plugin-spectrocloud-ingestor')); // SpectroCloud cluster supplier for Kubernetes plugin backend.add(import('@terasky/backstage-plugin-spectrocloud-cluster-provider')); // SpectroCloud OIDC auth backend backend.add(import('@terasky/backstage-plugin-spectrocloud-auth-backend')); // VCF SSO OIDC auth backend backend.add(import('@terasky/backstage-plugin-vcfsso-auth-backend')); // Educates training portal backend backend.add(import('@terasky/backstage-plugin-educates-backend')); // MCP backends for AI agents backend.add(import('@terasky/plugin-scaffolder-mcp-backend')); backend.add(import('@terasky/plugin-rbac-mcp-backend')); backend.add(import('@terasky/backstage-plugin-catalog-mcp-backend')); backend.add(import('@terasky/backstage-plugin-ai-rules-plugin-backend')); // Kyverno permissions backend backend.add(import('@terasky/backstage-plugin-kyverno-policy-reports-backend')); // Kubernetes resources permissions backend backend.add(import('@terasky/backstage-plugin-kubernetes-resources-permissions-backend')); // Module Federation CDN loader backend.add(import('@terasky/backstage-plugin-module-federation-cdn-backend')); // VCF Automation catalog ingestor backend.add(import('@terasky/backstage-plugin-vcf-automation-ingestor')); // VCFA VKS cluster supplier backend.add(import('@terasky/backstage-plugin-vcfa-vks-cluster-provider')); // Scaffolder custom actions (claim-template, crd-template, catalog-info-cleaner) backend.add(import('@terasky/backstage-plugin-scaffolder-backend-module-terasky-utils')); // Spring Boot project generator scaffolder action backend.add(import('@terasky/backstage-plugin-scaffolder-backend-module-spring-initializer')); backend.start(); ``` --- ## Plugin Installation and Registration — Frontend (New System) All frontend plugins with a `/alpha` export are added to `createApp({ features: [...] })` in `packages/app/src/App.tsx`. This replaces manual `EntityPage.tsx` wiring. ```typescript // packages/app/src/App.tsx import { createApp } from '@backstage/frontend-defaults'; const app = createApp({ features: [ // Auto-injects Crossplane tabs on matching entity pages import('@terasky/backstage-plugin-crossplane-resources-frontend/alpha'), // KRO resource overview + graph tabs import('@terasky/backstage-plugin-kro-resources-frontend/alpha'), // ScaleOps cost optimization card import('@terasky/backstage-plugin-scaleops-frontend/alpha'), // DevPod "Open in DevPod" overview card import('@terasky/backstage-plugin-devpod/alpha'), // Kyverno policy reports tab + overview card import('@terasky/backstage-plugin-kyverno-policy-reports/alpha'), // Kubernetes dependency graph + resource list tabs import('@terasky/backstage-plugin-kubernetes-resources-frontend/alpha'), // VCF Automation deployment/resource/project views import('@terasky/backstage-plugin-vcf-automation/alpha'), // VCF Operations metrics view import('@terasky/backstage-plugin-vcf-operations/alpha'), // SpectroCloud cluster + profile entity cards import('@terasky/backstage-plugin-spectrocloud/alpha'), // SpectroCloud OIDC frontend API ref import('@terasky/backstage-plugin-spectrocloud-auth/alpha'), // SpectroCloud Kubernetes auth module extension import('@terasky/backstage-plugin-spectrocloud-kubernetes-auth-module/alpha'), // Educates workshop browser page import('@terasky/backstage-plugin-educates/alpha'), // Entity scaffolder tab with template list import('@terasky/backstage-plugin-entity-scaffolder-content/alpha'), // GitOps manifest updater scaffolder field extension import('@terasky/backstage-plugin-gitops-manifest-updater/alpha'), // GitOps manifest updater scaffolder field extension import('@terasky/backstage-plugin-terraform-scaffolder/alpha'), // Spring Boot project configurator field extension import('@terasky/backstage-plugin-spring-initializer/alpha'), // Template Builder WYSIWYG editor page import('@terasky/backstage-plugin-template-builder/alpha'), // AI Rules frontend import('@terasky/backstage-plugin-ai-rules/alpha'), // CRD-type API documentation renderer import('@terasky/backstage-plugin-api-docs-module-crd/alpha'), // Frontend Extensions introspection page import('@terasky/backstage-plugin-frontend-extensions-explorer/alpha'), // Config-driven sign-in page import('@terasky/backstage-plugin-app-module-global-signin-page/alpha'), // VCF SSO frontend OAuth2 client import('@terasky/backstage-plugin-vcfsso-auth/alpha'), ], }); export default app.createRoot(); ``` --- ## Kubernetes Ingestor — Full Configuration `@terasky/backstage-plugin-kubernetes-ingestor` is the central backend plugin that auto-discovers Kubernetes workloads, Crossplane Claims/XRDs, KRO Instances/RGDs, and generic CRDs, and registers them as catalog entities and Software Templates. ```yaml # app-config.yaml kubernetesIngestor: # Whether to generate API entities of type "crd" (requires api-docs-module-crd) or "openapi" ingestAPIsAsCRDs: true # Default owner when no terasky.backstage.io/owner annotation is present defaultOwner: platform-engineering-team # Inherit owner annotation from the Namespace when the workload has none inheritOwnerFromNamespace: true # Map metadata fields to Backstage entity metadata mappings: namespaceModel: 'cluster' # cluster | namespace | default nameModel: 'name-cluster' # name-cluster | name-namespace | name-kind | name | uid titleModel: 'name' # name | name-cluster | name-namespace systemModel: 'cluster-namespace' # cluster | namespace | cluster-namespace | default referencesNamespaceModel: 'default' # default | same # Map SA-authenticated cluster names to OIDC cluster names for frontend use clusterNameMapping: mode: 'prefix-replacement' # or 'explicit' sourcePrefix: 'sa-' targetPrefix: 'oidc-' # Only ingest from specific clusters (omit to ingest from all non-OIDC clusters) allowedClusterNames: - sa-production - sa-staging # Standard Kubernetes workload ingestion components: enabled: true ingestAsResources: false # true → create Resource entities instead of Component onlyIngestAnnotatedResources: false # true → require terasky.backstage.io/add-to-catalog disableDefaultWorkloadTypes: false taskRunner: frequency: 10 # seconds between syncs timeout: 600 # max seconds per sync cycle excludedNamespaces: - kube-system - kube-public customWorkloadTypes: - group: pkg.crossplane.io apiVersion: v1 plural: providers defaultType: service - group: networking.k8s.io apiVersion: v1 plural: ingresses defaultType: ingress ingestAsResources: true # per-type override # Crossplane Claims and XRDs crossplane: enabled: true claims: ingestAllClaims: true ingestAsResources: false xrds: enabled: true ingestAllXRDs: true ingestOnlyAsAPI: false # true → API entities only, no Software Templates convertDefaultValuesToPlaceholders: true taskRunner: frequency: 10 timeout: 600 publishPhase: allowedTargets: ['github.com', 'gitlab.com'] target: github allowRepoSelection: true requestUserCredentialsForRepoUrl: false git: repoUrl: github.com?owner=my-org&repo=backstage-templates targetBranch: main # KRO (Kubernetes Resource Orchestrator) kro: enabled: true instances: ingestAsResources: false rgds: enabled: true ingestOnlyAsAPI: false taskRunner: frequency: 10 timeout: 600 publishPhase: target: github allowRepoSelection: true git: repoUrl: github.com?owner=my-org&repo=backstage-templates targetBranch: main # Generic CRDs (by label selector or explicit list) genericCRDTemplates: ingestOnlyAsAPI: false crdLabelSelector: key: terasky.backstage.io/generate-form value: "true" crds: - certificates.cert-manager.io # Auto-add ArgoCD annotation when the resource has Argo tracking labels argoIntegration: true ``` --- ## Kubernetes Ingestor — Kubernetes Annotations on Workloads Kubernetes resource annotations control how entities appear in Backstage. The ingestor reads these annotations at ingestion time. ```yaml # Example Deployment with all supported annotations apiVersion: apps/v1 kind: Deployment metadata: name: petstore namespace: web-apps annotations: # --- Identity --- terasky.backstage.io/title: "Petstore API" terasky.backstage.io/description: "OpenAPI Petstore demo service" terasky.backstage.io/owner: "group:default/platform-team" terasky.backstage.io/system: "ecommerce-platform" terasky.backstage.io/component-type: "service" terasky.backstage.io/lifecycle: "production" # --- API auto-registration (choose one) --- # Option 1: runtime fetch via $text directive terasky.backstage.io/provides-api-from-def: "http://petstore.example.com/api/v3/openapi.json" # Option 2: fetch-and-embed at ingestion time # terasky.backstage.io/provides-api-from-url: "http://petstore.example.com/swagger.json" # Option 3: resolve endpoint from another K8s resource # terasky.backstage.io/provides-api-from-resource-ref: | # { # "kind": "Service", "name": "petstore-svc", "apiVersion": "v1", # "path": "/swagger/openapi.json", "target-protocol": "http", # "target-port": "80", "target-field": ".status.loadBalancer.ingress[0].ip" # } # --- Catalog opt-in/opt-out (when onlyIngestAnnotatedResources: true) --- terasky.backstage.io/add-to-catalog: "true" # terasky.backstage.io/exclude-from-catalog: "true" # --- Source manifest URL (for GitOps Manifest Updater) --- terasky.backstage.io/source-file-url: "https://github.com/my-org/gitops/blob/main/web-apps/petstore/deployment.yaml" spec: replicas: 3 selector: matchLabels: app: petstore template: metadata: labels: app: petstore spec: containers: - name: petstore image: swaggerapi/petstore3:latest ``` --- ## Kubernetes Ingestor — Delta / Incremental Updates The `KubernetesEntityProvider` exposes a `deltaUpdate()` method for pushing individual resource changes without waiting for the next full sync cycle. ```typescript import { DeltaEvent } from '@terasky/backstage-plugin-kubernetes-ingestor'; // Upsert: re-fetches the resource from the cluster and updates catalog entities await provider.deltaUpdate({ action: 'upsert', apiVersion: 'apps/v1', kind: 'Deployment', name: 'petstore', namespace: 'web-apps', clusterName: 'sa-production', }); // Delete: removes all catalog entities for the resource await provider.deltaUpdate({ action: 'delete', apiVersion: 'apps/v1', kind: 'Deployment', name: 'petstore', namespace: 'web-apps', clusterName: 'sa-production', }); // Delete with explicit entity refs (needed when annotation-based naming was used) await provider.deltaUpdate({ action: 'delete', apiVersion: 'apps/v1', kind: 'Deployment', name: 'petstore', namespace: 'web-apps', clusterName: 'sa-production', entityNames: ['Component:default/my-custom-named-petstore'], }); // Notes: // - deltaUpdate() throws if the initial full sync has not yet completed. // - Shared System entities are never removed on a single resource delete. // - Concurrent calls are serialized internally via a mutex. ``` --- ## Crossplane Resources Frontend — Entity Page Integration (Legacy System) `@terasky/backstage-plugin-crossplane-resources-frontend` renders Claims, Composite Resources, Managed Resources, and a dependency graph for entities ingested by the Kubernetes Ingestor. ```typescript // packages/app/src/components/catalog/EntityPage.tsx import { CrossplaneResourcesTableSelector, CrossplaneOverviewCardSelector, CrossplaneResourceGraphSelector, useResourceGraphAvailable, useResourcesListAvailable, IfCrossplaneOverviewAvailable, IfCrossplaneResourceGraphAvailable, IfCrossplaneResourcesListAvailable, } from '@terasky/backstage-plugin-crossplane-resources-frontend'; import { CrossplaneApiClient, crossplaneApiRef, } from '@terasky/backstage-plugin-crossplane-resources-frontend'; import { createApiFactory, discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api'; // Register the API client (packages/app/src/apis.ts) export const apis: AnyApiFactory[] = [ createApiFactory({ api: crossplaneApiRef, deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef }, factory: ({ discoveryApi, fetchApi }) => new CrossplaneApiClient(discoveryApi, fetchApi), }), ]; // Entity page component const CrossplaneEntityPage = () => { const isResourcesListAvailable = useResourcesListAvailable(); const isResourceGraphAvailable = useResourceGraphAvailable(); return ( ); }; // Wire to entity switch const componentPage = ( ); ``` --- ## Crossplane Resources Backend — Permission Framework `@terasky/backstage-plugin-crossplane-resources-backend` integrates with Backstage's permission framework. Permissions can be managed via a CSV file with the community RBAC plugin. ```yaml # app-config.yaml permission: enabled: true rbac: policies-csv-file: /etc/backstage/permissions.csv policyFileReload: true pluginsWithPermission: - kubernetes - crossplane ``` ```csv # permissions.csv — grant a role all Crossplane read permissions p, role:default/platform-team, crossplane.claims.list, read, allow p, role:default/platform-team, crossplane.claims.view-yaml, read, allow p, role:default/platform-team, crossplane.claims.show-events, read, allow p, role:default/platform-team, crossplane.composite-resources.list, read, allow p, role:default/platform-team, crossplane.composite-resources.view-yaml, read, allow p, role:default/platform-team, crossplane.composite-resources.show-events, read, allow p, role:default/platform-team, crossplane.managed-resources.list, read, allow p, role:default/platform-team, crossplane.managed-resources.view-yaml, read, allow p, role:default/platform-team, crossplane.managed-resources.show-events, read, allow p, role:default/platform-team, crossplane.additional-resources.list, read, allow p, role:default/platform-team, crossplane.additional-resources.view-yaml, read, allow p, role:default/platform-team, crossplane.additional-resources.show-events, read, allow p, role:default/platform-team, crossplane.resource-graph.show, read, allow p, role:default/platform-team, crossplane.overview.view, read, allow g, group:default/all_users, role:default/platform-team ``` --- ## KRO Resources Frontend — Entity Page Integration `@terasky/backstage-plugin-kro-resources-frontend` visualizes KRO RGD instances, managed resources, and dependency graphs. ```typescript // packages/app/src/components/catalog/EntityPage.tsx import { IfKroOverviewAvailable, IfKroResourceGraphAvailable, IfKroResourcesListAvailable, isKroAvailable, KroOverviewCard, KroResourceGraph, KroResourceTable, useKroResourceListAvailable, useKroResourceGraphAvailable, } from '@terasky/backstage-plugin-kro-resources-frontend'; const KroEntityPage = () => { const isKroResourceListAvailable = useKroResourceListAvailable(); const isKroResourceGraphAvailable = useKroResourceGraphAvailable(); return ( ); }; const componentPage = ( ); ``` --- ## ScaleOps Backend — Configuration and MCP Actions `@terasky/backstage-plugin-scaleops-backend` proxies ScaleOps cost optimization data and exposes 5 MCP actions for AI agents. ```yaml # app-config.yaml scaleops: baseUrl: https://scaleops.example.com linkToDashboard: true # include dashboard URLs in MCP action output authentication: enabled: true type: internal # or 'ldap' user: ${SCALEOPS_USER} password: ${SCALEOPS_PASSWORD} ``` ```json // MCP action: query cost optimization data for a workload // Action: scaleops_get_recommendations { "workloadName": "petstore", "namespace": "web-apps", "clusterName": "production" } // Expected response (with linkToDashboard: true) { "workloads": [ { "workloadName": "petstore", "namespace": "web-apps", "currentCost": 42.5, "optimizedCost": 18.3, "savingsPercent": 56.9, "cpuRecommendation": { "request": "100m", "limit": "200m" }, "memoryRecommendation": { "request": "128Mi", "limit": "256Mi" }, "dashboardUrl": "https://scaleops.example.com/cost-report/compute?searchTerms=petstore&..." } ] } ``` --- ## Scaffolder Backend Module — TeraSky Utils (Custom Actions) `@terasky/backstage-plugin-scaffolder-backend-module-terasky-utils` adds three scaffolder actions: `terasky:claim-template`, `terasky:crd-template`, and `terasky:catalog-info-cleaner`. ```yaml # Software Template using terasky:claim-template apiVersion: scaffolder.backstage.io/v1beta3 kind: Template metadata: name: provision-postgres title: Provision PostgreSQL via Crossplane spec: owner: platform-team type: service parameters: - title: Database Configuration required: [xrName, xrNamespace, clusters] properties: xrName: type: string title: Database name xrNamespace: type: string title: Namespace default: databases clusters: type: array title: Target clusters items: type: string enum: [production, staging] size: type: string enum: [small, medium, large] default: small version: type: string default: "15" steps: - id: generate-claim name: Generate Crossplane Claim YAML action: terasky:claim-template input: parameters: ${{ parameters }} apiVersion: db.example.com/v1alpha1 kind: PostgreSQLInstance clusters: ${{ parameters.clusters }} removeEmptyParams: true excludeParams: [xrName, xrNamespace, clusters, targetBranch, repoUrl] - id: publish name: Push to Git action: publish:github:pull-request input: repoUrl: github.com?owner=my-org&repo=gitops branchName: provision-${{ parameters.xrName }} title: "Provision PostgreSQL: ${{ parameters.xrName }}" description: Auto-generated by Backstage output: links: - title: Pull Request url: ${{ steps.publish.output.remoteUrl }} - title: Download Manifest url: data:application/yaml;charset=utf-8,${{ steps['generate-claim'].output.manifestEncoded }} ``` ```yaml # Software Template using terasky:catalog-info-cleaner steps: - id: fetch-entity name: Fetch Entity action: catalog:fetch input: entityRef: ${{ parameters.entity }} - id: clean-entity name: Clean Entity Manifest for Git action: terasky:catalog-info-cleaner input: entity: ${{ steps['fetch-entity'].output.entity }} # Output: steps['clean-entity'].output.manifest (raw YAML) # steps['clean-entity'].output.filePath (path written to workspace) ``` --- ## Scaffolder MCP Backend — AI-Driven Template Execution `@terasky/plugin-scaffolder-mcp-backend` exposes 7 MCP actions enabling AI agents to discover and execute Backstage Software Templates programmatically. ```yaml # app-config.yaml — minimal config, no extra keys required permission: enabled: true rbac: policies-csv-file: /etc/backstage/permissions.csv pluginsWithPermission: - scaffolder ``` ```json // MCP action 1: list_software_templates // Input: {} (no required fields) // Returns: array of template names, titles, and descriptions // MCP action 2: get_software_template_parameter_schema { "templateRef": "template:default/provision-postgres" } // Returns: full JSON Schema of the template's parameters // MCP action 3: run_software_template — executes template and waits for completion { "templateRef": "template:default/provision-postgres", "parameters": { "xrName": "my-db", "xrNamespace": "databases", "clusters": ["production"], "size": "small", "version": "15" } } // Returns: { "taskId": "task:abc123", "status": "completed", "output": { ... } } // MCP action 4: list_software_template_actions // Returns: all registered scaffolder actions with their IDs and descriptions // MCP action 5: get_software_template_action_details { "actionId": "terasky:claim-template" } // Returns: full input/output JSON Schema for the action // MCP action 6: list_software_template_extensions // Returns: all registered scaffolder field extensions // MCP action 7: get_software_template_extension_details { "extensionId": "GitOpsManifestUpdater" } // Returns: field extension schema and usage documentation ``` --- ## Catalog MCP Backend — AI Catalog Queries `@terasky/backstage-plugin-catalog-mcp-backend` exposes 5 MCP actions for querying the Backstage Catalog. No additional configuration beyond standard catalog setup is needed. ```json // MCP action 1: get_entities_by_owner { "owner": "group:default/platform-team" } // Returns: all entities owned by that group // MCP action 2: get_entities_by_annotation { "annotation": "github.com/project-slug", "value": "my-org/petstore" } // Returns: entities matching the annotation key+value (omit value for key-only search) // MCP action 3: get_entity_types_for_kind { "kind": "Component" } // Returns: ["service", "website", "library", "crossplane-claim", "kro-instance", ...] // MCP action 4: get_all_entities_by_kind_and_type { "kind": "Component", "type": "crossplane-claim" } // Returns: all Component entities of type crossplane-claim // MCP action 5: get_entities_with_custom_query — flexible query with field selection { "filter": "kind=Component,spec.type=service,spec.owner=group:default/platform-team", "fields": "kind,metadata.name,metadata.namespace,spec.type,spec.owner,metadata.annotations" } // Returns: matched entities with only the requested fields (reduces payload size) // Tip: use comma-separated filters without spaces; field selection is highly recommended // for large catalogs (1000+ entities) ``` --- ## RBAC MCP Backend — AI Permission Management `@terasky/plugin-rbac-mcp-backend` exposes 10 MCP actions for managing RBAC policies. Users must have `permission.policy.*` admin permissions. ```bash # Create admin role via REST API (required before using MCP actions) curl -X POST http://localhost:7007/api/permission/roles \ -H "Content-Type: application/json" \ -d '{ "memberReferences": ["user:default/admin"], "name": "role:default/rbac-admin", "metadata": { "description": "RBAC administrators" } }' ``` ```csv # Grant RBAC admin permissions via CSV p, role:default/rbac-admin, permission.policy.read, read, allow p, role:default/rbac-admin, permission.policy.create, create, allow p, role:default/rbac-admin, permission.policy.update, update, allow p, role:default/rbac-admin, permission.policy.delete, delete, allow g, user:default/admin, role:default/rbac-admin ``` ```json // MCP action: create_role_with_permissions — creates role and grants permissions atomically { "roleName": "role:default/developers", "memberReferences": ["group:default/engineering"], "permissions": [ { "permission": "scaffolder.task.read", "action": "read" }, { "permission": "scaffolder.task.create", "action": "create" }, { "permission": "scaffolder-template", "action": "read" }, { "permission": "catalog-entity", "action": "read" } ] } // MCP action: list_roles — list all roles {} // MCP action: delete_role { "roleName": "role:default/old-role" } ``` --- ## VCF Automation Backend — API Endpoints and Configuration `@terasky/backstage-plugin-vcf-automation-backend` proxies VCF Automation APIs and exposes REST endpoints consumed by the frontend plugin. ```yaml # app-config.yaml — single instance vcfAutomation: name: vcfa-prod majorVersion: 9 # 8 or 9 orgName: my-org # required for VCF 9+ organizationType: all-apps # 'vm-apps' (default) or 'all-apps' baseUrl: https://vcfa.example.com authentication: username: ${VCFA_USERNAME} password: ${VCFA_PASSWORD} ``` ```yaml # app-config.yaml — multi-instance vcfAutomation: instances: - name: vcfa-eu baseUrl: https://vcfa-eu.example.com majorVersion: 9 orgName: eu-org organizationType: all-apps authentication: username: ${VCFA_EU_USER} password: ${VCFA_EU_PASS} - name: vcfa-us baseUrl: https://vcfa-us.example.com majorVersion: 8 authentication: username: ${VCFA_US_USER} password: ${VCFA_US_PASS} domain: corp.example.com ``` ```bash # REST endpoints exposed by the backend plugin GET /api/vcf-automation/deployments # list all deployments GET /api/vcf-automation/deployments/:id # deployment details GET /api/vcf-automation/deployments/:id/resources # resources in a deployment POST /api/vcf-automation/deployments/:id/operations # trigger deployment operation GET /api/vcf-automation/resources/:id # resource details GET /api/vcf-automation/projects # list all projects GET /api/vcf-automation/projects/:id # project details GET /api/vcf-automation/supervisor-resources # paginated supervisor resources GET /api/vcf-automation/supervisor-namespaces # supervisor namespaces POST /api/vcf-automation/resources/:resourceId/power-actions/:action # execute VM power action PUT /api/vcf-automation/standalone-vms/:ns/:nsName/:vmName/power-state # standalone VM power GET /api/vcf-automation/events # stream VCF events ``` --- ## VCF Operations Backend — Multi-Instance Configuration `@terasky/backstage-plugin-vcf-operations-backend` connects to VCF Operations instances to surface metrics data in Backstage. ```yaml # app-config.yaml vcfOperations: instances: - name: prod-vcf-primary baseUrl: https://vcf-prod-01.example.com majorVersion: 9 relatedVCFAInstances: # link to VCF Automation instances - vcfa-prod authentication: username: ${VCF_OPS_USERNAME} password: ${VCF_OPS_PASSWORD} - name: staging-vcf baseUrl: https://vcf-staging.example.com majorVersion: 8 authentication: username: ${VCF_OPS_STAGING_USER} password: ${VCF_OPS_STAGING_PASS} domain: staging.local ``` --- ## SpectroCloud Plugins — Authentication, Ingestion, and Frontend Cards The SpectroCloud plugin family covers OIDC auth, catalog ingestion, Kubernetes cluster supply, and entity cards. ```yaml # app-config.yaml — SpectroCloud OIDC auth backend auth: environment: production providers: spectrocloud: production: clientId: ${SPECTROCLOUD_CLIENT_ID} clientSecret: ${SPECTROCLOUD_CLIENT_SECRET} metadataUrl: https://spectrocloud.example.com/.well-known/openid-configuration signIn: resolvers: - resolver: emailMatchingUserEntityProfileEmail ``` ```yaml # app-config.yaml — SpectroCloud ingestor spectrocloud: baseUrl: https://spectrocloud.example.com apiKey: ${SPECTROCLOUD_API_KEY} taskRunner: frequency: 300 # sync every 5 minutes timeout: 600 ``` ```typescript // packages/app/src/components/catalog/EntityPage.tsx (legacy system) import { SpectroCloudClusterCard, SpectroCloudClusterProfileCard, } from '@terasky/backstage-plugin-spectrocloud'; const spectroCloudClusterPage = ( {/* shows K8s version, attached profiles, cloud type, kubeconfig download */} ); const spectroCloudProfilePage = ( {/* shows profile version, attached clusters */} ); // Route entity types to their pages // In EntitySwitch: // case 'spectrocloud-cluster' → spectroCloudClusterPage // case 'spectrocloud-cluster-profile' → spectroCloudProfilePage ``` --- ## VCF SSO Authentication — Backend and Frontend `@terasky/backstage-plugin-vcfsso-auth-backend` and `@terasky/backstage-plugin-vcfsso-auth` provide VCF SSO OIDC authentication with automatic handling of VCF SSO's non-standard `acct` claim. ```yaml # app-config.yaml auth: environment: production providers: vcfsso: production: clientId: ${VCFSSO_CLIENT_ID} clientSecret: ${VCFSSO_CLIENT_SECRET} metadataUrl: https://vcf-sso.example.com/oidc/endpoint/VCFSSO callbackUrl: https://backstage.example.com/api/auth/vcfsso/handler/frame prompt: login # force re-authentication on every sign-in signIn: resolvers: - resolver: emailMatchingUserEntityProfileEmail # matches acct claim to spec.profile.email - resolver: emailLocalPartMatchingUserEntityName # fallback: local part of acct to entity name allowedDomains: - example.com ``` ```typescript // packages/app/src/App.tsx — add VCF SSO sign-in (legacy system) import { vcfSsoAuthApiRef } from '@terasky/backstage-plugin-vcfsso-auth'; import { createApiFactory, discoveryApiRef, oauthRequestApiRef } from '@backstage/core-plugin-api'; export const apis: AnyApiFactory[] = [ createApiFactory({ api: vcfSsoAuthApiRef, deps: { discoveryApi: discoveryApiRef, oauthRequestApi: oauthRequestApiRef }, factory: ({ discoveryApi, oauthRequestApi }) => OAuth2.create({ discoveryApi, oauthRequestApi, provider: { id: 'vcfsso', title: 'VCF SSO', icon: () => null }, defaultScopes: ['openid', 'profile', 'email'], environment: 'production', }), }), ]; ``` --- ## Global Sign-In Page Module — Configuration-Driven Auth Providers `@terasky/backstage-plugin-app-module-global-signin-page` replaces hardcoded sign-in page TypeScript with YAML configuration. Supports all core Backstage auth providers plus SpectroCloud and VCF SSO. ```yaml # app-config.yaml signinPage: enableGuestProvider: false # NEVER true in production providers: microsoft: enabled: true title: Corporate SSO message: Sign in with your corporate Microsoft account github: enabled: true title: GitHub message: External contributors — sign in with GitHub okta: enabled: true title: Okta SSO message: Enterprise users — sign in with Okta spectrocloud: enabled: false vcfsso: enabled: true title: VCF SSO message: Sign in using VCF SSO google: enabled: false gitlab: enabled: false bitbucket: enabled: false openshift: enabled: false ``` --- ## Kyverno Policy Reports — Frontend Integration `@terasky/backstage-plugin-kyverno-policy-reports` displays Kyverno PolicyReport results for Kubernetes workloads on entity pages. ```typescript // packages/app/src/components/catalog/EntityPage.tsx import { KyvernoPolicyReportsTable, KyvernoOverviewCard, KyvernoCrossplanePolicyReportsTable, KyvernoCrossplaneOverviewCard, } from '@terasky/backstage-plugin-kyverno-policy-reports'; // Standard workloads: overview card + full table const overviewContent = ( {/* pass/warn/fail summary */} ); const serviceEntityPage = ( ); // Crossplane entities: use Crossplane-specific variants const crossplaneEntityPage = ( ); ``` --- ## DevPod Plugin — "Open in DevPod" Card `@terasky/backstage-plugin-devpod` adds a development environment launcher card to the entity overview page. ```typescript // packages/app/src/components/catalog/EntityPage.tsx import { DevpodComponent, isDevpodAvailable } from '@terasky/backstage-plugin-devpod'; const overviewContent = ( {/* IDE selector + "Open in DevPod" button + CLI command display */} ); ``` ```yaml # Entity annotation to enable DevPod for a component # (required on the catalog entity for isDevpodAvailable to return true) metadata: annotations: terasky.backstage.io/devpod-enabled: "true" ``` --- ## GitOps Manifest Updater — Scaffolder Field Extension `@terasky/backstage-plugin-gitops-manifest-updater` provides a `GitOpsManifestUpdater` scaffolder field extension that reads a CRD/XRD OpenAPI schema from a Git-stored manifest URL and renders a dynamic form, then creates a GitHub PR with the updated YAML. ```typescript // packages/app/src/App.tsx (legacy system registration) import { GitOpsManifestUpdaterExtension } from '@terasky/backstage-plugin-gitops-manifest-updater'; import { ScaffolderFieldExtensions } from '@backstage/plugin-scaffolder-react'; const routes = ( }> ); ``` ```yaml # Software Template using the GitOpsManifestUpdater field apiVersion: scaffolder.backstage.io/v1beta3 kind: Template metadata: name: update-kubernetes-manifest title: Update Kubernetes Manifest via GitOps spec: parameters: - title: GitOps Manifest Updater required: [gitOpsManifestUpdater] properties: gitOpsManifestUpdater: title: Update Parameters type: object ui:field: GitOpsManifestUpdater steps: - id: create-pull-request name: Create PR action: publish:github:pull-request input: repoUrl: ${{ 'github.com?owner=' + steps['parse-url'].output.result.owner + '&repo=' + steps['parse-url'].output.result.repo }} branchName: backstage-update-${{ parameters.entity }} title: Updating Kubernetes YAML for ${{ parameters.entity }} output: links: - title: Pull Request url: ${{ steps['create-pull-request'].output.remoteUrl }} # Entity annotation providing the source manifest URL: # terasky.backstage.io/source-file-url: "https://github.com/my-org/gitops/blob/main/clusters/prod/my-claim.yaml" ``` --- ## Kubernetes Resources Frontend — Dependency Graph `@terasky/backstage-plugin-kubernetes-resources-frontend` visualizes Kubernetes resource dependency graphs for catalog entities. ```yaml # app-config.yaml — proxy to the kubernetes-dependency-tracker service proxy: endpoints: '/kubernetes-resources/production': target: 'http://k8s-dependency-tracker.example.com' changeOrigin: true headers: Authorization: 'Bearer ${K8S_SA_TOKEN}' ``` ```yaml # Entity annotation wiring metadata: annotations: terasky.backstage.io/kubernetes-resource-name: 'my-deployment' terasky.backstage.io/kubernetes-resource-kind: 'Deployment' terasky.backstage.io/kubernetes-resource-api-version: 'apps/v1' terasky.backstage.io/kubernetes-resource-namespace: 'web-apps' backstage.io/managed-by-origin-location: 'production' # must match proxy cluster name ``` ```typescript // packages/app/src/components/catalog/EntityPage.tsx import { KubernetesResourcesPage, KubernetesResourceGraph, isKubernetesResourcesAvailable, } from '@terasky/backstage-plugin-kubernetes-resources-frontend'; const serviceEntityPage = ( ); ``` --- ## API Docs Module CRD — CRD API Documentation Renderer `@terasky/backstage-plugin-api-docs-module-crd` renders Backstage API entities of type `crd` with an interactive documentation viewer modelled after doc.crds.dev. ```yaml # Backstage API entity of type "crd" — registered automatically by kubernetes-ingestor # or manually via catalog-info.yaml apiVersion: backstage.io/v1alpha1 kind: API metadata: name: application-deployment-crd title: Application Deployment CRD tags: [kubernetes, crd, deployment] spec: type: crd # triggers the api-docs-module-crd renderer lifecycle: production owner: platform-team system: application-platform definition: | apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: applicationdeployments.platform.example.com spec: group: platform.example.com names: kind: ApplicationDeployment plural: applicationdeployments shortNames: [appd] scope: Namespaced versions: - name: v1 served: true storage: true schema: openAPIV3Schema: type: object required: [spec] properties: spec: type: object required: [image, replicas] properties: image: type: string description: Container image to deploy replicas: type: integer description: Number of replicas (1–100) minimum: 1 maximum: 100 default: 3 regions: type: array description: Target deployment regions items: type: string enum: [us-east-1, us-west-2, eu-west-1] ``` --- ## Educates — Training Workshop Browser `@terasky/backstage-plugin-educates` adds a `/educates` route for browsing and launching Educates training workshops. ```yaml # app-config.yaml — Educates backend configuration educates: portals: - name: platform-training url: https://training-portal.example.com accessToken: ${EDUCATES_ACCESS_TOKEN} ``` ```typescript // packages/app/src/App.tsx (legacy system) import { EducatesPage } from '@terasky/backstage-plugin-educates'; import { SchoolIcon } from '@material-ui/icons'; // Add route const routes = ( } /> ); // Add sidebar entry (packages/app/src/components/Root/Root.tsx) ``` --- ## Spring Initializer — Spring Boot Project Scaffolder Field `@terasky/backstage-plugin-spring-initializer` provides a `SpringInitializer` scaffolder field extension backed by start.spring.io. ```yaml # app-config.yaml — proxy configuration proxy: endpoints: '/spring-initializer': target: 'https://start.spring.io' changeOrigin: true springInitializer: endpoint: 'https://start.spring.io' proxyPath: '/spring-initializer' ``` ```yaml # Software Template using the SpringInitializer field apiVersion: scaffolder.backstage.io/v1beta3 kind: Template metadata: name: spring-boot-service title: Create Spring Boot Service spec: parameters: - title: Spring Boot Configuration properties: springConfig: title: Spring Configuration type: object ui:field: SpringInitializer - title: Repository Location required: [repoUrl] properties: repoUrl: type: string ui:field: RepoUrlPicker ui:options: allowedHosts: [github.com] steps: - id: generate-spring name: Generate Spring Boot Project action: terasky:spring-initializer input: type: ${{ parameters.springConfig.type }} language: ${{ parameters.springConfig.language }} bootVersion: ${{ parameters.springConfig.bootVersion }} groupId: ${{ parameters.springConfig.groupId }} artifactId: ${{ parameters.springConfig.artifactId }} name: ${{ parameters.springConfig.name }} description: ${{ parameters.springConfig.description }} packageName: ${{ parameters.springConfig.packageName }} packaging: ${{ parameters.springConfig.packaging }} javaVersion: ${{ parameters.springConfig.javaVersion }} dependencies: ${{ parameters.springConfig.dependencies }} - id: publish name: Publish to GitHub action: publish:github input: repoUrl: ${{ parameters.repoUrl }} defaultBranch: main ``` --- ## Module Federation CDN Backend — Dynamic Plugin Loading from CDN `@terasky/backstage-plugin-module-federation-cdn-backend` enables Backstage dynamic frontend plugins to be loaded directly from a CDN without copying files into `dynamic-plugins-root`. ```yaml # app-config.yaml cdn: - pluginName: "@my-org/backstage-plugin-dashboard" publicPath: "https://cdn.example.com/plugins/dashboard/" - pluginName: "@my-org/backstage-plugin-metrics" publicPath: "https://cdn.example.com/plugins/metrics/" # Versioned path for blue/green or canary deployments - pluginName: "@my-org/backstage-plugin-example" publicPath: "https://cdn.example.com/plugins/example/v1.2.3/" # Environment variable substitution - pluginName: "@my-org/backstage-plugin-other" publicPath: "${CDN_BASE_URL}/plugins/other/" ``` ```bash # Verify CDN-hosted plugins are registered after backend restart curl -s http://localhost:7007/api/dynamic-features/remotes | jq '.[].name' # Expected output: # "@my-org/backstage-plugin-dashboard" # "@my-org/backstage-plugin-metrics" # The CDN must serve mf-manifest.json at: mf-manifest.json # CORS header required: Access-Control-Allow-Origin: * ``` --- ## Frontend Extensions Explorer — NFS Introspection `@terasky/backstage-plugin-frontend-extensions-explorer` provides a live read-only introspection page for the Backstage New Frontend System extension tree. No backend plugin or network calls are required. ```typescript // The plugin reads directly from the appTreeApiRef runtime API at render time. // No manual registration is needed when using the /alpha export. // Access the page at: /frontend-extensions-explorer // Internal data source (for reference): import { useApi, appTreeApiRef } from '@backstage/frontend-plugin-api'; const { tree } = useApi(appTreeApiRef).getTree(); // tree.nodes: ReadonlyMap // Each AppNode has: // node.spec.disabled: boolean // node.instance: object | undefined (undefined = not running) // node.spec.config: object | null (applied app-config.yaml overrides) // Extension status: // spec.disabled === true → "Disabled" // spec.disabled === false && instance defined → "Enabled" // spec.disabled === false && instance missing → "Not Running" ``` --- ## Entity Scaffolder Content — Embedded Template Tab `@terasky/backstage-plugin-entity-scaffolder-content` embeds a filtered Software Template list and form directly on an entity's page tab. ```typescript // packages/app/src/components/catalog/EntityPage.tsx import { EntityScaffolderContent, isScaffolderAvailable, } from '@terasky/backstage-plugin-entity-scaffolder-content'; import { stringifyEntityRef } from '@backstage/catalog-model'; const serviceEntityPage = ( template.metadata?.labels?.target === 'component', }, ]} buildInitialState={entity => ({ entity: stringifyEntityRef(entity), })} ScaffolderFieldExtensions={ } /> ); ``` --- TeraSky's Backstage Plugins are purpose-built for platform engineering teams managing Kubernetes-heavy, multi-cloud environments. The primary pattern is to install the `kubernetes-ingestor` backend plugin once and let it automatically populate the catalog with hundreds of entities — standard workloads, Crossplane XRD/Claim pairs, KRO RGDs/Instances, and generic CRDs — each linked to generated Software Templates that let developers self-serve infrastructure without leaving Backstage. Frontend plugins layer on top to add Crossplane/KRO resource graphs, Kyverno policy compliance views, ScaleOps cost recommendations, and vendor-specific cards for VCF Automation, VCF Operations, and SpectroCloud, while MCP backend plugins (Scaffolder, Catalog, RBAC) expose these capabilities to AI agents and automation tooling. Integration follows a consistent pattern: all plugins support the Backstage New Frontend System (register via `createApp({ features: [...] })`), all backend plugins use the new `backend.add(import(...))` pattern, permission-framework integration is provided out of the box and configured entirely through CSV policies or the community RBAC plugin, and configuration is expressed declaratively in `app-config.yaml` with environment variable substitution for secrets. Teams can adopt individual plugins independently — for example, starting with only the kubernetes-ingestor and api-docs-module-crd to document CRDs, then progressively adding the MCP backends to enable AI-assisted catalog queries and scaffolding — without committing to the entire plugin suite.