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
yarn add --cwd packages/app @aws/[email protected]
Show AWS pages in the app routes
Edit packages/app/src/App.tsx
// 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
// 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
// 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
yarn add --cwd packages/backend @aws/[email protected]
Wire it into the backend entry that uses the new backend system
// 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
yarn add --cwd packages/backend @aws/[email protected]
Register the module in the backend entry
// 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.
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.