Set Up JFrog Identity and Projects with Crossplane

Create users, a group, group membership, a global role, and a project with the JFrog Crossplane providers.

Use these Crossplane recipes to set up identity and projects in the JFrog Platform. Each recipe is a set of manifests that combines the JFrog Crossplane providers.

  • Recipe 1 creates a user, a platform group, the group membership, and a global role.
  • Recipe 2 creates a project, an environment, a project role, and a project membership for the group.

Prerequisites

  • The providers are installed, and ClusterProviderConfigs named jfrog exist. For more information, see Configure Credentials for the JFrog Crossplane Providers.
  • The recipes use the namespace team-payments and the project key payments. These values differ from the team-a namespace used on other pages.
📘

Note

The manifests on this page omit providerConfigRef for brevity. Every managed resource also needs providerConfigRef: {kind: ClusterProviderConfig, name: jfrog} under spec. Add it to each managed resource before you apply, or name your configurations default.

Recipe 1: Users, Group, Membership, and Global Role

Step 1: Create the Recipe 1 Manifests

Save the following manifests to a directory:

apiVersion: v1
kind: Secret
metadata: {name: user-password, namespace: team-payments}
stringData: {password: '<initial-password>'}
---
apiVersion: user.artifactory.m.crossplane.io/v1alpha1
kind: User
metadata: {name: alice, namespace: team-payments}
spec:
  initProvider:
    groups: [readers]              # initial only: GroupMembers owns membership
  forProvider:
    email: [email protected]
    profileUpdatable: true
    passwordSecretRef: {name: user-password, key: password}
---
apiVersion: access.platform.m.crossplane.io/v1alpha1
kind: Group
metadata:
  name: payments-developers
  namespace: team-payments
  labels: {app.example.com/group: payments-developers}
spec:
  forProvider: {description: Payments developers, adminPrivileges: false, autoJoin: false}
---
apiVersion: access.platform.m.crossplane.io/v1alpha1
kind: GroupMembers
metadata: {name: payments-developers, namespace: team-payments}
spec:
  forProvider:
    members: [alice]               # authoritative list
    nameSelector: {matchLabels: {app.example.com/group: payments-developers}}
---
apiVersion: access.platform.m.crossplane.io/v1alpha1
kind: GlobalRole
metadata: {name: payments-auditor, namespace: team-payments}
spec:
  forProvider:
    type: CUSTOM_GLOBAL
    environments: [DEV, PROD]
    actions: [READ_REPOSITORY, READ_BUILD]

Replace <initial-password> with the initial password for the user.

About these manifests:

  • The User sets groups under spec.initProvider, so the value is sent only at creation.
  • GroupMembers owns the group membership. If two managed resources write the same membership, they overwrite each other on every poll.
  • GroupMembers sets the group with nameSelector, which selects the platform Group by its label.
  • The GroupMembers name field stays a spec field so that it can use nameRef or nameSelector.
  • The members list is authoritative. It replaces the whole membership list on update.

Step 2: Apply the Recipe 1 Manifests

Run the following command:

kubectl apply -R -f <dir>/

To add a user later, apply the new User and append it to members. GroupMembers retries until the user exists.

Recipe 2: Project, Environment, Role, and Membership

Step 1: Create the Recipe 2 Manifests

Save the following manifests to a directory:

apiVersion: project.project.m.crossplane.io/v1alpha1
kind: Project
metadata:
  name: payments                   # project key
  namespace: team-payments
  labels: {app.example.com/project: payments}
spec:
  forProvider:
    displayName: Payments
    maxStorageInGibibytes: 10
    adminPrivileges: [{manageMembers: true, manageResources: true, indexResources: true}]
---
apiVersion: project.project.m.crossplane.io/v1alpha1
kind: Environment
metadata: {name: staging, namespace: team-payments}      # stored as payments-staging
spec:
  forProvider:
    projectKeySelector: {matchLabels: {app.example.com/project: payments}}
---
apiVersion: access.project.m.crossplane.io/v1alpha1
kind: Role
metadata: {name: payments-releaser, namespace: team-payments}
spec:
  forProvider:
    type: CUSTOM
    environments: [DEV, PROD]
    actions: [READ_REPOSITORY, ANNOTATE_REPOSITORY, CREATE_RELEASE_BUNDLE, READ_RELEASE_BUNDLE]
    projectKeySelector: {matchLabels: {app.example.com/project: payments}}
---
apiVersion: access.project.m.crossplane.io/v1alpha1
kind: Group                        # adds the existing platform group to the project
metadata: {name: payments-developers, namespace: team-payments}
spec:
  forProvider:
    roles: [Developer]
    projectKeySelector: {matchLabels: {app.example.com/project: payments}}

About these manifests:

  • The Project name is the project key.
  • The Environment, Role, and project Group select the project by its label with projectKeySelector.
  • Project Group and User names must match existing principals. The project Group in this recipe adds the platform group payments-developers from Recipe 1.
  • Built-in project roles are Developer, Contributor, Viewer, Release Manager, Security Manager, and Project Admin.

Step 2: Apply the Recipe 2 Manifests

Run the following command:

kubectl apply -R -f <dir>/

Check the Result

List every managed resource:

kubectl get managed -A

To check one Kind, run the following commands. Use the fully-qualified Kind name, for example groupmembers.access.platform.m.crossplane.io.

kubectl get <fq-kind> -n team-payments
kubectl describe <fq-kind>/<name> -n team-payments
  • The first command shows the SYNCED, READY, and EXTERNAL-NAME columns.
  • The second command shows conditions and events.

Next Steps


Did this page help you?