Harmonix on AWS logo

Backstage Harmonix on AWS Plugin

Created by Amazon Web Services

Harmonix on AWS is an open source platform built on Backstage. It helps teams build environments and applications on AWS without deep cloud skills. Think of it as a bridge between your Backstage portal and AWS services, with a clear model for apps, environments, and shared resources.

The Harmonix on AWS backstage plugin brings that model into your own portal. The backend talks to AWS using the SDK and to your git provider, such as GitLab or GitHub. The frontend adds AWS aware pages and cards so engineers can see apps, environments, and resources in one place. A common package shares types across the stack. Scaffolder actions let your templates create and manage AWS resources. A catalog processor understands custom kinds for AWS environments and providers, so the catalog stays in sync with what runs in the cloud.

Typical use cases include creating a new app from a template, deploying it to an ECS or EKS cluster or a serverless runtime, and viewing its status next to related resources. You can standardize patterns across teams, wire in CI and CD, and give developers a simple path to logs, config, and relationships.

Installation Instructions

These instructions apply to self-hosted Backstage only.

Install the frontend package

Run this from your Backstage root

Copy
yarn add --cwd packages/app @aws/[email protected]

Show AWS pages in the app routes

Edit packages/app/src/App.tsx

Copy
// packages/app/src/App.tsx

[imports...]
+import { oktaAuthApiRef } from '@backstage/core-plugin-api';
+import { AppCatalogPage } from '@aws/plugin-aws-apps-for-backstage';
+import { HarmonixHomePage } from '@aws/plugin-aws-apps-for-backstage/src/demo/HarmonixHomePage/HarmonixHomePage';

[...]

const app = createApp({
   apis,
     });
   },
   components: {
-    SignInPage: props => <SignInPage {...props} auto providers={['guest']} />,
+    // SignInPage: props => <SignInPage {...props} auto providers={['guest']} />,
+    SignInPage: props => {
+      return (
+        <SignInPage
+          {...props}
+          auto
+          providers={[
+            {
+              id: 'okta-auth-provider',
+              title: 'Okta',
+              message: 'Sign in using Okta credentials',
+              apiRef: oktaAuthApiRef,
+            },
+          ]}
+        />
+      );
+    },
   },
 });

 [...]

const routes = (
  <FlatRoutes>
    [other Route configurations...]
-    <Route path="/" element={<Navigate to="catalog" />} />
+    <Route path="/" element={<Navigate to="home" />} />
+    <Route path="/home" element={<HarmonixHomePage/>} />
+   <Route path="/aws-apps-search-page" element={<CatalogIndexPage />}>
+     <AppCatalogPage kind="all" />
+   </Route>
+   <Route path="/aws-apps-search-page/environments" element={<CatalogIndexPage />}>
+     <AppCatalogPage kind="awsenvironment" />
+   </Route>
+   <Route path="/aws-apps-search-page/providers" element={<CatalogIndexPage />}>
+     <AppCatalogPage kind="awsenvironmentprovider" />
+   </Route>
+   <Route path="/aws-apps-search-page/apps" element={<CatalogIndexPage />}>
+     <AppCatalogPage kind="component" />
+   </Route>
+   <Route path="/aws-apps-search-page/resources" element={<CatalogIndexPage />}>
+     <AppCatalogPage kind="resource" />
+   </Route>
  </FlatRoutes>
);

Add AWS items to the sidebar

Edit packages/app/src/components/Root/Root.tsx

Copy
// packages/app/src/components/Root/Root.tsx

[imports...]
SidebarSpace,
   useSidebarOpenState,
   Link,
+  SidebarSubmenu,
+  SidebarSubmenuItem,
+  CatalogIcon,
 } from '@backstage/core-components';
 import MenuIcon from '@material-ui/icons/Menu';
 import SearchIcon from '@material-ui/icons/Search';
 import { MyGroupsSidebarItem } from '@backstage/plugin-org';
 import GroupIcon from '@material-ui/icons/People';
+import CloudIcon from '@material-ui/icons/Cloud';
+import { useApp } from '@backstage/core-plugin-api';

[...]
export const Root = ({ children }: PropsWithChildren<{}>) => (
  <SidebarPage>
    <Sidebar>
      <SidebarLogo />
      <SidebarGroup label="Menu" icon={<MenuIcon />}>
 -        <SidebarItem icon={HomeIcon} to="catalog" text="Home" />
+        <SidebarItem icon={HomeIcon} to="/home" text="Home" />
+        <SidebarItem icon={CatalogIcon} to="/catalog" text="Catalog" />
+        <SidebarDivider />
+        <SidebarGroup label="AWS" icon={<MenuIcon />}>
+          <SidebarItem icon={CloudIcon} text="AWS">
+            <SidebarSubmenu title="AWS Catalog">
+              <SidebarSubmenuItem
+                title="Environments"
+                to="aws-apps-search-page/environments?filters[kind]=awsenvironment"
+                icon={useApp().getSystemIcon('kind:domain')}
+              />
+              <SidebarSubmenuItem
+                title="Providers"
+                to="aws-apps-search-page/providers?filters[kind]=awsenvironmentprovider"
+                icon={useApp().getSystemIcon('kind:system')}
+              />
+              <SidebarSubmenuItem
+                title="Apps"
+                to="aws-apps-search-page/apps?filters[kind]=component"
+                icon={useApp().getSystemIcon('kind:component')}
+              />
+              <SidebarSubmenuItem
+                title="Resources"
+                to="aws-apps-search-page/resources?filters[kind]=resource"
+                icon={useApp().getSystemIcon('kind:resource')}
+              />
+            </SidebarSubmenu>
+          </SidebarItem>
+        </SidebarGroup>
        <SidebarItem icon={LibraryBooks} to="docs" text="Docs" />
        <SidebarItem icon={CreateComponentIcon} to="create" text="Create..." />
        {/* End global nav */}
      </SidebarGroup>
      <SidebarGroup label="Settings" icon={<UserSettingsSignInAvatar />} to="/settings">
        <SidebarSettings />
      </SidebarGroup>
    </Sidebar>
    {children}
  </SidebarPage>
);

