Skip to content

Commit 534ce96

Browse files
committed
allow definition of roleDefinitionIDs without subscription prefix
Signed-off-by: Markus Blaschke <mblaschke82@gmail.com>
1 parent 56d18f7 commit 534ce96

File tree

7 files changed

+70
-66
lines changed

7 files changed

+70
-66
lines changed

config/opts.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ type (
5252
RoleAssignments struct {
5353
Enable bool `long:"janitor.roleassignments" env:"JANITOR_ROLEASSIGNMENTS_ENABLE" description:"Enable Azure RoleAssignments cleanup"`
5454
Ttl time.Duration `long:"janitor.roleassignments.ttl" env:"JANITOR_ROLEASSIGNMENTS_TTL" description:"Janitor roleassignment ttl (time.duration)" default:"6h"`
55-
RoleDefintionIds []string `long:"janitor.roleassignments.roledefinitionid" env:"JANITOR_ROLEASSIGNMENTS_ROLEDEFINITIONID" env-delim:" " description:"Janitor roledefinition ID (eg: /subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/providers/Microsoft.Authorization/roleDefinitions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx) (space delimiter)"`
55+
RoleDefintionIds []string `long:"janitor.roleassignments.roledefinitionid" env:"JANITOR_ROLEASSIGNMENTS_ROLEDEFINITIONID" env-delim:" " description:"Janitor roledefinition ID (eg: /subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/providers/Microsoft.Authorization/roleDefinitions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx or /providers/Microsoft.Authorization/roleDefinitions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx for subscription independent roleDefinitions) (space delimiter)"`
5656
AdditionalFilter *string `long:"janitor.roleassignments.filter" env:"JANITOR_ROLEASSIGNMENTS_FILTER" description:"Additional $filter for Azure REST API for RoleAssignments"`
5757
Filter string
5858
DescriptionTtl *string `long:"janitor.roleassignments.descriptionttl" env:"JANITOR_ROLEASSIGNMENTS_DESCRIPTIONTTL" description:"Regexp for detecting ttl inside description of RoleAssignment"`

janitor/deployments.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package janitor
22

33
import (
44
"context"
5+
"strings"
56
"time"
67

78
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
@@ -66,16 +67,16 @@ func (j *Janitor) runDeployments(ctx context.Context, logger *zap.SugaredLogger,
6667
contextLogger.Infof("%s: successfully deleted", to.String(deployment.ID))
6768

6869
j.Prometheus.MetricDeletedResource.With(prometheus.Labels{
69-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
70-
"resourceType": stringToStringLower(resourceType),
70+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
71+
"resourceType": strings.ToLower(resourceType),
7172
}).Inc()
7273
} else {
7374
// failed delete
7475
contextLogger.Errorf("%s: ERROR %s", to.String(deployment.ID), err.Error())
7576

7677
j.Prometheus.MetricErrors.With(prometheus.Labels{
77-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
78-
"resourceType": stringToStringLower(resourceType),
78+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
79+
"resourceType": strings.ToLower(resourceType),
7980
}).Inc()
8081
}
8182
} else {
@@ -138,16 +139,16 @@ func (j *Janitor) runDeployments(ctx context.Context, logger *zap.SugaredLogger,
138139
resourceLogger.Infof("%s: successfully deleted", to.String(deployment.ID))
139140

140141
j.Prometheus.MetricDeletedResource.With(prometheus.Labels{
141-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
142-
"resourceType": stringToStringLower(resourceType),
142+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
143+
"resourceType": strings.ToLower(resourceType),
143144
}).Inc()
144145
} else {
145146
// failed delete
146147
resourceLogger.Errorf("%s: ERROR %s", to.String(deployment.ID), err.Error())
147148

148149
j.Prometheus.MetricErrors.With(prometheus.Labels{
149-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
150-
"resourceType": stringToStringLower(resourceType),
150+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
151+
"resourceType": strings.ToLower(resourceType),
151152
}).Inc()
152153
}
153154
} else {

janitor/misc.go

Lines changed: 0 additions & 23 deletions
This file was deleted.

janitor/resourcegroups.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package janitor
22

33
import (
44
"context"
5+
"strings"
56

67
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
78
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions"
@@ -37,10 +38,10 @@ func (j *Janitor) runResourceGroups(ctx context.Context, logger *zap.SugaredLogg
3738

3839
if resourceExpiryTime != nil {
3940
labels := prometheus.Labels{
40-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
41-
"resourceID": stringPtrToStringLower(resourceGroup.ID),
42-
"resourceGroup": stringPtrToStringLower(resourceGroup.Name),
43-
"resourceType": stringToStringLower(resourceType),
41+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
42+
"resourceID": to.StringLower(resourceGroup.ID),
43+
"resourceGroup": to.StringLower(resourceGroup.Name),
44+
"resourceType": strings.ToLower(resourceType),
4445
}
4546
labels = j.Azure.ResourceTagManager.AddResourceTagsToPrometheusLabels(ctx, labels, *resourceGroup.ID)
4647
resourceTtl.AddTime(labels, *resourceExpiryTime)
@@ -60,8 +61,8 @@ func (j *Janitor) runResourceGroups(ctx context.Context, logger *zap.SugaredLogg
6061
resourceLogger.Error(err.Error())
6162

6263
j.Prometheus.MetricErrors.With(prometheus.Labels{
63-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
64-
"resourceType": stringToStringLower(resourceType),
64+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
65+
"resourceType": strings.ToLower(resourceType),
6566
}).Inc()
6667
}
6768
}
@@ -73,16 +74,16 @@ func (j *Janitor) runResourceGroups(ctx context.Context, logger *zap.SugaredLogg
7374
resourceLogger.Infof("successfully deleted")
7475

7576
j.Prometheus.MetricDeletedResource.With(prometheus.Labels{
76-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
77-
"resourceType": stringToStringLower(resourceType),
77+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
78+
"resourceType": strings.ToLower(resourceType),
7879
}).Inc()
7980
} else {
8081
// failed delete
8182
resourceLogger.Error(err.Error())
8283

8384
j.Prometheus.MetricErrors.With(prometheus.Labels{
84-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
85-
"resourceType": stringToStringLower(resourceType),
85+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
86+
"resourceType": strings.ToLower(resourceType),
8687
}).Inc()
8788
}
8889
}

janitor/resources.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package janitor
22

33
import (
44
"context"
5+
"strings"
56

67
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
78
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions"
@@ -43,8 +44,8 @@ func (j *Janitor) runResources(ctx context.Context, logger *zap.SugaredLogger, s
4344
resourceLogger.Errorf("unable to detect apiVersion for Azure resource, cannot delete resource (please report this issue as bug)")
4445

4546
j.Prometheus.MetricErrors.With(prometheus.Labels{
46-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
47-
"resourceType": stringToStringLower(resourceType),
47+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
48+
"resourceType": strings.ToLower(resourceType),
4849
}).Inc()
4950

5051
continue
@@ -57,8 +58,8 @@ func (j *Janitor) runResources(ctx context.Context, logger *zap.SugaredLogger, s
5758

5859
if resourceExpiryTime != nil {
5960
labels := prometheus.Labels{
60-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
61-
"resourceID": stringPtrToStringLower(resource.ID),
61+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
62+
"resourceID": to.StringLower(resource.ID),
6263
"resourceGroup": azureResource.ResourceGroup,
6364
"resourceType": azureResource.ResourceType,
6465
}

janitor/roleassignments.go

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package janitor
22

33
import (
44
"context"
5+
"strings"
56
"time"
67

78
armauthorization "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2"
@@ -39,18 +40,18 @@ func (j *Janitor) runRoleAssignments(ctx context.Context, logger *zap.SugaredLog
3940
azureResource, _ := armclient.ParseResourceId(*roleAssignment.Properties.Scope)
4041

4142
roleAssignmentLogger := contextLogger.With(
42-
zap.String("roleAssignmentId", stringPtrToStringLower(roleAssignment.ID)),
43-
zap.String("scope", stringPtrToStringLower(roleAssignment.Properties.Scope)),
44-
zap.String("principalId", stringPtrToStringLower(roleAssignment.Properties.PrincipalID)),
45-
zap.String("principalType", stringToStringLower(string(*roleAssignment.Properties.PrincipalType))),
46-
zap.String("roleDefinitionId", stringPtrToStringLower(roleAssignment.Properties.RoleDefinitionID)),
47-
zap.String("subscriptionID", stringPtrToStringLower(subscription.SubscriptionID)),
43+
zap.String("roleAssignmentId", to.StringLower(roleAssignment.ID)),
44+
zap.String("scope", to.StringLower(roleAssignment.Properties.Scope)),
45+
zap.String("principalId", to.StringLower(roleAssignment.Properties.PrincipalID)),
46+
zap.String("principalType", strings.ToLower(string(*roleAssignment.Properties.PrincipalType))),
47+
zap.String("roleDefinitionId", to.StringLower(roleAssignment.Properties.RoleDefinitionID)),
48+
zap.String("subscriptionID", to.StringLower(subscription.SubscriptionID)),
4849
zap.String("resourceGroup", azureResource.ResourceGroup),
4950
)
5051

51-
// check if RoleDefinitionID is set
52+
// check if roleAssignment is allowed for cleanup
5253
// do not want to touch other RoleAssignments
53-
if stringInSlice(*roleAssignment.Properties.RoleDefinitionID, j.Conf.Janitor.RoleAssignments.RoleDefintionIds) {
54+
if j.isRoleAssignmentCleanupAllowed(roleAssignment) {
5455
var roleAssignmentTtl *time.Duration
5556
roleAssignmentLogger.Debug("checking ttl")
5657

@@ -77,12 +78,12 @@ func (j *Janitor) runRoleAssignments(ctx context.Context, logger *zap.SugaredLog
7778
roleAssignmentLogger.Debugf("detected ttl %v", roleAssignmentTtl.String())
7879

7980
resourceTtl.AddTime(prometheus.Labels{
80-
"roleAssignmentId": stringPtrToStringLower(roleAssignment.ID),
81-
"scope": stringPtrToStringLower(roleAssignment.Properties.Scope),
82-
"principalId": stringPtrToStringLower(roleAssignment.Properties.PrincipalID),
83-
"principalType": stringPtrToStringLower(roleAssignment.Type),
84-
"roleDefinitionId": stringPtrToStringLower(roleAssignment.Properties.RoleDefinitionID),
85-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
81+
"roleAssignmentId": to.StringLower(roleAssignment.ID),
82+
"scope": to.StringLower(roleAssignment.Properties.Scope),
83+
"principalId": to.StringLower(roleAssignment.Properties.PrincipalID),
84+
"principalType": to.StringLower(roleAssignment.Type),
85+
"roleDefinitionId": to.StringLower(roleAssignment.Properties.RoleDefinitionID),
86+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
8687
"resourceGroup": azureResource.ResourceGroup,
8788
}, roleAssignmentExpiry)
8889

@@ -94,16 +95,16 @@ func (j *Janitor) runRoleAssignments(ctx context.Context, logger *zap.SugaredLog
9495
roleAssignmentLogger.Infof("successfully deleted")
9596

9697
j.Prometheus.MetricDeletedResource.With(prometheus.Labels{
97-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
98-
"resourceType": stringToStringLower(resourceType),
98+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
99+
"resourceType": strings.ToLower(resourceType),
99100
}).Inc()
100101
} else {
101102
// failed delete
102103
roleAssignmentLogger.Error(err.Error())
103104

104105
j.Prometheus.MetricErrors.With(prometheus.Labels{
105-
"subscriptionID": stringPtrToStringLower(subscription.SubscriptionID),
106-
"resourceType": stringToStringLower(resourceType),
106+
"subscriptionID": to.StringLower(subscription.SubscriptionID),
107+
"resourceType": strings.ToLower(resourceType),
107108
}).Inc()
108109
}
109110
} else {
@@ -120,3 +121,19 @@ func (j *Janitor) runRoleAssignments(ctx context.Context, logger *zap.SugaredLog
120121
resourceTtl.GaugeSet(j.Prometheus.MetricTtlRoleAssignments)
121122
}
122123
}
124+
125+
func (j *Janitor) isRoleAssignmentCleanupAllowed(roleAssignment *armauthorization.RoleAssignment) bool {
126+
roleDefinitionID := to.StringLower(roleAssignment.Properties.RoleDefinitionID)
127+
for _, check := range j.Conf.Janitor.RoleAssignments.RoleDefintionIds {
128+
// sanity check, do not allow empty IDs
129+
if len(check) == 0 {
130+
continue
131+
}
132+
check = strings.ToLower(check)
133+
if strings.EqualFold(roleDefinitionID, check) || strings.HasSuffix(roleDefinitionID, check) {
134+
return true
135+
}
136+
}
137+
138+
return false
139+
}

main.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func initArgparser() {
105105

106106
if Opts.Janitor.RoleAssignments.Enable {
107107
if len(Opts.Janitor.RoleAssignments.RoleDefintionIds) == 0 {
108-
logger.Panic("roleAssignment janitor active but no roleDefinitionIds defined")
108+
logger.Fatal("roleAssignment janitor active but no roleDefinitionIds defined")
109109
}
110110
}
111111

@@ -115,13 +115,20 @@ func initArgparser() {
115115
}
116116

117117
if !Opts.Janitor.ResourceGroups.Enable && !Opts.Janitor.Resources.Enable && !Opts.Janitor.Deployments.Enable && !Opts.Janitor.RoleAssignments.Enable {
118-
logger.Fatal("no janitor task (resources, resourcegroups, deployments, roleassignments) enabled, not starting")
118+
logger.Fatal(`no janitor task (resources, resourcegroups, deployments, roleassignments) enabled, not starting`)
119119
}
120120

121121
if Opts.Janitor.RoleAssignments.DescriptionTtl != nil {
122122
Opts.Janitor.RoleAssignments.DescriptionTtlRegExp = regexp.MustCompile(*Opts.Janitor.RoleAssignments.DescriptionTtl)
123123
}
124124

125+
for _, val := range Opts.Janitor.RoleAssignments.RoleDefintionIds {
126+
val = strings.ToLower(val)
127+
if !strings.Contains(val, "/providers/microsoft.authorization/roledefinitions/") {
128+
logger.Fatal(`roleAssignment roleDefinition IDs MUST contain "/providers/Microsoft.Authorization/" for security reasons`)
129+
}
130+
}
131+
125132
checkForDeprecations()
126133
}
127134

0 commit comments

Comments
 (0)