Customer want to integrate Azure DevOps with Red Hat Developer Hub, so that they can see the code, and the pipeline status in the Developer Hub. We will try to do that.
The whole senario includes:
- rhdh
- dev workspace
- ocp gitops
To complish the target, we will use several components:
- rhsso, for azure ad integration.
- backstage plugin
- azure devops (included in rhdh)
- azure devops auto discovery (working)
- msgraph(does not work right now)
There is a great demo in demo.redhat.com, we will try this lab based on that demo env.
try with demo.redhat.com instance:
- Red Hat Developer Hub Demo
- using the 'latest' version
- RHDH Version: 1.2.0
- Backstage Version: 1.26.5
- Upstream: janus-idp/backstage-showcase main @ e3654a5e
- Midstream: gitlab.cee.redhat.com/rhidp/rhdh rhdh-1-rhel-9 @ cd137ad6
- using the 'latest' version
The document of this demo is here.
The github repo used for integrated gitlab:
- https://redhat-scholars.github.io/backstage-workshop/backstage-workshop/index.html
- https://github.com/redhat-gpte-devopsautomation/janus-idp-gitops
- https://github.com/redhat-gpte-devopsautomation/software-templates/tree/main
After the demo env startup, we need to customize the backstage plugin, to enable the azure devops plugin.
Before we can setup the plugin in rhdh, we need to setup the azure devops env.
create azure devops service in :
create a personal access token:
Try to create the personal access token in all organization, this will elimate the permission issue.
Install Code Search Feature for Azure devops
create a repo under your organization, and project. The content of the repo does matter for this lab, because it contains catalog for demo system, components, and api.
Find your tenant id, client id, and client secret
Create an application, then you can get the client id
Set correct permission
Set searchable branch.
In this lab, RHDP is gitopts installed, so find in gitlab, and change the content, both plugins and integrations.
Apply below content to the backstage-values.yaml, do not delete existed, just add. Not all config is correct, I am still working on it. Replace the secure key with your own ones.
Tip
get checksum with npm info
data:
dynamic-plugins.yaml: |
includes:
- dynamic-plugins.default.yaml
plugins:
- disabled: false
package: ./dynamic-plugins/dist/backstage-plugin-azure-devops
- disabled: false
package: ./dynamic-plugins/dist/backstage-plugin-azure-devops-backend-dynamic
# - disabled: false
# package: ./dynamic-plugins/dist/backstage-plugin-scaffolder-backend-module-azure-dynamic
# - disabled: false
# integrity: >-
# sha512-WxRXsTppHKxzMHpUvEiQR3rYPypSHDHABAqegjareHYEXgA5uVBsRW2zES6GpOeei45KnxGL+NcuoKQezg1D7A==
# package: '@backstage/[email protected]'
# - disabled: false
# integrity: >-
# sha512-wHZC7riqyakSzPrxM1+edu1Et99Q0gAd0WXxrnclUo7lT45+xvqYxzbdVR9Kr7OHr/6AugMghJZV1BzCxl2+PQ==
# package: '@backstage/[email protected]'
- disabled: false
integrity: >-
sha512-H3d4UThnU+EUCFfH3lBPvm0mYXdAQ/GG4blg71Oe8nfjm9eN9yATxq8r74430Xyi1xn+2HVbVbLyvWpgpIp/ig==
package: '@backstage/[email protected]'
- disabled: false
integrity: >-
sha512-C7qhlHOQeXMNMPekgEoTdTiVq2hHdZkHvUHpb4EyCOE8MzGFx1LTl7r7ch4jiFkr15YQuqOImYUc/JhGNnes8A==
package: '@backstage/[email protected]'
# - disabled: false
# integrity: >-
# sha512-eBfl2rPN3HrgECEeHS9uw9Y4xaAQgzNu7qn/kYarqTRi3Rnn5V8zMm5jU4gcqfcxdBbdpUb9HpRvOqk9V96VSA==
# package: '@backstage/[email protected]'
upstream:
backstage:
extraEnvVars:
- name: AZURE_CLIENT_ID
value: <change me to secret value>
- name: AZURE_CLIENT_SECRET
value: <change me to secret value>
- name: AZURE_TENANT_ID
value: <change me to secret value>
- name: AZURE_TOKEN
value: <change me to secret value>
- name: AZURE_ORG
value: wangzheng422
- name: KEYCLOAK_BASE_URL
value: https://keycloak-backstage.apps.cluster-wzjhs.sandbox2376.opentlc.com/auth
- name: KEYCLOAK_LOGIN_REALM
value: backstage
- name: KEYCLOAK_REALM
value: backstage
- name: KEYCLOAK_CLIENT_ID
valueFrom:
secretKeyRef:
key: CLIENT_ID
name: keycloak-client-secret-backstage
- name: KEYCLOAK_CLIENT_SECRET
valueFrom:
secretKeyRef:
key: CLIENT_SECRET
name: keycloak-client-secret-backstage
appConfig:
integrations:
azure:
- host: dev.azure.com
credentials:
- organizations:
- ${AZURE_ORG}
personalAccessToken: ${AZURE_TOKEN}
catalog:
locations:
- target: https://dev.azure.com/wangzheng422/demo/_git/service-demo?path=%2Forg.yaml&version=GBmain&_a=contents
# can be git url from azure devops repo
# target: https://dev.azure.com/wangzheng422/demo/_git/service-demo?path=%2Forg.yaml&version=GBmain&_a=contents
# https://github.com/wangzheng422/backstage-customize/blob/data/org.yaml
type: url
rules:
- allow: [Group, User]
- target: https://github.com/redhat-developer/red-hat-developer-hub-software-templates/blob/main/templates/azure/dotnet-frontend/template.yaml
type: url
rules:
- allow: [Template]
S
providers:
azureDevOps:
yourProviderId: # identifies your dataset / provider independent of config changes
organization: wangzheng422
project: '*'
repository: '*' # this will match all repos starting with service-*
path: /catalog-info.yaml
schedule: # optional; same options as in TaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
timeout: { minutes: 3 }
microsoftGraphOrg:
default:
tenantId: ${AZURE_TENANT_ID}
clientId: ${AZURE_CLIENT_ID}
clientSecret: ${AZURE_CLIENT_SECRET}
user:
filter: accountEnabled eq true and userType eq 'member'
select: [id,mail,displayName,givenName,mobilePhone]
group:
filter: >
securityEnabled eq false
and mailEnabled eq true
and groupTypes/any(c:c+eq+'Unified')
schedule:
frequency: PT1H
timeout: PT50M
keycloakOrg:
default:
baseUrl: ${KEYCLOAK_BASE_URL}
loginRealm: ${KEYCLOAK_LOGIN_REALM}
realm: ${KEYCLOAK_REALM}
clientId: ${KEYCLOAK_CLIENT_ID}
clientSecret: ${KEYCLOAK_CLIENT_SECRET}
schedule: # optional; same options as in TaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 1 }
# supports ISO duration, "human duration" as used in code
timeout: { minutes: 1 }
initialDelay: { seconds: 15 }
enabled:
azure: true
azureDevOps: true
microsoftGraphOrg: false
keycloakOrg: true
microsoft: false
In gitlab webconsole, change the content and commit
Then, from gitops operator view, update backstage.app.valueFile to the commited raw file url.
Wait sometime, you can see the plugin is enable in RHDP
Import the resources, with reference to azure devops, with the auto discovery you can see the components, system, and api.
For API
from the detail view, you can see the dependency relationship.
and you can see the azure devops repo's pull request
and the pipeline status
and the source code.
For components
For system
Because the msgraph does not work right now, you can see new group created, without user and the desire groups.
For users, we can only see the user from git source code.
Here is the catalog-info.yaml, for your referece
---
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-system
apiVersion: backstage.io/v1alpha1
kind: System
metadata:
name: azure-examples
annotations:
dev.azure.com/project-repo: demo/service-demo
dev.azure.com/host-org: dev.azure.com/wangzheng422
spec:
owner: azure-guests
---
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-component
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: azure-example-website
annotations:
dev.azure.com/project-repo: demo/service-demo
dev.azure.com/host-org: dev.azure.com/wangzheng422
spec:
type: azure-website
lifecycle: experimental
owner: azure-guests
system: azure-examples
providesApis: [azure-example-grpc-api]
---
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-api
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
name: azure-example-grpc-api
annotations:
dev.azure.com/project-repo: demo/service-demo
dev.azure.com/host-org: dev.azure.com/wangzheng422
spec:
type: grpc
lifecycle: experimental
owner: azure-guests
system: azure-examples
definition: |
syntax = "proto3";
service Exampler {
rpc Example (ExampleMessage) returns (ExampleMessage) {};
}
message ExampleMessage {
string example = 1;
};
Here is the org.yaml, for your reference
---
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-user
apiVersion: backstage.io/v1alpha1
kind: User
metadata:
name: azure-guest
spec:
memberOf: [azure-guests]
---
# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-group
apiVersion: backstage.io/v1alpha1
kind: Group
metadata:
name: azure-guests
spec:
type: team
children: []
It seems, you can disable azure devops auto discovery, and add the resource from github manually. The source code just the same.
We will use a repo host on github right now. The demo repo is here:
And you can see the result
And you can see there is already a merge request existed in the azure devops repo.
The demo.redhat.com 's demo lab, using rhsso with rhdp as the identity provider, so we can use the same way to integrate with azure ad. You can use this method, if the org import(plugin msgraph) from azure do not work.
reference:
Register a new app
Here you can see the new client id,
Create secret for the app
Set the expired date.
Set permission to read user info.
Add additional information to token claim, espeically the groups.
Get endpoint information:
add azure ad sso integration
Give it a name.
Copy the endpoint config from azure
Get group name and group id from azure entra
Add group to keycloak/rhsso manually, we do not know how to add it automatically.
Add group mapper, so the new user will be added to its group by matching the group id.
Add username mapping to oid, the rhdh will not create user entity for email address format
Get the redirect url setting from rhsso.
Add the redirect url to azure
Easy the security setting for your azure org, we are testing env, so the user can login only with password.
Open rhdh url, it will redirect to rhsso, select Azure
Login with azure account in your org.
After login, you will be redirected to rhdh, your user info is automaticalty set, because we defined a client scope and mapping for you.
Wait some moments, after rhdh sync user and group info from rhsso/keycloak, you can see the new user created, and link to sso user info.
If you login using an azure user without a group, you will see this:
The msgraph plugin does not work with azure/microsoft sso right now, the msgraph will import user with azure email as rhdh username, but azure/microsoft sso will use azure user oid as rhdh username, they are not match, so we need to hack it.
The upstream backstage have ways to match the user using azure oid, but we are rhdh, we can not cusomize the backstage plugin, so we need to hack the msgraph plugin.
We hack the msgraph using this repo. The change is simple, just use azure user oid as rhdh username.
The patch for demo.redhat.com is here:
data:
dynamic-plugins.yaml: |
plugins:
- disabled: false
package: ./dynamic-plugins/dist/backstage-plugin-azure-devops
- disabled: false
package: ./dynamic-plugins/dist/backstage-plugin-azure-devops-backend-dynamic
# - disabled: false
# package: ./dynamic-plugins/dist/backstage-plugin-scaffolder-backend-module-azure-dynamic
# - disabled: false
# integrity: >-
# sha512-WxRXsTppHKxzMHpUvEiQR3rYPypSHDHABAqegjareHYEXgA5uVBsRW2zES6GpOeei45KnxGL+NcuoKQezg1D7A==
# package: '@backstage/[email protected]'
# - disabled: false
# integrity: >-
# sha512-wHZC7riqyakSzPrxM1+edu1Et99Q0gAd0WXxrnclUo7lT45+xvqYxzbdVR9Kr7OHr/6AugMghJZV1BzCxl2+PQ==
# package: '@backstage/[email protected]'
- disabled: false
integrity: >-
sha512-H3d4UThnU+EUCFfH3lBPvm0mYXdAQ/GG4blg71Oe8nfjm9eN9yATxq8r74430Xyi1xn+2HVbVbLyvWpgpIp/ig==
package: '@backstage/[email protected]'
- disabled: false
integrity: >-
sha512-RTOzk+k0XCu0ivqqHrnis4lzsQqAhEVv++6bb4exqhU8LpWGX0R15RPljVzHtWKqT35CXhNt0JWYv8hSjUPzgg==
package: '@wangzheng422/[email protected]'
# - disabled: false
# integrity: >-
# sha512-C7qhlHOQeXMNMPekgEoTdTiVq2hHdZkHvUHpb4EyCOE8MzGFx1LTl7r7ch4jiFkr15YQuqOImYUc/JhGNnes8A==
# package: '@backstage/[email protected]'
# - disabled: false
# integrity: >-
# sha512-eBfl2rPN3HrgECEeHS9uw9Y4xaAQgzNu7qn/kYarqTRi3Rnn5V8zMm5jU4gcqfcxdBbdpUb9HpRvOqk9V96VSA==
# package: '@backstage/[email protected]'
# - disabled: false
# integrity: >-
# sha512-iRxCHis0E2CemuEQ/CQvk9O5vVw3dRA/EOLvo4Ms1scfFDdJqogHH+KiVzEOf5nhf3YUmPpMT0cB+G4kx+th9A==
# package: '@backstage/[email protected]'
upstream:
backstage:
# image:
# registry: quay.io
# repository: wangzheng422/qimgs
# tag: 'rhdh-hub-rhel9-1.1-2024.05.17.v02'
extraEnvVars:
- name: AZURE_CLIENT_ID
value: <change me to secret value>
- name: AZURE_CLIENT_SECRET
value: <change me to secret value>
- name: AZURE_TENANT_ID
value: <change me to secret value>
- name: AZURE_TOKEN
value: <change me to secret value>
- name: AZURE_ORG
value: wangzheng422
# - name: KEYCLOAK_BASE_URL
# value: https://keycloak-backstage.apps.cluster-qjwdr.sandbox928.opentlc.com/auth
- name: KEYCLOAK_BASE_URL
value: $(APP_CONFIG_catalog_providers_keycloakOrg_default_baseUrl)
- name: KEYCLOAK_LOGIN_REALM
value: backstage
- name: KEYCLOAK_REALM
value: backstage
- name: KEYCLOAK_CLIENT_ID
valueFrom:
secretKeyRef:
key: CLIENT_ID
name: keycloak-client-secret-backstage
- name: KEYCLOAK_CLIENT_SECRET
valueFrom:
secretKeyRef:
key: CLIENT_SECRET
name: keycloak-client-secret-backstage
appConfig:
integrations:
azure:
- host: dev.azure.com
credentials:
- organizations:
- ${AZURE_ORG}
personalAccessToken: ${AZURE_TOKEN}
# clientId: ${AZURE_CLIENT_ID}
# clientSecret: ${AZURE_CLIENT_SECRET}
# tenantId: ${AZURE_TENANT_ID}
auth:
environment: production
providers:
microsoft:
production:
clientId: ${AZURE_CLIENT_ID}
clientSecret: ${AZURE_CLIENT_SECRET}
tenantId: ${AZURE_TENANT_ID}
domainHint: ${AZURE_TENANT_ID}
additionalScopes:
- Mail.Send
signIn:
resolvers:
# typically you would pick one of these
- resolver: idMatchingUserEntityAnnotation
- resolver: emailMatchingUserEntityProfileEmail
- resolver: emailLocalPartMatchingUserEntityName
- resolver: emailMatchingUserEntityAnnotation
signInPage: microsoft
catalog:
locations:
# https://dev.azure.com/wangzheng422/demo/_git/service-demo?path=%2Forg.yaml&version=GBmain&_a=contents
# https://github.com/wangzheng422/backstage-customize/blob/data/org.yaml
- target: https://dev.azure.com/wangzheng422/demo/_git/service-demo?path=%2Forg.yaml&version=GBmain&_a=contents
type: url
rules:
- allow: [Group, User]
- target: https://github.com/wangzheng422/red-hat-developer-hub-software-templates/blob/wzh-hack/templates/azure/dotnet-frontend/template.yaml
type: url
rules:
- allow: [Template]
providers:
azureDevOps:
yourProviderId: # identifies your dataset / provider independent of config changes
organization: wangzheng422
project: '*'
repository: '*' # this will match all repos starting with service-*
path: /catalog-info.yaml
schedule: # optional; same options as in TaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
timeout: { minutes: 3 }
microsoftGraphOrg:
default:
tenantId: ${AZURE_TENANT_ID}
clientId: ${AZURE_CLIENT_ID}
clientSecret: ${AZURE_CLIENT_SECRET}
user:
filter: >
accountEnabled eq true and userType eq 'member'
# select: ['id', 'displayName', 'mail']
# userPrincipalName eq '[email protected]'
group:
filter: >
displayName eq 'demo-group-backstage'
schedule:
frequency: PT1H
timeout: PT50M
keycloakOrg:
default:
baseUrl: ${KEYCLOAK_BASE_URL}
loginRealm: ${KEYCLOAK_LOGIN_REALM}
realm: ${KEYCLOAK_REALM}
clientId: ${KEYCLOAK_CLIENT_ID}
clientSecret: ${KEYCLOAK_CLIENT_SECRET}
schedule: # optional; same options as in TaskScheduleDefinition
# supports cron, ISO duration, "human duration" as used in code
frequency: { minutes: 1 }
# supports ISO duration, "human duration" as used in code
timeout: { minutes: 1 }
initialDelay: { seconds: 15 }
enabled:
kubernetes: true
techdocs: true
argocd: true
sonarqube: false
keycloak: false # true -> false
ocm: true
github: false
githubOrg: false
gitlab: true
jenkins: false
permission: false
azure: true
azureDevOps: true
microsoftGraphOrg: true
keycloakOrg: false
microsoft: true
azureEasyAuth: false
service:
ports:
backend: 4180
targetPort: backend
It turn out the hack works, the user is created with azure oid as username.
- https://cloud.redhat.com/experts/idp/azuread-red-hat-sso/
- https://cloud.redhat.com/experts/idp/
- https://cloud.redhat.com/experts/
There is a golden path template for backstage, you can use it to create a new backstage app.
- https://github.com/redhat-developer/red-hat-developer-hub-software-templates
- https://github.com/wangzheng422/red-hat-developer-hub-software-templates/blob/main/templates/azure/dotnet-frontend/template.yaml
we will use this template for test:
New repo created in azure devops:
Go back to rhdh, we can see new component created.
Add user to org and project.
Login using user in the org.
Set your personal access token
Copy the token generate
Access the dev space in openshift
There is only 4 provider right now
import and create the workspace
The devfile generated
schemaVersion: 2.1.0
metadata:
attributes:
metadata-name-field: generateName
metadata-name-original-value: dummy-repo-01
name: dummy-repo-01
namespace: user1-devspaces
attributes:
che-theia.eclipse.org/sidecar-policy: mergeImage
projects:
- attributes: {}
name: dummy-repo-01
git:
remotes:
origin: https://dev.azure.com/wangzheng422/demo/_git/dummy-repo-01
components:
- name: universal-developer-image
container:
image: registry.redhat.io/devspaces/udi-rhel8@sha256:022cc606ec53638f7079a638f0810fee3a1717b42426bcfa31c2ba2e78520c54
commands: []
You can see the code editor webUI now.
make some change to see whether git push
works.
git push
ok
- The dev space link can be integrated into rhdh, we will show how to later.
- integrate with azure ad/sso
The offical document is here
For our azure devops env, we can not link it to auzre ad/entra right now, because subscription/license issue. So we can not test the sso integration now.