Customize the Entity page for AWS entities

Edit packages/app/src/components/catalog/EntityPage.tsx

Copy
// packages/app/src/components/catalog/EntityPage.tsx

[imports...]

   isOrphan,
   hasRelationWarnings,
   EntityRelationWarning,
+  isResourceType,
 } from '@backstage/plugin-catalog';
 import {
   EntityUserProfileCard,
   EntityCatalogGraphCard,
 } from '@backstage/plugin-catalog-graph';
 import {
+  Entity,
   RELATION_API_CONSUMED_BY,
   RELATION_API_PROVIDED_BY,
   RELATION_CONSUMES_API,
   RELATION_PROVIDES_API,
 } from '@backstage/catalog-model';
 
+import {
+  AwsEnvironmentPage,
+  AwsEnvironmentProviderPage,
+  AwsComponentPage,
+} from '@aws/plugin-aws-apps-for-backstage';
+
+import { isGitlabAvailable, EntityGitlabContent } from '@immobiliarelabs/backstage-plugin-gitlab';
+
+import {
+  EntityGithubActionsContent,
+  isGithubActionsAvailable,
+} from '@backstage-community/plugin-github-actions';
+
+const isCicdApplicable = (entity: Entity) => {
+  return isGitlabAvailable(entity) || isGithubActionsAvailable(entity);
+}
+



[...]
         <EntityGithubActionsContent />
       </EntitySwitch.Case>
      */}
+    <EntitySwitch.Case if={isGithubActionsAvailable}>
+      <EntityGithubActionsContent />
+    </EntitySwitch.Case>
+    <EntitySwitch.Case if={isGitlabAvailable}>
+      <EntityGitlabContent />
+    </EntitySwitch.Case>
     <EntitySwitch.Case>
       <EmptyState
         title="No CI/CD available for this entity"
[...]
       {overviewContent}
     </EntityLayout.Route>
 
+    <EntityLayout.Route path="/ci-cd" title="CI/CD" if={isCicdApplicable}>
+      {cicdContent}
+    </EntityLayout.Route>
+
     <EntityLayout.Route path="/docs" title="Docs">
       {techdocsContent}
     </EntityLayout.Route>
[...]
       {serviceEntityPage}
     </EntitySwitch.Case>
 
+    <EntitySwitch.Case if={isComponentType('aws-app')}>
+      <AwsComponentPage componentType='aws-app' />
+    </EntitySwitch.Case>
+
     <EntitySwitch.Case if={isComponentType('website')}>
       {websiteEntityPage}
     </EntitySwitch.Case>
[...]
   </EntityLayout>

   +const awsEnvironmentProviderEntityPage = (
+  <AwsEnvironmentProviderPage />
+);
+
+const awsEnvironmentEntityPage = (
+  <AwsEnvironmentPage />
+);
+
+const resourceEntityPage = (
+  <EntitySwitch>
+    <EntitySwitch.Case if={isResourceType('aws-resource')}>
+      <AwsComponentPage componentType='aws-resource' />
+    </EntitySwitch.Case>
+    <EntitySwitch.Case>{defaultEntityPage}</EntitySwitch.Case>
+  </EntitySwitch>
+);

export const entityPage = (
   <EntitySwitch>
     <EntitySwitch.Case if={isKind('component')} children={componentPage} />
+    <EntitySwitch.Case if={isKind('resource')} children={resourceEntityPage} />
+    <EntitySwitch.Case if={isKind('awsenvironment')} children={awsEnvironmentEntityPage} />
+    <EntitySwitch.Case if={isKind('awsenvironmentprovider')} children={awsEnvironmentProviderEntityPage} />
     <EntitySwitch.Case if={isKind('api')} children={apiPage} />
     <EntitySwitch.Case if={isKind('group')} children={groupPage} />
     <EntitySwitch.Case if={isKind('user')} children={userPage} />

This makes the AWS catalog pages and the AWS entity views visible in the app

Install the backend services package new backend

Run this from your Backstage root

Copy
yarn add --cwd packages/backend @aws/[email protected]

Wire it into the backend entry that uses the new backend system

Copy
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();

backend.add(import('@aws/plugin-aws-apps-backend-for-backstage'));

backend.start();

Note that the common Harmonix package is a dependency of these packages. Installing the frontend and backend packages pulls it in

Install the Scaffolder actions new backend

Run this from your Backstage root

Copy
yarn add --cwd packages/backend @aws/[email protected]

Register the module in the backend entry

Copy
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';

const backend = createBackend();

backend.add(import('@aws/plugin-aws-apps-backend-for-backstage'));
backend.add(import('@aws/plugin-scaffolder-backend-aws-apps-for-backstage'));

backend.start();

After this your software templates can use the new actions. You can browse the actions list at the create actions endpoint in your app

Old backend notes

The backend steps above use the new backend system in Backstage. The plugin docs do not include legacy registration code. If your backend still uses the old ServiceBuilder pattern you need to migrate that entry to the new backend system to use these modules without custom glue code

Changelog

This changelog is produced from commits made to the Harmonix on AWS plugin since 6 months ago, and based on the code located here. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.

Bug Fixes

  • Fix homepage export #166 5 months ago

Maintenance

  • Upgrade to v1.38 and other updates #165 5 months ago

Set up Backstage in minutes with Roadie