-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Terraform] Add fine-grained audit config for projects. (#1094)
Merged PR #1094.
- Loading branch information
1 parent
8bacb16
commit 0933739
Showing
6 changed files
with
686 additions
and
3 deletions.
There are no files selected for viewing
Submodule terraform
updated
from 8e515c to 3ee26a
Submodule terraform-beta
updated
from 97025a to 7b70cd
247 changes: 247 additions & 0 deletions
247
third_party/terraform/resources/resource_iam_audit_config.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,247 @@ | ||
package google | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"log" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"google.golang.org/api/cloudresourcemanager/v1" | ||
) | ||
|
||
var iamAuditConfigSchema = map[string]*schema.Schema{ | ||
"service": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"audit_log_config": { | ||
Type: schema.TypeSet, | ||
Required: true, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"log_type": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"exempted_members": { | ||
Type: schema.TypeSet, | ||
Elem: &schema.Schema{Type: schema.TypeString}, | ||
Optional: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
"etag": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
} | ||
|
||
func ResourceIamAuditConfig(parentSpecificSchema map[string]*schema.Schema, newUpdaterFunc newResourceIamUpdaterFunc) *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceIamAuditConfigCreate(newUpdaterFunc), | ||
Read: resourceIamAuditConfigRead(newUpdaterFunc), | ||
Update: resourceIamAuditConfigUpdate(newUpdaterFunc), | ||
Delete: resourceIamAuditConfigDelete(newUpdaterFunc), | ||
Schema: mergeSchemas(iamAuditConfigSchema, parentSpecificSchema), | ||
} | ||
} | ||
|
||
func ResourceIamAuditConfigWithImport(parentSpecificSchema map[string]*schema.Schema, newUpdaterFunc newResourceIamUpdaterFunc, resourceIdParser resourceIdParserFunc) *schema.Resource { | ||
r := ResourceIamAuditConfig(parentSpecificSchema, newUpdaterFunc) | ||
r.Importer = &schema.ResourceImporter{ | ||
State: iamAuditConfigImport(resourceIdParser), | ||
} | ||
return r | ||
} | ||
|
||
func resourceIamAuditConfigCreate(newUpdaterFunc newResourceIamUpdaterFunc) schema.CreateFunc { | ||
return func(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
updater, err := newUpdaterFunc(d, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
p := getResourceIamAuditConfig(d) | ||
err = iamPolicyReadModifyWrite(updater, func(ep *cloudresourcemanager.Policy) error { | ||
ep.AuditConfigs = mergeAuditConfigs(append(ep.AuditConfigs, p)) | ||
return nil | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
d.SetId(updater.GetResourceId() + "/audit_config/" + p.Service) | ||
return resourceIamAuditConfigRead(newUpdaterFunc)(d, meta) | ||
} | ||
} | ||
|
||
func resourceIamAuditConfigRead(newUpdaterFunc newResourceIamUpdaterFunc) schema.ReadFunc { | ||
return func(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
updater, err := newUpdaterFunc(d, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
eAuditConfig := getResourceIamAuditConfig(d) | ||
p, err := updater.GetResourceIamPolicy() | ||
if err != nil { | ||
if isGoogleApiErrorWithCode(err, 404) { | ||
log.Printf("[DEBUG]: AuditConfig for service %q not found for non-existent resource %s, removing from state file.", eAuditConfig.Service, updater.DescribeResource()) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return err | ||
} | ||
log.Printf("[DEBUG]: Retrieved policy for %s: %+v", updater.DescribeResource(), p) | ||
|
||
var ac *cloudresourcemanager.AuditConfig | ||
for _, b := range p.AuditConfigs { | ||
if b.Service != eAuditConfig.Service { | ||
continue | ||
} | ||
ac = b | ||
break | ||
} | ||
if ac == nil { | ||
log.Printf("[DEBUG]: AuditConfig for service %q not found in policy for %s, removing from state file.", eAuditConfig.Service, updater.DescribeResource()) | ||
d.SetId("") | ||
return nil | ||
} | ||
d.Set("etag", p.Etag) | ||
err = d.Set("audit_log_config", flattenAuditLogConfigs(ac.AuditLogConfigs)) | ||
if err != nil { | ||
return fmt.Errorf("Error flattening audit log config: %s", err) | ||
} | ||
d.Set("service", ac.Service) | ||
return nil | ||
} | ||
} | ||
|
||
func iamAuditConfigImport(resourceIdParser resourceIdParserFunc) schema.StateFunc { | ||
return func(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { | ||
if resourceIdParser == nil { | ||
return nil, errors.New("Import not supported for this IAM resource.") | ||
} | ||
config := m.(*Config) | ||
s := strings.Fields(d.Id()) | ||
if len(s) != 2 { | ||
d.SetId("") | ||
return nil, fmt.Errorf("Wrong number of parts to AuditConfig id %s; expected 'resource_name service'.", s) | ||
} | ||
id, service := s[0], s[1] | ||
|
||
// Set the ID only to the first part so all IAM types can share the same resourceIdParserFunc. | ||
d.SetId(id) | ||
d.Set("service", service) | ||
err := resourceIdParser(d, config) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Set the ID again so that the ID matches the ID it would have if it had been created via TF. | ||
// Use the current ID in case it changed in the resourceIdParserFunc. | ||
d.SetId(d.Id() + "/audit_config/" + service) | ||
return []*schema.ResourceData{d}, nil | ||
} | ||
} | ||
|
||
func resourceIamAuditConfigUpdate(newUpdaterFunc newResourceIamUpdaterFunc) schema.UpdateFunc { | ||
return func(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
updater, err := newUpdaterFunc(d, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
ac := getResourceIamAuditConfig(d) | ||
err = iamPolicyReadModifyWrite(updater, func(p *cloudresourcemanager.Policy) error { | ||
var found bool | ||
for pos, b := range p.AuditConfigs { | ||
if b.Service != ac.Service { | ||
continue | ||
} | ||
found = true | ||
p.AuditConfigs[pos] = ac | ||
break | ||
} | ||
if !found { | ||
p.AuditConfigs = append(p.AuditConfigs, ac) | ||
} | ||
return nil | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return resourceIamAuditConfigRead(newUpdaterFunc)(d, meta) | ||
} | ||
} | ||
|
||
func resourceIamAuditConfigDelete(newUpdaterFunc newResourceIamUpdaterFunc) schema.DeleteFunc { | ||
return func(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
updater, err := newUpdaterFunc(d, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
ac := getResourceIamAuditConfig(d) | ||
err = iamPolicyReadModifyWrite(updater, func(p *cloudresourcemanager.Policy) error { | ||
toRemove := -1 | ||
for pos, b := range p.AuditConfigs { | ||
if b.Service != ac.Service { | ||
continue | ||
} | ||
toRemove = pos | ||
break | ||
} | ||
if toRemove < 0 { | ||
log.Printf("[DEBUG]: Policy audit configs for %s did not include an audit config for service %q", updater.DescribeResource(), ac.Service) | ||
return nil | ||
} | ||
|
||
p.AuditConfigs = append(p.AuditConfigs[:toRemove], p.AuditConfigs[toRemove+1:]...) | ||
return nil | ||
}) | ||
if err != nil { | ||
if isGoogleApiErrorWithCode(err, 404) { | ||
log.Printf("[DEBUG]: Resource %s is missing or deleted, marking policy audit config as deleted", updater.DescribeResource()) | ||
return nil | ||
} | ||
return err | ||
} | ||
|
||
return resourceIamAuditConfigRead(newUpdaterFunc)(d, meta) | ||
} | ||
} | ||
|
||
func getResourceIamAuditConfig(d *schema.ResourceData) *cloudresourcemanager.AuditConfig { | ||
auditLogConfigSet := d.Get("audit_log_config").(*schema.Set) | ||
auditLogConfigs := make([]*cloudresourcemanager.AuditLogConfig, auditLogConfigSet.Len()) | ||
for x, y := range auditLogConfigSet.List() { | ||
logConfig := y.(map[string]interface{}) | ||
auditLogConfigs[x] = &cloudresourcemanager.AuditLogConfig{ | ||
LogType: logConfig["log_type"].(string), | ||
ExemptedMembers: convertStringArr(logConfig["exempted_members"].(*schema.Set).List()), | ||
} | ||
} | ||
return &cloudresourcemanager.AuditConfig{ | ||
AuditLogConfigs: auditLogConfigs, | ||
Service: d.Get("service").(string), | ||
} | ||
} | ||
|
||
func flattenAuditLogConfigs(configs []*cloudresourcemanager.AuditLogConfig) *schema.Set { | ||
res := schema.NewSet(schema.HashResource(iamAuditConfigSchema["audit_log_config"].Elem.(*schema.Resource)), []interface{}{}) | ||
for _, conf := range configs { | ||
res.Add(map[string]interface{}{ | ||
"log_type": conf.LogType, | ||
"exempted_members": schema.NewSet(schema.HashSchema(iamAuditConfigSchema["audit_log_config"].Elem.(*schema.Resource).Schema["exempted_members"].Elem.(*schema.Schema)), convertStringArrToInterface(conf.ExemptedMembers)), | ||
}) | ||
} | ||
return res | ||
} |
Oops, something went wrong.