Add workload cluster service (#62865)

* feat(lib/services): add WorkloadCluster interfaces

* feat(lib/auth): add WorkloadCluster service

* feat(api/client): support WorkloadClusters

* feat(lib/services): add local workload cluster

* feat(lib/services): add workload cluster to preset editor role

* gen: run make dump-preset-roles

* feat(lib/services): support parsing workload cluster events

* feat(lib/auth): start non-cloud workload cluster service

* feat(lib/auth): initialize local workloadcluster service

* feat(web/packages): add WorkloadCluster
This commit is contained in:
Dustin Specker
2026-01-28 16:35:22 +00:00
committed by GitHub
parent 1468cfe3fa
commit d1ca8ddee0
15 changed files with 517 additions and 0 deletions
+71
View File
@@ -110,6 +110,7 @@ import (
userspb "github.com/gravitational/teleport/api/gen/proto/go/teleport/users/v1"
usertaskv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/usertasks/v1"
"github.com/gravitational/teleport/api/gen/proto/go/teleport/vnet/v1"
workloadclusterv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
workloadidentityv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1"
userpreferencespb "github.com/gravitational/teleport/api/gen/proto/go/userpreferences/v1"
"github.com/gravitational/teleport/api/internalutils/stream"
@@ -6118,3 +6119,73 @@ func (c *Client) CreateAppSessionWithJWT(ctx context.Context, req *appauthconfig
}
return res.GetSession(), nil
}
// WorkloadClustersClient returns an [workloadclusterv1.WorkloadClusterServiceClient].
func (c *Client) WorkloadClustersClient() workloadclusterv1.WorkloadClusterServiceClient {
return workloadclusterv1.NewWorkloadClusterServiceClient(c.conn)
}
// ListWorkloadClusters returns a list of WorkloadClusters.
func (c *Client) ListWorkloadClusters(ctx context.Context, pageSize int, nextToken string) ([]*workloadclusterv1.WorkloadCluster, string, error) {
resp, err := c.WorkloadClustersClient().ListWorkloadClusters(ctx, &workloadclusterv1.ListWorkloadClustersRequest{
PageSize: int32(pageSize),
PageToken: nextToken,
})
if err != nil {
return nil, "", trace.Wrap(err)
}
return resp.Clusters, resp.NextPageToken, nil
}
// CreateWorkloadCluster creates a new WorkloadCluster.
func (c *Client) CreateWorkloadCluster(ctx context.Context, req *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
resp, err := c.WorkloadClustersClient().CreateWorkloadCluster(ctx, &workloadclusterv1.CreateWorkloadClusterRequest{
Cluster: req,
})
if err != nil {
return nil, trace.Wrap(err)
}
return resp, nil
}
// GetWorkloadCluster returns a WorkloadCluster by name.
func (c *Client) GetWorkloadCluster(ctx context.Context, name string) (*workloadclusterv1.WorkloadCluster, error) {
resp, err := c.WorkloadClustersClient().GetWorkloadCluster(ctx, &workloadclusterv1.GetWorkloadClusterRequest{
Name: name,
})
if err != nil {
return nil, trace.Wrap(err)
}
return resp, nil
}
// UpdateWorkloadCluster updates an existing WorkloadCluster.
func (c *Client) UpdateWorkloadCluster(ctx context.Context, req *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
resp, err := c.WorkloadClustersClient().UpdateWorkloadCluster(ctx, &workloadclusterv1.UpdateWorkloadClusterRequest{
Cluster: req,
})
if err != nil {
return nil, trace.Wrap(err)
}
return resp, nil
}
// UpsertWorkloadCluster upserts a WorkloadCluster.
func (c *Client) UpsertWorkloadCluster(ctx context.Context, req *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
resp, err := c.WorkloadClustersClient().UpsertWorkloadCluster(ctx, &workloadclusterv1.UpsertWorkloadClusterRequest{
Cluster: req,
})
if err != nil {
return nil, trace.Wrap(err)
}
return resp, nil
}
// DeleteWorkloadCluster deletes a WorkloadCluster.
func (c *Client) DeleteWorkloadCluster(ctx context.Context, name string) error {
_, err := c.WorkloadClustersClient().DeleteWorkloadCluster(ctx, &workloadclusterv1.DeleteWorkloadClusterRequest{
Name: name,
})
return trace.Wrap(err)
}
+3
View File
@@ -699,6 +699,9 @@ const (
// KindValidatedMFAChallenge is the resource kind for validated MFA challenges.
KindValidatedMFAChallenge = "validated_mfa_challenge"
// KindWorkloadCluster is the resource kind for workload clusters.
KindWorkloadCluster = "workload_cluster"
// V8 is the eighth version of resources.
V8 = "v8"
+12
View File
@@ -1236,6 +1236,18 @@
"update",
"delete"
]
},
{
"resources": [
"workload_cluster"
],
"verbs": [
"list",
"create",
"read",
"update",
"delete"
]
}
]
},
+8
View File
@@ -637,6 +637,13 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (as *Server, err error) {
}
}
if cfg.WorkloadClusterService == nil {
cfg.WorkloadClusterService, err = local.NewWorkloadClusterService(cfg.Backend)
if err != nil {
return nil, trace.Wrap(err, "creating WorkloadClusterService")
}
}
scopedAccessCache, err := scopedaccesscache.NewCache(scopedaccesscache.CacheConfig{
Events: cfg.Events,
Reader: cfg.ScopedAccess,
@@ -706,6 +713,7 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (as *Server, err error) {
ScopedTokenService: cfg.ScopedTokenService,
AppAuthConfig: cfg.AppAuthConfig,
MFAService: cfg.MFAService,
WorkloadClusterService: cfg.WorkloadClusterService,
}
as = &Server{
+7
View File
@@ -86,6 +86,7 @@ import (
usersv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/users/v1"
usertaskv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/usertasks/v1"
vnetv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/vnet/v1"
workloadclusterv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
workloadidentityv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadidentity/v1"
userpreferencesv1pb "github.com/gravitational/teleport/api/gen/proto/go/userpreferences/v1"
"github.com/gravitational/teleport/api/internalutils/stream"
@@ -132,6 +133,7 @@ import (
"github.com/gravitational/teleport/lib/auth/users/usersv1"
"github.com/gravitational/teleport/lib/auth/usertasks/usertasksv1"
"github.com/gravitational/teleport/lib/auth/vnetconfig/vnetconfigv1"
"github.com/gravitational/teleport/lib/auth/workloadcluster/workloadclusterv1"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/decision/decisionv1"
@@ -6598,6 +6600,11 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) {
}
appauthconfigv1pb.RegisterAppAuthConfigSessionsServiceServer(server, appAuthConfigSessionsSvc)
// start a workload cluster service that returns errors for all RPCs when not running on Teleport Cloud
if !modules.GetModules().Features().Cloud {
workloadclusterv1pb.RegisterWorkloadClusterServiceServer(server, workloadclusterv1.NewService())
}
return authServer, nil
}
+3
View File
@@ -438,6 +438,9 @@ type InitConfig struct {
// MFAService is the service that manages backend MFA resources.
MFAService MFAService
// WorkloadClusterService is the service that manages WorkloadClusters.
WorkloadClusterService services.WorkloadClusterService
}
// Init instantiates and configures an instance of AuthServer
+1
View File
@@ -96,6 +96,7 @@ type Services struct {
services.Summarizer
services.ScopedTokenService
MFAService
services.WorkloadClusterService
}
// MFAService defines the interface for managing MFA resources in the backend.
@@ -0,0 +1,79 @@
/*
* Teleport
* Copyright (C) 2026 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package workloadclusterv1
import (
"context"
"github.com/gravitational/trace"
"google.golang.org/protobuf/types/known/emptypb"
"github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
"github.com/gravitational/teleport/lib/services"
)
// Backend interface for manipulating WorkloadCluster resources.
type Backend interface {
services.WorkloadClusterService
}
// Service implements the gRPC API layer for the WorkloadCluster.
type Service struct {
workloadcluster.UnimplementedWorkloadClusterServiceServer
}
// NewService returns a new service that returns a license error for every RPC
func NewService() *Service {
return &Service{}
}
// GetWorkloadCluster gets the requested WorkloadCluster.
func (s *Service) GetWorkloadCluster(ctx context.Context, req *workloadcluster.GetWorkloadClusterRequest) (*workloadcluster.WorkloadCluster, error) {
return nil, requireCloud()
}
// CreateWorkloadCluster creates a new WorkloadCluster.
func (s *Service) CreateWorkloadCluster(ctx context.Context, req *workloadcluster.CreateWorkloadClusterRequest) (*workloadcluster.WorkloadCluster, error) {
return nil, requireCloud()
}
// UpdateWorkloadCluster updates the requested WorkloadCluster.
func (s *Service) UpdateWorkloadCluster(ctx context.Context, req *workloadcluster.UpdateWorkloadClusterRequest) (*workloadcluster.WorkloadCluster, error) {
return nil, requireCloud()
}
// UpsertWorkloadCluster updates or creates the requested WorkloadCluster.
func (s *Service) UpsertWorkloadCluster(ctx context.Context, req *workloadcluster.UpsertWorkloadClusterRequest) (*workloadcluster.WorkloadCluster, error) {
return nil, requireCloud()
}
// DeleteWorkloadCluster deletes the requested WorkloadCluster.
func (s *Service) DeleteWorkloadCluster(ctx context.Context, req *workloadcluster.DeleteWorkloadClusterRequest) (*emptypb.Empty, error) {
return nil, requireCloud()
}
// ListWorkloadClusters returns a list of workload clusters.
func (s *Service) ListWorkloadClusters(ctx context.Context, req *workloadcluster.ListWorkloadClustersRequest) (*workloadcluster.ListWorkloadClustersResponse, error) {
return nil, requireCloud()
}
func requireCloud() error {
return trace.AccessDenied(
"workload_cluster resources are only available for Teleport Cloud users")
}
+2
View File
@@ -279,6 +279,8 @@ func (e *EventsService) NewWatcher(ctx context.Context, watch types.Watch) (type
parser = newScopedTokenParser()
case types.KindAppAuthConfig:
parser = newAppAuthConfigParser()
case types.KindWorkloadCluster:
parser = newWorkloadClusterParser()
default:
if watch.AllowPartialSuccess {
continue
+141
View File
@@ -0,0 +1,141 @@
/*
* Teleport
* Copyright (C) 2026 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package local
import (
"context"
"strings"
"github.com/gravitational/trace"
apidefaults "github.com/gravitational/teleport/api/defaults"
workloadclusterv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/services/local/generic"
)
const (
workloadClusterPrefix = "workload_cluster"
)
// WorkloadClusterService manages [workloadclusterv1.WorkloadCluster] resources
// in the backend.
type WorkloadClusterService struct {
service *generic.ServiceWrapper[*workloadclusterv1.WorkloadCluster]
}
// NewWorkloadClusterService creates a new WorkloadClusterService.
func NewWorkloadClusterService(b backend.Backend) (*WorkloadClusterService, error) {
service, err := generic.NewServiceWrapper(
generic.ServiceConfig[*workloadclusterv1.WorkloadCluster]{
Backend: b,
ResourceKind: types.KindWorkloadCluster,
BackendPrefix: backend.NewKey(workloadClusterPrefix),
MarshalFunc: services.MarshalWorkloadCluster,
UnmarshalFunc: services.UnmarshalWorkloadCluster,
})
if err != nil {
return nil, trace.Wrap(err)
}
return &WorkloadClusterService{service: service}, nil
}
// ListWorkloadClusters returns a paginated lists of WorkloadClusters.
func (s *WorkloadClusterService) ListWorkloadClusters(ctx context.Context, pagesize int, lastKey string) ([]*workloadclusterv1.WorkloadCluster, string, error) {
r, nextToken, err := s.service.ListResources(ctx, pagesize, lastKey)
return r, nextToken, trace.Wrap(err)
}
// GetWorkloadCluster returns the specified WorkloadCluster.
func (s *WorkloadClusterService) GetWorkloadCluster(ctx context.Context, name string) (*workloadclusterv1.WorkloadCluster, error) {
r, err := s.service.GetResource(ctx, name)
return r, trace.Wrap(err)
}
// CreateWorkloadCluster creates a new WorkloadCluster.
func (s *WorkloadClusterService) CreateWorkloadCluster(ctx context.Context, workloadCluster *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
r, err := s.service.CreateResource(ctx, workloadCluster)
return r, trace.Wrap(err)
}
// UpdateWorkloadCluster updates an existing WorkloadCluster.
func (s *WorkloadClusterService) UpdateWorkloadCluster(ctx context.Context, workloadCluster *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
r, err := s.service.ConditionalUpdateResource(ctx, workloadCluster)
return r, trace.Wrap(err)
}
// UpsertWorkloadCluster upserts an existing WorkloadCluster.
func (s *WorkloadClusterService) UpsertWorkloadCluster(ctx context.Context, workloadCluster *workloadclusterv1.WorkloadCluster) (*workloadclusterv1.WorkloadCluster, error) {
r, err := s.service.UpsertResource(ctx, workloadCluster)
return r, trace.Wrap(err)
}
// DeleteWorkloadCluster removes the specified WorkloadCluster.
func (s *WorkloadClusterService) DeleteWorkloadCluster(ctx context.Context, name string) error {
err := s.service.DeleteResource(ctx, name)
return trace.Wrap(err)
}
// DeleteAllWorkloadClusters removes all WorkloadClusters.
func (s *WorkloadClusterService) DeleteAllWorkloadClusters(ctx context.Context) error {
err := s.service.DeleteAllResources(ctx)
return trace.Wrap(err)
}
func newWorkloadClusterParser() *workloadClusterParser {
return &workloadClusterParser{
baseParser: newBaseParser(backend.NewKey(workloadClusterPrefix)),
}
}
type workloadClusterParser struct {
baseParser
}
func (p *workloadClusterParser) parse(event backend.Event) (types.Resource, error) {
switch event.Type {
case types.OpDelete:
name := event.Item.Key.TrimPrefix(backend.NewKey(workloadClusterPrefix)).String()
if name == "" {
return nil, trace.NotFound("failed parsing %v", event.Item.Key.String())
}
return &types.ResourceHeader{
Kind: types.KindWorkloadCluster,
Version: types.V1,
Metadata: types.Metadata{
Name: strings.TrimPrefix(name, backend.SeparatorString),
Namespace: apidefaults.Namespace,
},
}, nil
case types.OpPut:
r, err := services.UnmarshalWorkloadCluster(event.Item.Value,
services.WithExpires(event.Item.Expires),
services.WithRevision(event.Item.Revision),
)
if err != nil {
return nil, trace.Wrap(err)
}
return types.Resource153ToLegacy(r), nil
default:
return nil, trace.BadParameter("event %v is not supported", event.Type)
}
}
+1
View File
@@ -228,6 +228,7 @@ func NewPresetEditorRole() types.Role {
types.NewRule(scopedaccess.KindScopedRoleAssignment, RW()),
types.NewRule(types.KindScopedToken, RW()),
types.NewRule(types.KindAppAuthConfig, RW()),
types.NewRule(types.KindWorkloadCluster, RW()),
},
},
},
+2
View File
@@ -303,6 +303,8 @@ func ParseShortcut(in string) (string, error) {
return types.KindRelayServer, nil
case types.KindAppAuthConfig, types.KindAppAuthConfig + "s", "aac":
return types.KindAppAuthConfig, nil
case types.KindWorkloadCluster, types.KindWorkloadCluster + "s":
return types.KindWorkloadCluster, nil
}
return "", trace.BadParameter("unsupported resource: %q - resources should be expressed as 'type/name', for example 'connector/github'", in)
}
+61
View File
@@ -0,0 +1,61 @@
/*
* Teleport
* Copyright (C) 2026 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package services
import (
"context"
"github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
)
// WorkloadClusterServiceGetter defines only read-only service methods.
type WorkloadClusterServiceGetter interface {
// GetWorkloadCluster gets the requested WorkloadCluster resource.
GetWorkloadCluster(ctx context.Context, name string) (*workloadcluster.WorkloadCluster, error)
// ListWorkloadClusters returns a WorkloadCluster page.
ListWorkloadClusters(ctx context.Context, pageSize int, pageToken string) ([]*workloadcluster.WorkloadCluster, string, error)
}
// WorkloadClusterService manges WorkloadCluster resources.
type WorkloadClusterService interface {
WorkloadClusterServiceGetter
// CreateWorkloadCluster creates the requested WorkloadCluster resource.
CreateWorkloadCluster(ctx context.Context, config *workloadcluster.WorkloadCluster) (*workloadcluster.WorkloadCluster, error)
// UpdateWorkloadCluster updates the requested WorkloadCluster resource.
UpdateWorkloadCluster(ctx context.Context, config *workloadcluster.WorkloadCluster) (*workloadcluster.WorkloadCluster, error)
// UpsertWorkloadCluster sets the requested WorkloadCluster resource.
UpsertWorkloadCluster(ctx context.Context, c *workloadcluster.WorkloadCluster) (*workloadcluster.WorkloadCluster, error)
// DeleteWorkloadCluster deletes the requested WorkloadCluster resource.
DeleteWorkloadCluster(ctx context.Context, name string) error
}
// MarshalWorkloadCluster marshals the WorkloadCluster object into a JSON byte array.
func MarshalWorkloadCluster(object *workloadcluster.WorkloadCluster, opts ...MarshalOption) ([]byte, error) {
return MarshalProtoResource(object, opts...)
}
// UnmarshalWorkloadCluster unmarshals the WorkloadCluster object from a JSON byte array.
func UnmarshalWorkloadCluster(data []byte, opts ...MarshalOption) (*workloadcluster.WorkloadCluster, error) {
return UnmarshalProtoResource[*workloadcluster.WorkloadCluster](data, opts...)
}
+125
View File
@@ -0,0 +1,125 @@
/*
* Teleport
* Copyright (C) 2026 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package services
import (
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1"
workloadclusterv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/workloadcluster/v1"
"github.com/gravitational/teleport/lib/utils"
)
func TestMarshalWorkloadClusterRoundTrip(t *testing.T) {
t.Parallel()
obj := &workloadclusterv1.WorkloadCluster{
Metadata: &headerv1.Metadata{
Name: "test-workload-cluster",
},
Spec: &workloadclusterv1.WorkloadClusterSpec{
Regions: []*workloadclusterv1.Region{
{
Name: "us-west-2",
},
},
Bot: &workloadclusterv1.Bot{
Name: "test",
},
Token: &workloadclusterv1.Token{
JoinMethod: "iam",
Allow: []*workloadclusterv1.Allow{
{
AwsAccount: "account",
AwsArn: "arn",
},
},
},
},
}
out, err := MarshalWorkloadCluster(obj)
require.NoError(t, err)
newObj, err := UnmarshalWorkloadCluster(out)
require.NoError(t, err)
require.True(t, proto.Equal(obj, newObj), "messages are not equal")
}
func TestUnmarshalWorkloadCluster(t *testing.T) {
t.Parallel()
data, err := utils.ToJSON([]byte(correctWorkloadClusterYAML))
require.NoError(t, err)
expected := &workloadclusterv1.WorkloadCluster{
Version: "v1",
Kind: "workload_cluster",
Metadata: &headerv1.Metadata{
Name: "test-workload-cluster",
Labels: map[string]string{
"env": "example",
},
},
Spec: &workloadclusterv1.WorkloadClusterSpec{
Regions: []*workloadclusterv1.Region{
{
Name: "us-west-2",
},
},
Bot: &workloadclusterv1.Bot{
Name: "test",
},
Token: &workloadclusterv1.Token{
JoinMethod: "iam",
Allow: []*workloadclusterv1.Allow{
{
AwsAccount: "account",
AwsArn: "arn",
},
},
},
},
}
obj, err := UnmarshalWorkloadCluster(data)
require.NoError(t, err)
require.True(t, proto.Equal(expected, obj), "WorkloadCluster objects are not equal")
}
const correctWorkloadClusterYAML = `
version: v1
kind: workload_cluster
metadata:
labels:
env: example
name: test-workload-cluster
spec:
regions:
- name: us-west-2
bot:
name: test
token:
join_method: iam
allow:
- aws_account: account
aws_arn: arn
`
@@ -361,6 +361,7 @@ export enum ResourceKind {
WebToken = 'web_token',
WindowsDesktop = 'windows_desktop',
WindowsDesktopService = 'windows_desktop_service',
WorkloadCluster = 'workload_cluster',
WorkloadIdentity = 'workload_identity',
// Resources that have no actual data representation, but serve for checking