diff --git a/citrixadc/provider.go b/citrixadc/provider.go index 7780e545..dcc297b9 100644 --- a/citrixadc/provider.go +++ b/citrixadc/provider.go @@ -865,6 +865,7 @@ func providerResources() map[string]*schema.Resource { "citrixadc_gslbvserver_lbpolicy_binding": resourceCitrixAdcGslbvserver_lbpolicy_binding(), "citrixadc_lbvserver_lbpolicy_binding": resourceCitrixAdcLbvserver_lbpolicy_binding(), "citrixadc_sslprofile_ecccurve_binding": resourceCitrixAdcSslprofile_ecccurve_binding(), + "citrixadc_systemuser_systemcmdpolicy_binding": resourceCitrixAdcSystemuser_systemcmdpolicy_binding(), } } diff --git a/citrixadc/resource_citrixadc_systemuser.go b/citrixadc/resource_citrixadc_systemuser.go index 89c1394d..304e83d0 100644 --- a/citrixadc/resource_citrixadc_systemuser.go +++ b/citrixadc/resource_citrixadc_systemuser.go @@ -120,10 +120,12 @@ func createSystemuserFunc(d *schema.ResourceData, meta interface{}) error { if err != nil { return err } - - err = updateCmdpolicyBindings(d, meta) - if err != nil { - return err + // Ignore bindings unless there is an explicit configuration for it + if _, ok := d.GetOk("cmdpolicybinding"); ok { + err = updateCmdpolicyBindings(d, meta) + if err != nil { + return err + } } d.SetId(username) @@ -172,9 +174,11 @@ func readSystemuserFunc(d *schema.ResourceData, meta interface{}) error { d.Set("timeout", data["timeout"]) d.Set("allowedmanagementinterface", data["allowedmanagementinterface"]) - err = readCmdpolicybindings(d, meta) - if err != nil { - return err + if _, ok := d.GetOk("cmdpolicybinding"); ok { + err = readCmdpolicybindings(d, meta) + if err != nil { + return err + } } return nil diff --git a/citrixadc/resource_citrixadc_systemuser_systemcmdpolicy_binding.go b/citrixadc/resource_citrixadc_systemuser_systemcmdpolicy_binding.go new file mode 100644 index 00000000..b48d99ff --- /dev/null +++ b/citrixadc/resource_citrixadc_systemuser_systemcmdpolicy_binding.go @@ -0,0 +1,153 @@ +package citrixadc + +import ( + "net/url" + + "github.com/citrix/adc-nitro-go/resource/config/system" + "github.com/citrix/adc-nitro-go/service" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + + "fmt" + "log" + "strings" +) + +func resourceCitrixAdcSystemuser_systemcmdpolicy_binding() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + Create: createSystemuser_systemcmdpolicy_bindingFunc, + Read: readSystemuser_systemcmdpolicy_bindingFunc, + Delete: deleteSystemuser_systemcmdpolicy_bindingFunc, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "policyname": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "priority": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "username": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func createSystemuser_systemcmdpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] citrixadc-provider: In createSystemuser_systemcmdpolicy_bindingFunc") + client := meta.(*NetScalerNitroClient).client + username := d.Get("username") + policyname := d.Get("policyname") + bindingId := fmt.Sprintf("%s,%s", username, policyname) + + systemuser_systemcmdpolicy_binding := system.Systemusersystemcmdpolicybinding{ + Policyname: d.Get("policyname").(string), + Priority: d.Get("priority").(int), + Username: d.Get("username").(string), + } + + _, err := client.AddResource(service.Systemuser_systemcmdpolicy_binding.Type(), bindingId, &systemuser_systemcmdpolicy_binding) + if err != nil { + return err + } + + d.SetId(bindingId) + + err = readSystemuser_systemcmdpolicy_bindingFunc(d, meta) + if err != nil { + log.Printf("[ERROR] netscaler-provider: ?? we just created this systemuser_systemcmdpolicy_binding but we can't read it ?? %s", bindingId) + return nil + } + return nil +} + +func readSystemuser_systemcmdpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] citrixadc-provider: In readSystemuser_systemcmdpolicy_bindingFunc") + client := meta.(*NetScalerNitroClient).client + bindingId := d.Id() + idSlice := strings.SplitN(bindingId, ",", 2) + + username := idSlice[0] + policyname := idSlice[1] + + log.Printf("[DEBUG] citrixadc-provider: Reading systemuser_systemcmdpolicy_binding state %s", bindingId) + + findParams := service.FindParams{ + ResourceType: "systemuser_systemcmdpolicy_binding", + ResourceName: username, + ResourceMissingErrorCode: 258, + } + dataArr, err := client.FindResourceArrayWithParams(findParams) + + // Unexpected error + if err != nil { + log.Printf("[DEBUG] citrixadc-provider: Error during FindResourceArrayWithParams %s", err.Error()) + return err + } + + // Resource is missing + if len(dataArr) == 0 { + log.Printf("[DEBUG] citrixadc-provider: FindResourceArrayWithParams returned empty array") + log.Printf("[WARN] citrixadc-provider: Clearing systemuser_systemcmdpolicy_binding state %s", bindingId) + d.SetId("") + return nil + } + + // Iterate through results to find the one with the right id + foundIndex := -1 + for i, v := range dataArr { + if v["policyname"].(string) == policyname { + foundIndex = i + break + } + } + + // Resource is missing + if foundIndex == -1 { + log.Printf("[DEBUG] citrixadc-provider: FindResourceArrayWithParams secondIdComponent not found in array") + log.Printf("[WARN] citrixadc-provider: Clearing systemuser_systemcmdpolicy_binding state %s", bindingId) + d.SetId("") + return nil + } + // Fallthrough + + data := dataArr[foundIndex] + + d.Set("policyname", data["policyname"]) + setToInt("priority", d, data["priority"]) + d.Set("username", data["username"]) + + return nil + +} + +func deleteSystemuser_systemcmdpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] citrixadc-provider: In deleteSystemuser_systemcmdpolicy_bindingFunc") + client := meta.(*NetScalerNitroClient).client + + bindingId := d.Id() + idSlice := strings.SplitN(bindingId, ",", 2) + + name := idSlice[0] + policyname := idSlice[1] + + args := make([]string, 0) + args = append(args, fmt.Sprintf("policyname:%s", url.QueryEscape(policyname))) + + err := client.DeleteResourceWithArgs(service.Systemuser_systemcmdpolicy_binding.Type(), name, args) + if err != nil { + return err + } + + d.SetId("") + + return nil +} diff --git a/citrixadc/resource_citrixadc_systemuser_systemcmdpolicy_binding_test.go b/citrixadc/resource_citrixadc_systemuser_systemcmdpolicy_binding_test.go new file mode 100644 index 00000000..e373e608 --- /dev/null +++ b/citrixadc/resource_citrixadc_systemuser_systemcmdpolicy_binding_test.go @@ -0,0 +1,202 @@ +/* +Copyright 2016 Citrix Systems, Inc + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package citrixadc + +import ( + "fmt" + "github.com/citrix/adc-nitro-go/service" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "strings" + "testing" +) + +const testAccSystemuser_systemcmdpolicy_binding_basic = ` + resource "citrixadc_systemuser" "tf_user" { + username = "tf_user" + password = "tf_password" + timeout = 200 + } + + resource "citrixadc_systemcmdpolicy" "tf_policy" { + policyname = "tf_policy" + action = "DENY" + cmdspec = "add.*" + } + + resource "citrixadc_systemuser_systemcmdpolicy_binding" "tf_bind" { + username = citrixadc_systemuser.tf_user.username + policyname = citrixadc_systemcmdpolicy.tf_policy.policyname + priority = 100 + } +` + +const testAccSystemuser_systemcmdpolicy_binding_basic_step2 = ` + # Keep the above bound resources without the actual binding to check proper deletion + resource "citrixadc_systemuser" "tf_user" { + username = "tf_user" + password = "tf_password" + timeout = 200 + } + + resource "citrixadc_systemcmdpolicy" "tf_policy" { + policyname = "tf_policy" + action = "DENY" + cmdspec = "add.*" + } +` + +func TestAccSystemuser_systemcmdpolicy_binding_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSystemuser_systemcmdpolicy_bindingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccSystemuser_systemcmdpolicy_binding_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckSystemuser_systemcmdpolicy_bindingExist("citrixadc_systemuser_systemcmdpolicy_binding.tf_bind", nil), + ), + }, + { + Config: testAccSystemuser_systemcmdpolicy_binding_basic_step2, + Check: resource.ComposeTestCheckFunc( + testAccCheckSystemuser_systemcmdpolicy_bindingNotExist("citrixadc_systemuser_systemcmdpolicy_binding.tf_bind", "tf_user,tf_policy"), + ), + }, + }, + }) +} + +func testAccCheckSystemuser_systemcmdpolicy_bindingExist(n string, id *string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No systemuser_systemcmdpolicy_binding id is set") + } + + if id != nil { + if *id != "" && *id != rs.Primary.ID { + return fmt.Errorf("Resource ID has changed!") + } + + *id = rs.Primary.ID + } + + client := testAccProvider.Meta().(*NetScalerNitroClient).client + + bindingId := rs.Primary.ID + + idSlice := strings.SplitN(bindingId, ",", 2) + + username := idSlice[0] + policyname := idSlice[1] + + findParams := service.FindParams{ + ResourceType: "systemuser_systemcmdpolicy_binding", + ResourceName: username, + ResourceMissingErrorCode: 258, + } + dataArr, err := client.FindResourceArrayWithParams(findParams) + + // Unexpected error + if err != nil { + return err + } + + // Iterate through results to find the one with the matching secondIdComponent + found := false + for _, v := range dataArr { + if v["policyname"].(string) == policyname { + found = true + break + } + } + + if !found { + return fmt.Errorf("systemuser_systemcmdpolicy_binding %s not found", n) + } + + return nil + } +} + +func testAccCheckSystemuser_systemcmdpolicy_bindingNotExist(n string, id string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*NetScalerNitroClient).client + + if !strings.Contains(id, ",") { + return fmt.Errorf("Invalid id string %v. The id string must contain a comma.", id) + } + idSlice := strings.SplitN(id, ",", 2) + + username := idSlice[0] + policyname := idSlice[1] + + findParams := service.FindParams{ + ResourceType: "systemuser_systemcmdpolicy_binding", + ResourceName: username, + ResourceMissingErrorCode: 258, + } + dataArr, err := client.FindResourceArrayWithParams(findParams) + + // Unexpected error + if err != nil { + return err + } + + // Iterate through results to hopefully not find the one with the matching secondIdComponent + found := false + for _, v := range dataArr { + if v["policyname"].(string) == policyname { + found = true + break + } + } + + if found { + return fmt.Errorf("systemuser_systemcmdpolicy_binding %s was found, but it should have been destroyed", n) + } + + return nil + } +} + +func testAccCheckSystemuser_systemcmdpolicy_bindingDestroy(s *terraform.State) error { + nsClient := testAccProvider.Meta().(*NetScalerNitroClient).client + + for _, rs := range s.RootModule().Resources { + if rs.Type != "citrixadc_systemuser_systemcmdpolicy_binding" { + continue + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No name is set") + } + + _, err := nsClient.FindResource(service.Systemuser_systemcmdpolicy_binding.Type(), rs.Primary.ID) + if err == nil { + return fmt.Errorf("systemuser_systemcmdpolicy_binding %s still exists", rs.Primary.ID) + } + + } + + return nil +} diff --git a/docs/resources/systemuser.md b/docs/resources/systemuser.md index a6e8c214..396013b7 100644 --- a/docs/resources/systemuser.md +++ b/docs/resources/systemuser.md @@ -32,7 +32,12 @@ resource "citrixadc_systemuser" "tf_user" { * `hashedpassword` - (Optional) * `allowedmanagementinterface` - (Optional) Allowed Management interfaces to the system user. By default user is allowed from both API and CLI interfaces. If management interface for a user is set to API, then user is not allowed to access NS through CLI. GUI interface will come under API interface. Default value: [ NS_INTERFACE_ALL ]. Possible values = [ CLI, API ] -* `cmdpolicybinding` - (Optional) A set of blocks binding systemcommandpolicies to the systemuser. See below for details. +* `cmdpolicybinding` - (Optional) A set of blocks binding systemcommandpolicies to the systemuser. See below for details. (deprecates soon) + + +!> +[**DEPRECATED**] Please use `systemuser_systemcmdpolicy_binding` to bind `systemcmdpolicy` to `systemuser` insted of this resource. The support for binding `systemcmdpolicy` to `systemuser` in this resource will get deprecated soon. + A `cmdpolicybinding` block can contain the following attributes diff --git a/docs/resources/systemuser_systemcmdpolicy_binding.md b/docs/resources/systemuser_systemcmdpolicy_binding.md new file mode 100644 index 00000000..951bbb4c --- /dev/null +++ b/docs/resources/systemuser_systemcmdpolicy_binding.md @@ -0,0 +1,57 @@ +--- +subcategory: "System" +--- + +# Resource: systemuser_systemcmdpolicy_binding + +The systemuser_systemcmdpolicy_binding resource is used to bind systemuser and systemcmdpolicy. + +~> If you are using this resource to bind systemcmdpolicy to a systemuser, do not define the `cmdpolicybinding` attribute in the systemuser resource. + + +## Example usage + +```hcl + +resource "citrixadc_systemuser" "tf_user" { + username = "tf_user" + password = "tf_password" + timeout = 200 +} + +resource "citrixadc_systemcmdpolicy" "tf_policy" { + policyname = "tf_policy" + action = "DENY" + cmdspec = "add.*" +} + +resource "citrixadc_systemuser_systemcmdpolicy_binding" "tf_bind" { + username = citrixadc_systemuser.tf_user.username + policyname = citrixadc_systemcmdpolicy.tf_policy.policyname + priority = 100 +} + +``` + + +## Argument Reference + +* `policyname` - (Required) The name of command policy. +* `priority` - (Required) The priority of the policy. +* `username` - (Required) Name of the system-user entry to which to bind the command policy. Minimum length = 1 + + +## Attribute Reference + +In addition to the arguments, the following attributes are available: + +* `id` - The id of the systemuser_systemcmdpolicy_binding. It is the concatenation of the `username` and `policyname` attributes separated by a comma. + + +## Import + +A systemuser_systemcmdpolicy_binding can be imported using its id, e.g. + +```shell +terraform import citrixadc_systemuser_systemcmdpolicy_binding.tf_bind tf_user,tf_policy +```