AWS Roles Anywhere: profile sync summary web api (#58007)

This PR adds the AWS IAM Roles Anywhere Status fields to the already
existing endpoint which returns the integration status.
This commit is contained in:
Marco Dinis
2025-08-19 17:06:12 +00:00
committed by GitHub
parent a822beb630
commit 60479458d7
3 changed files with 188 additions and 1 deletions
+20
View File
@@ -338,6 +338,26 @@ func collectIntegrationStats(ctx context.Context, req collectIntegrationStatsReq
}
ret.Integration = uiIg
integrationStatus := req.integration.GetStatus()
switch req.integration.GetSubKind() {
case types.IntegrationSubKindAWSRolesAnywhere:
ret.RolesAnywhereProfileSync = &ui.RolesAnywhereProfileSync{}
awsRolesAnywhereSpec := req.integration.GetAWSRolesAnywhereIntegrationSpec()
if awsRolesAnywhereSpec != nil {
ret.RolesAnywhereProfileSync.Enabled = awsRolesAnywhereSpec.ProfileSyncConfig.Enabled
}
if integrationStatus.AWSRolesAnywhere != nil {
ret.RolesAnywhereProfileSync.Status = integrationStatus.AWSRolesAnywhere.LastProfileSync.Status
ret.RolesAnywhereProfileSync.ErrorMessage = integrationStatus.AWSRolesAnywhere.LastProfileSync.ErrorMessage
ret.RolesAnywhereProfileSync.SyncedProfiles = int(integrationStatus.AWSRolesAnywhere.LastProfileSync.SyncedProfiles)
ret.RolesAnywhereProfileSync.SyncStartTime = integrationStatus.AWSRolesAnywhere.LastProfileSync.StartTime
ret.RolesAnywhereProfileSync.SyncEndTime = integrationStatus.AWSRolesAnywhere.LastProfileSync.EndTime
}
}
var nextPage string
for {
filters := &usertasksv1.ListUserTasksFilters{
+145 -1
View File
@@ -244,7 +244,7 @@ func (m *mockUserTasksLister) ListUserTasks(ctx context.Context, pageSize int64,
return ret, "", nil
}
func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
func TestCollectIntegrationStats(t *testing.T) {
ctx := context.Background()
logger := logtest.NewLogger()
@@ -505,6 +505,150 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
}
require.Equal(t, expectedSummary, gotSummary)
})
t.Run("returns AWS IAM Roles Anywhere Profile Sync status", func(t *testing.T) {
syncStartTime := time.Now()
syncEndTime := syncStartTime.Add(5 * time.Minute)
integrationName := "my-integration"
integration, 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/uuid2",
ProfileAcceptsRoleSessionName: true,
RoleARN: "arn:aws:iam::123456789012:role/SyncRole",
},
},
)
require.NoError(t, err)
integration.SetStatus(types.IntegrationStatusV1{
AWSRolesAnywhere: &types.AWSRAIntegrationStatusV1{
LastProfileSync: &types.AWSRolesAnywhereProfileSyncIterationSummary{
Status: "SUCCESS",
SyncedProfiles: 4,
StartTime: syncStartTime,
EndTime: syncEndTime,
},
},
})
clt := &mockRelevantAWSRegionsClient{
discoveryConfigs: []*discoveryconfig.DiscoveryConfig{},
databaseServices: &proto.ListResourcesResponse{},
databases: make([]types.Database, 0),
}
deployedDatabaseServicesClient := &mockDeployedDatabaseServices{
listErr: trace.AccessDenied("AccessDenied to ECS:ListServices"),
}
req := collectIntegrationStatsRequest{
logger: logger,
integration: integration,
discoveryConfigLister: clt,
databaseGetter: clt,
awsOIDCClient: deployedDatabaseServicesClient,
userTasksClient: &mockUserTasksLister{},
}
gotSummary, err := collectIntegrationStats(ctx, req)
require.NoError(t, err)
expectedSummary := &ui.IntegrationWithSummary{
Integration: &ui.Integration{
Name: integrationName,
SubKind: "aws-ra",
AWSRA: &ui.IntegrationAWSRASpec{
TrustAnchorARN: "arn:aws:rolesanywhere:eu-west-2:123456789012:trust-anchor/12345678-1234-1234-1234-123456789012",
ProfileSyncConfig: ui.AWSRAProfileSync{
Enabled: true,
ProfileARN: "arn:aws:rolesanywhere:eu-west-2:123456789012:profile/uuid2",
RoleARN: "arn:aws:iam::123456789012:role/SyncRole",
},
},
},
RolesAnywhereProfileSync: &ui.RolesAnywhereProfileSync{
Enabled: true,
Status: "SUCCESS",
SyncedProfiles: 4,
SyncStartTime: syncStartTime,
SyncEndTime: syncEndTime,
},
}
require.Equal(t, expectedSummary, gotSummary)
})
t.Run("returns AWS IAM Roles Anywhere Profile Sync status with error message", func(t *testing.T) {
syncStartTime := time.Now()
syncEndTime := syncStartTime.Add(5 * time.Minute)
integrationName := "my-integration"
integration, 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/uuid2",
ProfileAcceptsRoleSessionName: true,
RoleARN: "arn:aws:iam::123456789012:role/SyncRole",
},
},
)
require.NoError(t, err)
integration.SetStatus(types.IntegrationStatusV1{
AWSRolesAnywhere: &types.AWSRAIntegrationStatusV1{
LastProfileSync: &types.AWSRolesAnywhereProfileSyncIterationSummary{
Status: "ERROR",
SyncedProfiles: 0,
StartTime: syncStartTime,
EndTime: syncEndTime,
ErrorMessage: "Failed to sync profiles due to access denied error",
},
},
})
clt := &mockRelevantAWSRegionsClient{
discoveryConfigs: []*discoveryconfig.DiscoveryConfig{},
databaseServices: &proto.ListResourcesResponse{},
databases: make([]types.Database, 0),
}
deployedDatabaseServicesClient := &mockDeployedDatabaseServices{
listErr: trace.AccessDenied("AccessDenied to ECS:ListServices"),
}
req := collectIntegrationStatsRequest{
logger: logger,
integration: integration,
discoveryConfigLister: clt,
databaseGetter: clt,
awsOIDCClient: deployedDatabaseServicesClient,
userTasksClient: &mockUserTasksLister{},
}
gotSummary, err := collectIntegrationStats(ctx, req)
require.NoError(t, err)
expectedSummary := &ui.IntegrationWithSummary{
Integration: &ui.Integration{
Name: integrationName,
SubKind: "aws-ra",
AWSRA: &ui.IntegrationAWSRASpec{
TrustAnchorARN: "arn:aws:rolesanywhere:eu-west-2:123456789012:trust-anchor/12345678-1234-1234-1234-123456789012",
ProfileSyncConfig: ui.AWSRAProfileSync{
Enabled: true,
ProfileARN: "arn:aws:rolesanywhere:eu-west-2:123456789012:profile/uuid2",
RoleARN: "arn:aws:iam::123456789012:role/SyncRole",
},
},
},
RolesAnywhereProfileSync: &ui.RolesAnywhereProfileSync{
Enabled: true,
Status: "ERROR",
SyncedProfiles: 0,
SyncStartTime: syncStartTime,
SyncEndTime: syncEndTime,
ErrorMessage: "Failed to sync profiles due to access denied error",
},
}
require.Equal(t, expectedSummary, gotSummary)
})
}
func TestCollectAutoDiscoveryRules(t *testing.T) {
+23
View File
@@ -124,6 +124,9 @@ type IntegrationWithSummary struct {
AWSRDS ResourceTypeSummary `json:"awsrds"`
// AWSEKS contains the summary for the AWS EKS resources for this integration.
AWSEKS ResourceTypeSummary `json:"awseks"`
// RolesAnywhereProfileSync contains the summary for the AWS Roles Anywhere Profile Sync.
RolesAnywhereProfileSync *RolesAnywhereProfileSync `json:"rolesAnywhereProfileSync,omitempty"`
}
// ResourceTypeSummary contains the summary of the enrollment rules and found resources by the integration.
@@ -150,6 +153,26 @@ type ResourceTypeSummary struct {
ECSDatabaseServiceCount int `json:"ecsDatabaseServiceCount,omitempty"`
}
// RolesAnywhereProfileSync contains the summary for the AWS Roles Anywhere Profile Sync.
type RolesAnywhereProfileSync struct {
// Enabled indicates whether the profile sync is enabled.
Enabled bool `json:"enabled"`
// Status is the string representation of the profile sync status.
// Either ERROR or SUCCESS.
Status string `json:"status,omitempty"`
// ErrorMessage is the error message from the last sync when the Status is ERROR.
ErrorMessage string `json:"errorMessage,omitempty"`
// SyncedProfiles is the number of profiles that were imported into Teleport.
SyncedProfiles int `json:"syncedProfiles,omitempty"`
// SyncStartTime is the time when the profile sync started.
SyncStartTime time.Time `json:"syncStartTime,omitempty"`
// SyncEndTime is the time when the profile sync ended.
SyncEndTime time.Time `json:"syncEndTime,omitempty"`
}
// IntegrationDiscoveryRule describes a discovery rule associated with an integration.
type IntegrationDiscoveryRule struct {
// ResourceType indicates the type of resource that this rule targets.