-
Notifications
You must be signed in to change notification settings - Fork 4.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New resource azurerm_policy_remediation
#5746
Merged
katbyte
merged 32 commits into
hashicorp:master
from
ArcturusZhang:PolicyInsightsRemediation
Mar 27, 2020
Merged
Changes from 22 commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
0c6555f
Add ID parsing and validation functions, add name validate function
ArcturusZhang 5582e41
Add validate function for data source
ArcturusZhang cb3a052
Goimports
ArcturusZhang 83435db
Added new resource and data source azurerm_policy_remediation
ArcturusZhang 3c6ba09
Add some more test cases
ArcturusZhang c53b8db
Some changes to conform the changes in 2.0
ArcturusZhang 689bcdc
Resolve some comments
ArcturusZhang b281d69
Add ID parsing and validation functions, add name validate function
ArcturusZhang 5eb2e9b
Add validate function for data source
ArcturusZhang 22dc3b7
Goimports
ArcturusZhang cf1d469
Resolve comments
ArcturusZhang 48dc397
Use parsing functions from mgmt group
ArcturusZhang aae0f5d
Resolve more comments
ArcturusZhang be89874
Resolve more comments
ArcturusZhang 8142ed5
Fix CI failures
ArcturusZhang e4cf05a
Some refactor
ArcturusZhang d083570
Removed a useless comment
ArcturusZhang 4ebd2f0
Some refinement on file structure
ArcturusZhang dcc9859
Fix CI failures
ArcturusZhang 383ae37
Resolve comments
ArcturusZhang 4171232
Merge branch 'Add-ID-parsing-function-for-mgmt-group' into PolicyInsi…
ArcturusZhang 5c8dd44
Merge remote-tracking branch 'origin/master' into PolicyInsightsRemed…
ArcturusZhang 080d94b
Merge branch 'master' into PolicyInsightsRemediation
ArcturusZhang abddb70
Merge branch 'master' into PolicyInsightsRemediation
ArcturusZhang 2e42ed3
Refresh dependencies for policyinsights
ArcturusZhang f31c25a
Merge branch 'master' into PolicyInsightsRemediation
ArcturusZhang 02cac69
Resolve comments
ArcturusZhang 1e0db42
Resolve comments
ArcturusZhang 97bd696
Include some changes on policy definition
ArcturusZhang e53b998
Fix name validation to forbid upper case letters
ArcturusZhang 3407903
make fmt
katbyte 88cb232
Goimports :(
ArcturusZhang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
azurerm/internal/services/managementgroup/parse/management_group.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package parse | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
type ManagementGroupId struct { | ||
GroupId string | ||
} | ||
|
||
func ManagementGroupID(input string) (*ManagementGroupId, error) { | ||
regex := regexp.MustCompile(`^/providers/[Mm]icrosoft\.[Mm]anagement/management[Gg]roups/`) | ||
if !regex.MatchString(input) { | ||
return nil, fmt.Errorf("Unable to parse Management Group ID %q", input) | ||
} | ||
|
||
// Split the input ID by the regex | ||
segments := regex.Split(input, -1) | ||
if len(segments) != 2 { | ||
return nil, fmt.Errorf("Unable to parse Management Group ID %q: expected id to have two segments after splitting", input) | ||
} | ||
|
||
groupID := segments[1] | ||
if groupID == "" { | ||
return nil, fmt.Errorf("unable to parse Management Group ID %q: management group name is empty", input) | ||
} | ||
if segments := strings.Split(groupID, "/"); len(segments) != 1 { | ||
return nil, fmt.Errorf("unable to parse Management Group ID %q: ID has extra segments", input) | ||
} | ||
|
||
id := ManagementGroupId{ | ||
GroupId: groupID, | ||
} | ||
|
||
return &id, nil | ||
} |
83 changes: 83 additions & 0 deletions
83
azurerm/internal/services/managementgroup/parse/management_group_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package parse | ||
|
||
import "testing" | ||
|
||
func TestManagementGroupID(t *testing.T) { | ||
testData := []struct { | ||
Name string | ||
Input string | ||
Error bool | ||
Expected *ManagementGroupId | ||
}{ | ||
{ | ||
Name: "Empty", | ||
Input: "", | ||
Error: true, | ||
}, | ||
{ | ||
Name: "No Management Groups Segment", | ||
Input: "/providers/Microsoft.Management", | ||
Error: true, | ||
}, | ||
{ | ||
Name: "No Management Group ID", | ||
Input: "/providers/Microsoft.Management/managementGroups/", | ||
Error: true, | ||
}, | ||
{ | ||
Name: "Management Group ID in UUID", | ||
Input: "/providers/Microsoft.Management/managementGroups/00000000-0000-0000-0000-000000000000", | ||
Expected: &ManagementGroupId{ | ||
GroupId: "00000000-0000-0000-0000-000000000000", | ||
}, | ||
}, | ||
{ | ||
Name: "Management Group ID in Readable ID", | ||
Input: "/providers/Microsoft.Management/managementGroups/myGroup", | ||
Expected: &ManagementGroupId{ | ||
GroupId: "myGroup", | ||
}, | ||
}, | ||
{ | ||
Name: "Management Group ID in UUID with wrong casing", | ||
Input: "/providers/microsoft.management/managementgroups/00000000-0000-0000-0000-000000000000", | ||
Expected: &ManagementGroupId{ | ||
GroupId: "00000000-0000-0000-0000-000000000000", | ||
}, | ||
}, | ||
{ | ||
Name: "Management Group ID in Readable ID with wrong casing", | ||
Input: "/providers/microsoft.management/managementgroups/group1", | ||
Expected: &ManagementGroupId{ | ||
GroupId: "group1", | ||
}, | ||
}, | ||
{ | ||
Name: "Invalid Management group id", | ||
Input: "/providers/Microsoft.Management/managementGroups/myGroup/another", | ||
Error: true, | ||
}, | ||
{ | ||
Name: "Resource ID in management group", | ||
Input: "/providers/Microsoft.Management/managementGroups/myGroup/providers/Microsoft.Authorization/policyDefinitions/def1", | ||
Error: true, | ||
}, | ||
} | ||
|
||
for _, v := range testData { | ||
t.Logf("[DEBUG] Testing %q", v.Name) | ||
|
||
actual, err := ManagementGroupID(v.Input) | ||
if err != nil { | ||
if v.Error { | ||
continue | ||
} | ||
|
||
t.Fatalf("Expected a value but got an error: %s", err) | ||
} | ||
|
||
if actual.GroupId != v.Expected.GroupId { | ||
t.Fatalf("Expected %q but got %q for Name", v.Expected.GroupId, actual.GroupId) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,8 @@ import ( | |
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/managementgroup/parse" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/managementgroup/validate" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" | ||
) | ||
|
@@ -38,10 +40,11 @@ func resourceArmManagementGroup() *schema.Resource { | |
|
||
Schema: map[string]*schema.Schema{ | ||
"group_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
ForceNew: true, | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
ForceNew: true, | ||
ValidateFunc: validate.ManagementGroupName, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The is a group_id property but we are validating a name? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same reason as the above comment |
||
}, | ||
|
||
"display_name": { | ||
|
@@ -51,9 +54,10 @@ func resourceArmManagementGroup() *schema.Resource { | |
}, | ||
|
||
"parent_management_group_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Computed: true, | ||
ValidateFunc: validate.ManagementGroupID, | ||
}, | ||
|
||
"subscription_ids": { | ||
|
@@ -88,7 +92,7 @@ func resourceArmManagementGroupCreateUpdate(d *schema.ResourceData, meta interfa | |
existing, err := client.Get(ctx, groupId, "children", &recurse, "", managementGroupCacheControl) | ||
if err != nil { | ||
if !utils.ResponseWasNotFound(existing.Response) { | ||
return fmt.Errorf("Error checking for presence of existing Management Group %q: %s", groupId, err) | ||
return fmt.Errorf("unable to check for presence of existing Management Group %q: %s", groupId, err) | ||
} | ||
} | ||
|
||
|
@@ -117,16 +121,16 @@ func resourceArmManagementGroupCreateUpdate(d *schema.ResourceData, meta interfa | |
|
||
future, err := client.CreateOrUpdate(ctx, groupId, properties, managementGroupCacheControl) | ||
if err != nil { | ||
return fmt.Errorf("Error creating Management Group %q: %+v", groupId, err) | ||
return fmt.Errorf("unable to create Management Group %q: %+v", groupId, err) | ||
} | ||
|
||
if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { | ||
return fmt.Errorf("Error waiting for creation of Management Group %q: %+v", groupId, err) | ||
return fmt.Errorf("failed when waiting for creation of Management Group %q: %+v", groupId, err) | ||
} | ||
|
||
resp, err := client.Get(ctx, groupId, "children", &recurse, "", managementGroupCacheControl) | ||
if err != nil { | ||
return fmt.Errorf("Error retrieving Management Group %q: %+v", groupId, err) | ||
return fmt.Errorf("unable to retrieve Management Group %q: %+v", groupId, err) | ||
} | ||
|
||
d.SetId(*resp.ID) | ||
|
@@ -139,15 +143,15 @@ func resourceArmManagementGroupCreateUpdate(d *schema.ResourceData, meta interfa | |
if props := resp.Properties; props != nil { | ||
subscriptionIdsToRemove, err2 := determineManagementGroupSubscriptionsIdsToRemove(props.Children, subscriptionIds) | ||
if err2 != nil { | ||
return fmt.Errorf("Error determining which subscriptions should be removed from Management Group %q: %+v", groupId, err2) | ||
return fmt.Errorf("unable to determine which subscriptions should be removed from Management Group %q: %+v", groupId, err2) | ||
} | ||
|
||
for _, subscriptionId := range *subscriptionIdsToRemove { | ||
log.Printf("[DEBUG] De-associating Subscription ID %q from Management Group %q", subscriptionId, groupId) | ||
deleteResp, err2 := subscriptionsClient.Delete(ctx, groupId, subscriptionId, managementGroupCacheControl) | ||
if err2 != nil { | ||
if !response.WasNotFound(deleteResp.Response) { | ||
return fmt.Errorf("Error de-associating Subscription %q from Management Group %q: %+v", subscriptionId, groupId, err2) | ||
return fmt.Errorf("unable to de-associate Subscription %q from Management Group %q: %+v", subscriptionId, groupId, err2) | ||
} | ||
} | ||
} | ||
|
@@ -172,31 +176,31 @@ func resourceArmManagementGroupRead(d *schema.ResourceData, meta interface{}) er | |
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
id, err := parseManagementGroupId(d.Id()) | ||
id, err := parse.ManagementGroupID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
recurse := true | ||
resp, err := client.Get(ctx, id.groupId, "children", &recurse, "", managementGroupCacheControl) | ||
resp, err := client.Get(ctx, id.GroupId, "children", &recurse, "", managementGroupCacheControl) | ||
if err != nil { | ||
if utils.ResponseWasNotFound(resp.Response) { | ||
log.Printf("[INFO] Management Group %q doesn't exist - removing from state", d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("Error reading Management Group %q: %+v", d.Id(), err) | ||
return fmt.Errorf("unable to read Management Group %q: %+v", d.Id(), err) | ||
} | ||
|
||
d.Set("group_id", id.groupId) | ||
d.Set("group_id", id.GroupId) | ||
|
||
if props := resp.Properties; props != nil { | ||
d.Set("display_name", props.DisplayName) | ||
|
||
subscriptionIds, err := flattenArmManagementGroupSubscriptionIds(props.Children) | ||
if err != nil { | ||
return fmt.Errorf("Error flattening `subscription_ids`: %+v", err) | ||
return fmt.Errorf("unable to flatten `subscription_ids`: %+v", err) | ||
} | ||
d.Set("subscription_ids", subscriptionIds) | ||
|
||
|
@@ -220,20 +224,20 @@ func resourceArmManagementGroupDelete(d *schema.ResourceData, meta interface{}) | |
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
id, err := parseManagementGroupId(d.Id()) | ||
id, err := parse.ManagementGroupID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
recurse := true | ||
group, err := client.Get(ctx, id.groupId, "children", &recurse, "", managementGroupCacheControl) | ||
group, err := client.Get(ctx, id.GroupId, "children", &recurse, "", managementGroupCacheControl) | ||
if err != nil { | ||
if utils.ResponseWasNotFound(group.Response) { | ||
log.Printf("[DEBUG] Management Group %q doesn't exist in Azure - nothing to do!", id.groupId) | ||
log.Printf("[DEBUG] Management Group %q doesn't exist in Azure - nothing to do!", id.GroupId) | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("Error retrieving Management Group %q: %+v", id.groupId, err) | ||
return fmt.Errorf("unable to retrieve Management Group %q: %+v", id.GroupId, err) | ||
} | ||
|
||
// before deleting a management group, return any subscriptions to the root management group | ||
|
@@ -245,26 +249,26 @@ func resourceArmManagementGroupDelete(d *schema.ResourceData, meta interface{}) | |
} | ||
|
||
subscriptionId := *v.ID | ||
log.Printf("[DEBUG] De-associating Subscription %q from Management Group %q..", subscriptionId, id.groupId) | ||
log.Printf("[DEBUG] De-associating Subscription %q from Management Group %q..", subscriptionId, id.GroupId) | ||
// NOTE: whilst this says `Delete` it's actually `Deassociate` - which is /really/ helpful | ||
deleteResp, err2 := subscriptionsClient.Delete(ctx, id.groupId, subscriptionId, managementGroupCacheControl) | ||
deleteResp, err2 := subscriptionsClient.Delete(ctx, id.GroupId, subscriptionId, managementGroupCacheControl) | ||
if err2 != nil { | ||
if !response.WasNotFound(deleteResp.Response) { | ||
return fmt.Errorf("Error de-associating Subscription %q from Management Group %q: %+v", subscriptionId, id.groupId, err2) | ||
return fmt.Errorf("unable to de-associate Subscription %q from Management Group %q: %+v", subscriptionId, id.GroupId, err2) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
resp, err := client.Delete(ctx, id.groupId, managementGroupCacheControl) | ||
resp, err := client.Delete(ctx, id.GroupId, managementGroupCacheControl) | ||
if err != nil { | ||
return fmt.Errorf("Error deleting Management Group %q: %+v", id.groupId, err) | ||
return fmt.Errorf("unable to delete Management Group %q: %+v", id.GroupId, err) | ||
} | ||
|
||
err = resp.WaitForCompletionRef(ctx, client.Client) | ||
if err != nil { | ||
return fmt.Errorf("Error waiting for the deletion of Management Group %q: %+v", id.groupId, err) | ||
return fmt.Errorf("failed when waiting for the deletion of Management Group %q: %+v", id.GroupId, err) | ||
} | ||
|
||
return nil | ||
|
@@ -295,7 +299,7 @@ func flattenArmManagementGroupSubscriptionIds(input *[]managementgroups.ChildInf | |
|
||
id, err := parseManagementGroupSubscriptionID(*child.ID) | ||
if err != nil { | ||
return nil, fmt.Errorf("Unable to parse child Subscription ID %+v", err) | ||
return nil, fmt.Errorf("unable to parse child Subscription ID %+v", err) | ||
} | ||
|
||
if id != nil { | ||
|
@@ -306,31 +310,13 @@ func flattenArmManagementGroupSubscriptionIds(input *[]managementgroups.ChildInf | |
return subscriptionIds, nil | ||
} | ||
|
||
type managementGroupId struct { | ||
groupId string | ||
} | ||
|
||
type subscriptionId struct { | ||
subscriptionId string | ||
} | ||
|
||
func parseManagementGroupId(input string) (*managementGroupId, error) { | ||
// /providers/Microsoft.Management/managementGroups/00000000-0000-0000-0000-000000000000 | ||
segments := strings.Split(input, "/") | ||
if len(segments) != 5 { | ||
return nil, fmt.Errorf("Expected there to be 5 segments but got %d", len(segments)) | ||
} | ||
|
||
id := managementGroupId{ | ||
groupId: segments[4], | ||
} | ||
return &id, nil | ||
} | ||
|
||
func parseManagementGroupSubscriptionID(input string) (*subscriptionId, error) { | ||
// this is either: | ||
// /subscriptions/00000000-0000-0000-0000-000000000000 | ||
// /providers/Microsoft.Management/managementGroups/e4115b99-6be7-4153-a73f-5ff5e778ce28 | ||
|
||
// we skip out the managementGroup ID's | ||
if strings.HasPrefix(input, "/providers/Microsoft.Management/managementGroups/") { | ||
|
@@ -340,11 +326,11 @@ func parseManagementGroupSubscriptionID(input string) (*subscriptionId, error) { | |
components := strings.Split(input, "/") | ||
|
||
if len(components) == 0 { | ||
return nil, fmt.Errorf("Subscription Id is empty or not formatted correctly: %s", input) | ||
return nil, fmt.Errorf("subscription Id is empty or not formatted correctly: %s", input) | ||
} | ||
|
||
if len(components) != 3 { | ||
return nil, fmt.Errorf("Subscription Id should have 2 segments, got %d: %q", len(components)-1, input) | ||
return nil, fmt.Errorf("subscription Id should have 2 segments, got %d: %q", len(components)-1, input) | ||
} | ||
|
||
id := subscriptionId{ | ||
|
@@ -366,7 +352,7 @@ func determineManagementGroupSubscriptionsIdsToRemove(existing *[]managementgrou | |
|
||
id, err := parseManagementGroupSubscriptionID(*v.ID) | ||
if err != nil { | ||
return nil, fmt.Errorf("Error parsing Subscription ID %q: %+v", *v.ID, err) | ||
return nil, fmt.Errorf("unable to parse Subscription ID %q: %+v", *v.ID, err) | ||
} | ||
|
||
// not a Subscription - so let's skip it | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the property is group_id but this is validating the name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here group_id means this one in its ID:
the thing in other resources' IDs at the same position would be called as a
name
, for instanceAnd another reason is that if we call this
group_id
in the validation function, the validation function would looks likeManagementGroupGroupID
orManagementGroupID
. This would be confusing with the validation function of the real management group ID. Therefore I took this name.