Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 76 additions & 9 deletions cloudstack/resource_cloudstack_security_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,20 @@ func resourceCloudStackSecurityGroup() *schema.Resource {
ForceNew: true,
},

"project": {
"account": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"domainid": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},

"projectid": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Expand All @@ -66,6 +79,18 @@ func resourceCloudStackSecurityGroupCreate(d *schema.ResourceData, meta interfac

name := d.Get("name").(string)

// Validate that account is used with domainid
if account, ok := d.GetOk("account"); ok {
if _, domainOk := d.GetOk("domainid"); !domainOk {
return fmt.Errorf("account parameter requires domainid to be set")
}
// Account and projectid are mutually exclusive
if _, projectOk := d.GetOk("projectid"); projectOk {
return fmt.Errorf("account and projectid parameters are mutually exclusive")
}
log.Printf("[DEBUG] Creating security group %s for account %s", name, account)
}

// Create a new parameter struct
p := cs.SecurityGroup.NewCreateSecurityGroupParams(name)

Expand All @@ -76,9 +101,27 @@ func resourceCloudStackSecurityGroupCreate(d *schema.ResourceData, meta interfac
p.SetDescription(name)
}

// If there is a project supplied, we retrieve and set the project id
if err := setProjectid(p, cs, d); err != nil {
return err
// Set the account if provided
if account, ok := d.GetOk("account"); ok {
p.SetAccount(account.(string))
}

// If there is a domainid supplied, retrieve and set the domain id (supports both names and IDs)
if domain, ok := d.GetOk("domainid"); ok {
domainID, err := retrieveID(cs, "domain", domain.(string))
if err != nil {
return err.Error()
}
p.SetDomainid(domainID)
}

// If there is a projectid supplied, retrieve and set the project id (supports both names and IDs)
if project, ok := d.GetOk("projectid"); ok {
projectID, err := retrieveID(cs, "project", project.(string))
if err != nil {
return err.Error()
}
p.SetProjectid(projectID)
}

r, err := cs.SecurityGroup.CreateSecurityGroup(p)
Expand All @@ -97,7 +140,7 @@ func resourceCloudStackSecurityGroupRead(d *schema.ResourceData, meta interface{
// Get the security group details
sg, count, err := cs.SecurityGroup.GetSecurityGroupByID(
d.Id(),
cloudstack.WithProject(d.Get("project").(string)),
cloudstack.WithProject(d.Get("projectid").(string)),
)
if err != nil {
if count == 0 {
Expand All @@ -113,7 +156,13 @@ func resourceCloudStackSecurityGroupRead(d *schema.ResourceData, meta interface{
d.Set("name", sg.Name)
d.Set("description", sg.Description)

setValueOrID(d, "project", sg.Project, sg.Projectid)
// Only set account if it was explicitly configured
if _, ok := d.GetOk("account"); ok {
d.Set("account", sg.Account)
}

setValueOrID(d, "domainid", sg.Domain, sg.Domainid)
setValueOrID(d, "projectid", sg.Project, sg.Projectid)

return nil
}
Expand All @@ -125,9 +174,27 @@ func resourceCloudStackSecurityGroupDelete(d *schema.ResourceData, meta interfac
p := cs.SecurityGroup.NewDeleteSecurityGroupParams()
p.SetId(d.Id())

// If there is a project supplied, we retrieve and set the project id
if err := setProjectid(p, cs, d); err != nil {
return err
// Set the account if provided
if account, ok := d.GetOk("account"); ok {
p.SetAccount(account.(string))
}

// If there is a domainid supplied, retrieve and set the domain id (supports both names and IDs)
if domain, ok := d.GetOk("domainid"); ok {
domainID, err := retrieveID(cs, "domain", domain.(string))
if err != nil {
return err.Error()
}
p.SetDomainid(domainID)
}

// If there is a projectid supplied, retrieve and set the project id (supports both names and IDs)
if project, ok := d.GetOk("projectid"); ok {
projectID, err := retrieveID(cs, "project", project.(string))
if err != nil {
return err.Error()
}
p.SetProjectid(projectID)
}

// Delete the security group
Expand Down
68 changes: 68 additions & 0 deletions cloudstack/resource_cloudstack_security_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,47 @@ func TestAccCloudStackSecurityGroup_basic(t *testing.T) {
})
}

func TestAccCloudStackSecurityGroup_project(t *testing.T) {
var sg cloudstack.SecurityGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudStackSecurityGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudStackSecurityGroup_project,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudStackSecurityGroupExists(
"cloudstack_security_group.foo", &sg),
resource.TestCheckResourceAttrPair(
"cloudstack_security_group.foo", "projectid",
"cloudstack_project.test", "id"),
),
},
},
})
}

func TestAccCloudStackSecurityGroup_account(t *testing.T) {
var sg cloudstack.SecurityGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudStackSecurityGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudStackSecurityGroup_account,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudStackSecurityGroupExists(
"cloudstack_security_group.foo", &sg),
resource.TestCheckResourceAttr(
"cloudstack_security_group.foo", "account", "admin"),
),
},
},
})
}

