AWS Roles Anywhere: implement profile filters (sync and list profiles method) (#56718)

* AWS IAM Roles Anywhere: implement filters for Profile sync and lister

* move client out of request

* improve godoc for the ListRolesAnywhereProfiles api
This commit is contained in:
Marco Dinis
2025-07-24 15:47:28 +00:00
committed by GitHub
parent 23c5fa3c37
commit d12554bc83
14 changed files with 366 additions and 38 deletions
@@ -263,7 +263,8 @@ type ListRolesAnywhereProfilesRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Integration is the AWS Roles Anywhere Integration name.
Integration string `protobuf:"bytes,1,opt,name=integration,proto3" json:"integration,omitempty"`
// page_size is the size of the page to request.
// page_size is the max size of the page to request.
// Depending on the filters, the actual number of profiles returned may be less than this value.
PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
// next_page_token is the page token.
NextPageToken string `protobuf:"bytes,3,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
@@ -53,6 +53,9 @@ type AWSRolesAnywhereServiceClient interface {
// It uses the following API:
// https://docs.aws.amazon.com/rolesanywhere/latest/APIReference/API_ListProfiles.html
// https://docs.aws.amazon.com/rolesanywhere/latest/APIReference/API_ListTagsForResource.html
//
// The number of profiles returned is always between 0 and page_size.
// If the number of elements is 0, then there are no more profiles to return and the next page token is empty.
ListRolesAnywhereProfiles(ctx context.Context, in *ListRolesAnywhereProfilesRequest, opts ...grpc.CallOption) (*ListRolesAnywhereProfilesResponse, error)
}
@@ -100,6 +103,9 @@ type AWSRolesAnywhereServiceServer interface {
// It uses the following API:
// https://docs.aws.amazon.com/rolesanywhere/latest/APIReference/API_ListProfiles.html
// https://docs.aws.amazon.com/rolesanywhere/latest/APIReference/API_ListTagsForResource.html
//
// The number of profiles returned is always between 0 and page_size.
// If the number of elements is 0, then there are no more profiles to return and the next page token is empty.
ListRolesAnywhereProfiles(context.Context, *ListRolesAnywhereProfilesRequest) (*ListRolesAnywhereProfilesResponse, error)
mustEmbedUnimplementedAWSRolesAnywhereServiceServer()
}
@@ -31,6 +31,9 @@ service AWSRolesAnywhereService {
// It uses the following API:
// https://docs.aws.amazon.com/rolesanywhere/latest/APIReference/API_ListProfiles.html
// https://docs.aws.amazon.com/rolesanywhere/latest/APIReference/API_ListTagsForResource.html
//
// The number of profiles returned is always between 0 and page_size.
// If the number of elements is 0, then there are no more profiles to return and the next page token is empty.
rpc ListRolesAnywhereProfiles(ListRolesAnywhereProfilesRequest) returns (ListRolesAnywhereProfilesResponse);
}
@@ -73,7 +76,8 @@ message AWSRolesAnywherePingResponse {
message ListRolesAnywhereProfilesRequest {
// Integration is the AWS Roles Anywhere Integration name.
string integration = 1;
// page_size is the size of the page to request.
// page_size is the max size of the page to request.
// Depending on the filters, the actual number of profiles returned may be less than this value.
int32 page_size = 2;
// next_page_token is the page token.
string next_page_token = 3;
+3 -1
View File
@@ -230,7 +230,9 @@ func (s *AWSRolesAnywhereService) ListRolesAnywhereProfiles(ctx context.Context,
}
profileList, err := awsra.ListRolesAnywhereProfiles(ctx, listRolesAnywhereClient, awsra.ListRolesAnywhereProfilesRequest{
NextToken: req.GetNextPageToken(),
NextToken: req.GetNextPageToken(),
ProfileNameFilters: req.GetProfileNameFilters(),
PageSize: int(req.GetPageSize()),
})
if err != nil {
return nil, trace.Wrap(err)
@@ -100,6 +100,70 @@ func TestGenerateAWSRACredentials(t *testing.T) {
})
}
func TestAWSRolesAnywhereProfileSyncFilters(t *testing.T) {
t.Parallel()
clusterName := "test-cluster"
proxyPublicAddr := "example.com:443"
ca := newCertAuthority(t, types.AWSRACA, clusterName)
ctx, localClient, resourceSvc := initSvc(t, ca, clusterName, proxyPublicAddr)
ctx = authorizerForDummyUser(t, ctx, types.RoleSpecV6{
Allow: types.RoleConditions{Rules: []types.Rule{
{Resources: []string{types.KindIntegration}, Verbs: []string{types.VerbCreate, types.VerbUpdate}},
}},
}, localClient)
integrationWithFilters := func(t *testing.T, integrationName string, filters []string) *types.IntegrationV1 {
ig, err := types.NewIntegrationAWSRA(
types.Metadata{Name: integrationName},
&types.AWSRAIntegrationSpecV1{
TrustAnchorARN: "arn:aws:rolesanywhere:eu-west-2:123456789012:trust-anchor/12345678-1234-1234-1234-123456789012",
ProfileSyncConfig: &types.AWSRolesAnywhereProfileSyncConfig{
Enabled: true,
ProfileARN: "arn:aws:rolesanywhere:eu-west-2:123456789012:profile/12345678-1234-1234-1234-123456789012",
RoleARN: "arn:aws:iam::123456789012:role/SyncRole",
ProfileNameFilters: filters,
},
},
)
require.NoError(t, err)
return ig
}
t.Run("valid without filters", func(t *testing.T) {
ig := integrationWithFilters(t, "integration1", []string{})
_, err := resourceSvc.CreateIntegration(ctx, &integrationv1.CreateIntegrationRequest{
Integration: ig,
})
require.NoError(t, err)
})
t.Run("valid with glob filter", func(t *testing.T) {
ig := integrationWithFilters(t, "integration2", []string{"MyTeam-*"})
_, err := resourceSvc.CreateIntegration(ctx, &integrationv1.CreateIntegrationRequest{
Integration: ig,
})
require.NoError(t, err)
})
t.Run("valid with regex filter", func(t *testing.T) {
ig := integrationWithFilters(t, "integration3", []string{"^MyTeam-.*$"})
_, err := resourceSvc.CreateIntegration(ctx, &integrationv1.CreateIntegrationRequest{
Integration: ig,
})
require.NoError(t, err)
})
t.Run("invalid with invalid regex", func(t *testing.T) {
ig := integrationWithFilters(t, "integration5", []string{`^[invalid-regex{$`})
_, err := resourceSvc.CreateIntegration(ctx, &integrationv1.CreateIntegrationRequest{
Integration: ig,
})
require.Error(t, err)
})
}
func TestAWSRolesAnywherePing(t *testing.T) {
t.Parallel()
clusterName := "test-cluster"
@@ -41,6 +41,7 @@ import (
"github.com/gravitational/teleport/lib/integrations/awsra/createsession"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/utils"
)
// Cache is the subset of the cached resources that the Service queries.
@@ -254,6 +255,10 @@ func (s *Service) CreateIntegration(ctx context.Context, req *integrationpb.Crea
if errs := validation.IsDNS1035Label(req.GetIntegration().GetName()); len(errs) > 0 {
return nil, trace.BadParameter("integration name %q must be a lower case valid DNS subdomain so that it can be used to allow Web/CLI access", req.GetIntegration().GetName())
}
if err := validateAWSRolesAnywhereProfileFilters(req.Integration); err != nil {
return nil, trace.Wrap(err)
}
}
ig, err := s.backend.CreateIntegration(ctx, req.Integration)
@@ -302,6 +307,10 @@ func (s *Service) UpdateIntegration(ctx context.Context, req *integrationpb.Upda
return nil, trace.Wrap(err)
}
if err := validateAWSRolesAnywhereProfileFilters(req.Integration); err != nil {
return nil, trace.Wrap(err)
}
if err := s.maybeUpdateStaticCredentials(ctx, req.Integration); err != nil {
return nil, trace.Wrap(err)
}
@@ -341,6 +350,24 @@ func (s *Service) UpdateIntegration(ctx context.Context, req *integrationpb.Upda
return igV1, nil
}
func validateAWSRolesAnywhereProfileFilters(ig types.Integration) error {
rolesAnywhereSpec := ig.GetAWSRolesAnywhereIntegrationSpec()
if rolesAnywhereSpec == nil {
return nil
}
if rolesAnywhereSpec.ProfileSyncConfig == nil {
return nil
}
for _, profileNameFilter := range rolesAnywhereSpec.ProfileSyncConfig.ProfileNameFilters {
if _, err := utils.CompileExpression(profileNameFilter); err != nil {
return trace.BadParameter("invalid filter %q, use glob-like matching or regex by adding the anchors (eg, ^regex$): %v", profileNameFilter, err)
}
}
return nil
}
// DeleteIntegration removes the specified Integration resource.
//
// This RPC may remove multiple resources in the backend:
@@ -26,6 +26,7 @@ import (
"github.com/gravitational/trace"
integrationv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
"github.com/gravitational/teleport/lib/utils"
)
// ListRolesAnywhereProfilesRequest contains the required fields to list the Roles Anywhere Profiles in Amazon IAM.
@@ -35,6 +36,20 @@ type ListRolesAnywhereProfilesRequest struct {
NextToken string
// PageSize is the maximum number of records to retrieve.
PageSize int
// ProfileNameFilters is a list of filters applied to the profile name.
// Only matching profiles will be synchronized as application servers.
// If empty, no filtering is applied.
//
// Filters can be globs, for example:
//
// profile*
// *name*
//
// Or regexes if they're prefixed and suffixed with ^ and $, for example:
//
// ^profile.*$
// ^.*name.*$
ProfileNameFilters []string
}
// ListRolesAnywhereProfilesResponse contains a page of Roles Anywhere Profiles.
@@ -81,7 +96,12 @@ func ListRolesAnywhereProfiles(ctx context.Context, clt RolesAnywhereProfilesLis
if req.NextToken != "" {
nextToken = aws.String(req.NextToken)
}
profiles, nextPageToken, err := listRolesAnywhereProfilesPage(ctx, clt, nextToken, req.PageSize)
listReq := listRolesAnywhereProfilesRequest{
nextPage: nextToken,
pageSize: req.PageSize,
filters: req.ProfileNameFilters,
}
profiles, nextPageToken, err := listRolesAnywhereProfilesPage(ctx, clt, listReq)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -92,28 +112,64 @@ func ListRolesAnywhereProfiles(ctx context.Context, clt RolesAnywhereProfilesLis
}, nil
}
func listRolesAnywhereProfilesPage(ctx context.Context, raClient RolesAnywhereProfilesLister, nextPage *string, pageSize int) ([]*integrationv1.RolesAnywhereProfile, *string, error) {
type listRolesAnywhereProfilesRequest struct {
nextPage *string
pageSize int
filters []string
}
func listRolesAnywhereProfilesPage(ctx context.Context, raClient RolesAnywhereProfilesLister, req listRolesAnywhereProfilesRequest) (ret []*integrationv1.RolesAnywhereProfile, nextToken *string, err error) {
var pageSizeRequest *int32
if req.pageSize > 0 {
pageSizeRequest = aws.Int32(int32(req.pageSize))
}
nextPage := req.nextPage
for {
profilesListResp, err := raClient.ListProfiles(ctx, &rolesanywhere.ListProfilesInput{
NextToken: nextPage,
PageSize: pageSizeRequest,
})
if err != nil {
return nil, nil, trace.Wrap(err)
}
profilesAsProto, err := convertRolesAnywhereProfilePageToProto(ctx, profilesListResp, raClient, req)
if err != nil {
return nil, nil, trace.Wrap(err)
}
ret = append(ret, profilesAsProto...)
// If no profile was added (because of filters), and there's more resources, fetch the next page.
// This ensures that the client doesn't receive an empty page when there are more resources to fetch.
nextPage = profilesListResp.NextToken
if len(ret) == 0 && aws.ToString(nextPage) != "" {
continue
}
break
}
return ret, nextPage, nil
}
func convertRolesAnywhereProfilePageToProto(ctx context.Context, profilesListResp *rolesanywhere.ListProfilesOutput, raClient RolesAnywhereProfilesLister, req listRolesAnywhereProfilesRequest) ([]*integrationv1.RolesAnywhereProfile, error) {
var ret []*integrationv1.RolesAnywhereProfile
var pageSizeRequest *int32
if pageSize > 0 {
pageSizeRequest = aws.Int32(int32(pageSize))
}
profilesListResp, err := raClient.ListProfiles(ctx, &rolesanywhere.ListProfilesInput{
NextToken: nextPage,
PageSize: pageSizeRequest,
})
if err != nil {
return nil, nil, trace.Wrap(err)
}
for _, profile := range profilesListResp.Profiles {
matches, err := profileNameMatchesFilters(aws.ToString(profile.Name), req.filters)
if err != nil {
return nil, trace.Wrap(err)
}
if !matches {
continue
}
profileTags, err := raClient.ListTagsForResource(ctx, &rolesanywhere.ListTagsForResourceInput{
ResourceArn: profile.ProfileArn,
})
if err != nil {
return nil, nil, trace.Wrap(err)
return nil, trace.Wrap(err)
}
labels := make(map[string]string, len(profileTags.Tags))
@@ -131,5 +187,23 @@ func listRolesAnywhereProfilesPage(ctx context.Context, raClient RolesAnywherePr
})
}
return ret, profilesListResp.NextToken, nil
return ret, nil
}
func profileNameMatchesFilters(profileName string, filters []string) (bool, error) {
// If no filters are provided, all profiles match.
if len(filters) == 0 {
return true, nil
}
for _, filter := range filters {
matches, err := utils.MatchString(profileName, filter)
if err != nil {
return false, trace.Wrap(err, "error parsing filter: %s", filter)
}
if matches {
return true, nil
}
}
return false, nil
}
@@ -25,7 +25,10 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/rolesanywhere"
ratypes "github.com/aws/aws-sdk-go-v2/service/rolesanywhere/types"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
integrationv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
)
func TestListRolesAnywhereProfiles(t *testing.T) {
@@ -63,6 +66,109 @@ func TestListRolesAnywhereProfiles(t *testing.T) {
require.Len(t, resp.Profiles, 3)
}
func TestListRolesAnywhereProfilesPage(t *testing.T) {
rolesAnywhereProfileWithName := func(name string) ratypes.ProfileDetail {
return ratypes.ProfileDetail{
Name: aws.String(name),
ProfileArn: aws.String(uuid.NewString()),
}
}
for _, tt := range []struct {
name string
req listRolesAnywhereProfilesRequest
clientMock RolesAnywhereProfilesLister
expectedResp func(t *testing.T, page []*integrationv1.RolesAnywhereProfile)
expectedErrCheck require.ErrorAssertionFunc
}{
{
name: "no resources",
req: listRolesAnywhereProfilesRequest{},
clientMock: &mockIAMRolesAnywhere{},
expectedResp: func(t *testing.T, page []*integrationv1.RolesAnywhereProfile) {
require.Empty(t, page)
},
expectedErrCheck: require.NoError,
},
{
name: "some resources",
req: listRolesAnywhereProfilesRequest{},
clientMock: &mockIAMRolesAnywhere{
profilesByID: map[string]ratypes.ProfileDetail{
"id1": rolesAnywhereProfileWithName("ExampleProfile"),
},
},
expectedResp: func(t *testing.T, page []*integrationv1.RolesAnywhereProfile) {
require.Len(t, page, 1)
require.Equal(t, "ExampleProfile", page[0].Name)
},
expectedErrCheck: require.NoError,
},
{
name: "with filters using glob",
req: listRolesAnywhereProfilesRequest{
filters: []string{"TeamB-*"},
},
clientMock: &mockIAMRolesAnywhere{
profilesByID: map[string]ratypes.ProfileDetail{
"id1": rolesAnywhereProfileWithName("TeamA-subteam1"),
"id2": rolesAnywhereProfileWithName("TeamA-subteam2"),
"id3": rolesAnywhereProfileWithName("TeamA-subteam3"),
"id4": rolesAnywhereProfileWithName("TeamB-subteam1"),
"id5": rolesAnywhereProfileWithName("TeamB-subteam2"),
"id6": rolesAnywhereProfileWithName("TeamB-subteam3"),
},
},
expectedResp: func(t *testing.T, page []*integrationv1.RolesAnywhereProfile) {
require.Len(t, page, 3)
profile := page[0]
require.NotEmpty(t, profile.Arn)
require.Contains(t, profile.Name, "TeamB-subteam")
},
expectedErrCheck: require.NoError,
},
{
name: "with filters using regex",
req: listRolesAnywhereProfilesRequest{
filters: []string{`^Team[A,B]{1}\-subteam\d$`},
},
clientMock: &mockIAMRolesAnywhere{
profilesByID: map[string]ratypes.ProfileDetail{
"id1": rolesAnywhereProfileWithName("TeamA-subteam1"),
"id2": rolesAnywhereProfileWithName("TeamA-subteam2"),
"id3": rolesAnywhereProfileWithName("TeamA-subteam3"),
"id4": rolesAnywhereProfileWithName("TeamB-subteam1"),
"id5": rolesAnywhereProfileWithName("TeamB-subteam2"),
"id6": rolesAnywhereProfileWithName("TeamB-subteam3"),
"id7": rolesAnywhereProfileWithName("TeamC-subteam1"),
"id8": rolesAnywhereProfileWithName("TeamC-subteam2"),
"id9": rolesAnywhereProfileWithName("TeamC-subteam3"),
"id10": rolesAnywhereProfileWithName("AnotherTeam"),
},
},
expectedResp: func(t *testing.T, page []*integrationv1.RolesAnywhereProfile) {
require.Len(t, page, 6)
profileNames := make([]string, 0, len(page))
for _, profile := range page {
profileNames = append(profileNames, profile.Name)
}
require.Contains(t, profileNames, "TeamA-subteam1")
},
expectedErrCheck: require.NoError,
},
} {
t.Run(tt.name, func(t *testing.T) {
resp, _, err := listRolesAnywhereProfilesPage(t.Context(), tt.clientMock, tt.req)
tt.expectedErrCheck(t, err)
tt.expectedResp(t, resp)
})
}
}
type mockListRolesAnywhereProfiles struct {
profiles []ratypes.ProfileDetail
}
+4 -2
View File
@@ -80,8 +80,10 @@ func Ping(ctx context.Context, clt PingClient) (*PingResponse, error) {
profileCounter := 0
var nextToken *string
for {
const useAPIDefaultPageSize = 0
profiles, nextPageToken, err := listRolesAnywhereProfilesPage(ctx, clt, nextToken, useAPIDefaultPageSize)
listReq := listRolesAnywhereProfilesRequest{
nextPage: nextToken,
}
profiles, nextPageToken, err := listRolesAnywhereProfilesPage(ctx, clt, listReq)
if err != nil {
errs = append(errs, err)
break
+7 -2
View File
@@ -388,10 +388,15 @@ func syncProfileForIntegration(ctx context.Context, params AWSRolesAnywherProfil
return ret
}
profileNameFilters := integration.GetAWSRolesAnywhereIntegrationSpec().ProfileSyncConfig.ProfileNameFilters
var nextPage *string
for {
const useAPIDefaultPageSize = 0
profilesListResp, respNextToken, err := listRolesAnywhereProfilesPage(ctx, raClient, nextPage, useAPIDefaultPageSize)
listReq := listRolesAnywhereProfilesRequest{
nextPage: nextPage,
filters: profileNameFilters,
}
profilesListResp, respNextToken, err := listRolesAnywhereProfilesPage(ctx, raClient, listReq)
if err != nil {
ret.setupError = trace.Wrap(err)
return ret
+8 -6
View File
@@ -110,9 +110,10 @@ func (h *Handler) integrationsCreate(w http.ResponseWriter, r *http.Request, p h
}, &types.AWSRAIntegrationSpecV1{
TrustAnchorARN: req.Integration.AWSRA.TrustAnchorARN,
ProfileSyncConfig: &types.AWSRolesAnywhereProfileSyncConfig{
Enabled: req.Integration.AWSRA.ProfileSyncConfig.Enabled,
ProfileARN: req.Integration.AWSRA.ProfileSyncConfig.ProfileARN,
RoleARN: req.Integration.AWSRA.ProfileSyncConfig.RoleARN,
Enabled: req.Integration.AWSRA.ProfileSyncConfig.Enabled,
ProfileARN: req.Integration.AWSRA.ProfileSyncConfig.ProfileARN,
RoleARN: req.Integration.AWSRA.ProfileSyncConfig.RoleARN,
ProfileNameFilters: req.Integration.AWSRA.ProfileSyncConfig.ProfileNameFilters,
},
})
if err != nil {
@@ -212,9 +213,10 @@ func (h *Handler) integrationsUpdate(w http.ResponseWriter, r *http.Request, p h
spec := integration.GetAWSRolesAnywhereIntegrationSpec()
spec.TrustAnchorARN = req.AWSRA.TrustAnchorARN
spec.ProfileSyncConfig = &types.AWSRolesAnywhereProfileSyncConfig{
Enabled: req.AWSRA.ProfileSyncConfig.Enabled,
ProfileARN: req.AWSRA.ProfileSyncConfig.ProfileARN,
RoleARN: req.AWSRA.ProfileSyncConfig.RoleARN,
Enabled: req.AWSRA.ProfileSyncConfig.Enabled,
ProfileARN: req.AWSRA.ProfileSyncConfig.ProfileARN,
RoleARN: req.AWSRA.ProfileSyncConfig.RoleARN,
ProfileNameFilters: req.AWSRA.ProfileSyncConfig.ProfileNameFilters,
ProfileAcceptsRoleSessionName: spec.ProfileSyncConfig.ProfileAcceptsRoleSessionName,
}
+3 -2
View File
@@ -223,8 +223,9 @@ func (h *Handler) awsRolesAnywhereListProfiles(w http.ResponseWriter, r *http.Re
}
listResp, err := clt.IntegrationAWSRolesAnywhereClient().ListRolesAnywhereProfiles(ctx, &integrationv1.ListRolesAnywhereProfilesRequest{
Integration: integrationName,
NextPageToken: req.StartKey,
Integration: integrationName,
NextPageToken: req.StartKey,
ProfileNameFilters: req.Filters,
})
if err != nil {
return nil, trace.Wrap(err)
+6 -3
View File
@@ -171,9 +171,10 @@ func TestIntegrationsCRUDRolesAnywhere(t *testing.T) {
AWSRA: &ui.IntegrationAWSRASpec{
TrustAnchorARN: updatedTrustAnchor,
ProfileSyncConfig: ui.AWSRAProfileSync{
Enabled: true,
ProfileARN: syncProfileARN,
RoleARN: syncRoleARN,
Enabled: true,
ProfileARN: syncProfileARN,
RoleARN: syncRoleARN,
ProfileNameFilters: []string{"ExposedProfile-*"},
},
},
}
@@ -197,6 +198,8 @@ func TestIntegrationsCRUDRolesAnywhere(t *testing.T) {
require.True(t, integrationObject.AWSRA.ProfileSyncConfig.Enabled)
require.Equal(t, syncProfileARN, integrationObject.AWSRA.ProfileSyncConfig.ProfileARN)
require.Equal(t, syncRoleARN, integrationObject.AWSRA.ProfileSyncConfig.RoleARN)
require.Len(t, integrationObject.AWSRA.ProfileSyncConfig.ProfileNameFilters, 1)
require.Equal(t, "ExposedProfile-*", integrationObject.AWSRA.ProfileSyncConfig.ProfileNameFilters[0])
// Delete Integration
err = wPack.server.Auth().DeleteIntegration(ctx, integrationName)
+34 -3
View File
@@ -68,6 +68,21 @@ type AWSRAProfileSync struct {
// RoleARN is the ARN of the IAM Role that is used to sync profiles.
RoleARN string `json:"roleArn"`
// ProfileNameFilters are the filters applied to the profiles.
// Only matching profiles will be synchronized as application servers.
// If empty, no filtering is applied.
//
// Filters can be globs, for example:
//
// profile*
// *name*
//
// Or regexes if they're prefixed and suffixed with ^ and $, for example:
//
// ^profile.*$
// ^.*name.*$
ProfileNameFilters []string `json:"filters"`
}
// CheckAndSetDefaults for the aws oidc integration spec.
@@ -355,9 +370,10 @@ func MakeIntegration(ig types.Integration) (*Integration, error) {
ret.AWSRA = &IntegrationAWSRASpec{
TrustAnchorARN: spec.TrustAnchorARN,
ProfileSyncConfig: AWSRAProfileSync{
Enabled: spec.ProfileSyncConfig.Enabled,
ProfileARN: spec.ProfileSyncConfig.ProfileARN,
RoleARN: spec.ProfileSyncConfig.RoleARN,
Enabled: spec.ProfileSyncConfig.Enabled,
ProfileARN: spec.ProfileSyncConfig.ProfileARN,
RoleARN: spec.ProfileSyncConfig.RoleARN,
ProfileNameFilters: spec.ProfileSyncConfig.ProfileNameFilters,
},
}
}
@@ -699,4 +715,19 @@ type AWSRolesAnywhereListProfilesRequest struct {
// StartKey is the token to be used to fetch the next page.
// If empty, the first page is fetched.
StartKey string `json:"startKey"`
// Filters are the filters applied to the profiles.
// Only matching profiles will be synchronized as application servers.
// If empty, no filtering is applied.
//
// Filters can be globs, for example:
//
// profile*
// *name*
//
// Or regexes if they're prefixed and suffixed with ^ and $, for example:
//
// ^profile.*$
// ^.*name.*$
Filters []string `json:"filters"`
}