func TestAccCloudStackSecurityGroup_import(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Expand Down Expand Up @@ -136,3 +177,30 @@ resource "cloudstack_security_group" "foo" {
name = "terraform-security-group"
description = "terraform-security-group-text"
}`

const testAccCloudStackSecurityGroup_project = `
resource "cloudstack_project" "test" {
name = "terraform-security-group-test-project"
displaytext = "Terraform Security Group Test Project"
}

resource "cloudstack_security_group" "foo" {
name = "terraform-security-group-project"
description = "terraform-security-group-project-text"
projectid = cloudstack_project.test.id
}`

const testAccCloudStackSecurityGroup_account = `
data "cloudstack_domain" "root" {
filter {
name = "name"
value = "ROOT"
}
}

resource "cloudstack_security_group" "foo" {
name = "terraform-security-group-account"
description = "terraform-security-group-account-text"
account = "admin"
domainid = data.cloudstack_domain.root.id
}`
13 changes: 13 additions & 0 deletions cloudstack/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,19 @@ func setProjectid(p cloudstack.ProjectIDSetter, cs *cloudstack.CloudStackClient,
return nil
}

// If there is a domain supplied, we retrieve and set the domain id
func setDomainid(p cloudstack.DomainIDSetter, cs *cloudstack.CloudStackClient, d *schema.ResourceData) error {
if domain, ok := d.GetOk("domain"); ok {
domainid, e := retrieveID(cs, "domain", domain.(string))
if e != nil {
return e.Error()
}
p.SetDomainid(domainid)
}

return nil
}

// importStatePassthrough is a generic importer with project support.
func importStatePassthrough(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
// Try to split the ID to extract the optional project name.
Expand Down
43 changes: 42 additions & 1 deletion website/docs/r/security_group.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,39 @@ resource "cloudstack_security_group" "default" {
}
```

### With Account and Domain

```hcl
data "cloudstack_domain" "my_domain" {
filter {
name = "name"
value = "ROOT"
}
}

resource "cloudstack_security_group" "account_sg" {
name = "allow_web"
description = "Allow access to HTTP and HTTPS"
account = "my-account"
domainid = data.cloudstack_domain.my_domain.id
}
```

### With Project

```hcl
resource "cloudstack_project" "my_project" {
name = "my-project"
displaytext = "My Project"
}

resource "cloudstack_security_group" "project_sg" {
name = "allow_web"
description = "Allow access to HTTP and HTTPS"
projectid = cloudstack_project.my_project.id
}
```

## Argument Reference

The following arguments are supported:
Expand All @@ -29,9 +62,17 @@ The following arguments are supported:
* `description` - (Optional) The description of the security group. Changing
this forces a new resource to be created.

* `project` - (Optional) The name or ID of the project to create this security
* `account` - (Optional) The account name to create the security group for.
Must be used with `domainid`. Cannot be used with `projectid`. Changing this
forces a new resource to be created.

* `domainid` - (Optional) The name or ID of the domain to create this security
group in. Changing this forces a new resource to be created.

* `projectid` - (Optional) The name or ID of the project to create this security
group in. Cannot be used with `account`. Changing this forces a new
resource to be created.

## Attributes Reference

The following attributes are exported:
Expand Down
Loading