This is an automated email from the ASF dual-hosted git repository.
sureshanaparti pushed a commit to branch main
in repository
https://gitbox.apache.org/repos/asf/cloudstack-terraform-provider.git
The following commit(s) were added to refs/heads/main by this push:
new c2457ff Add datasource for security groups (#328)
c2457ff is described below
commit c2457ffd5316ea51643c622326514287221aa712
Author: bddvlpr <[email protected]>
AuthorDate: Fri Sep 4 13:51:31 2026 +0200
Add datasource for security groups (#328)
---
.../data_source_cloudstack_security_group.go | 142 +++++++++++++++++++++
.../data_source_cloudstack_security_group_test.go | 68 ++++++++++
cloudstack/provider.go | 1 +
website/docs/README.md | 1 +
website/docs/d/security_group.html.markdown | 41 ++++++
5 files changed, 253 insertions(+)
diff --git a/cloudstack/data_source_cloudstack_security_group.go
b/cloudstack/data_source_cloudstack_security_group.go
new file mode 100644
index 0000000..9a034b7
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_security_group.go
@@ -0,0 +1,142 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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 cloudstack
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func dataSourceCloudstackSecurityGroup() *schema.Resource {
+ return &schema.Resource{
+ Read: dataSourceCloudstackSecurityGroupRead,
+ Schema: map[string]*schema.Schema{
+ "filter": dataSourceFiltersSchema(),
+
+ //Computed values
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "description": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "project": {
+ Type: schema.TypeString,
+ Computed: true,
+ Optional: true,
+ },
+ },
+ }
+}
+
+func dataSourceCloudstackSecurityGroupRead(d *schema.ResourceData, meta
interface{}) error {
+ cs := meta.(*cloudstack.CloudStackClient)
+ p := cs.SecurityGroup.NewListSecurityGroupsParams()
+
+ // If there is a project supplied, we retrieve and set the project id
+ if err := setProjectid(p, cs, d); err != nil {
+ return err
+ }
+
+ securityGroups, err := cs.SecurityGroup.ListSecurityGroups(p)
+ if err != nil {
+ return fmt.Errorf("failed to list security groups: %s", err)
+ }
+
+ filters := d.Get("filter").(*schema.Set)
+ var matches []*cloudstack.SecurityGroup
+
+ for _, securityGroup := range securityGroups.SecurityGroups {
+ match, err := applySecurityGroupFilters(securityGroup, filters)
+ if err != nil {
+ return err
+ }
+ if match {
+ matches = append(matches, securityGroup)
+ }
+ }
+
+ if len(matches) == 0 {
+ return fmt.Errorf("no security group matches the specified
filters")
+ }
+ if len(matches) > 1 {
+ return fmt.Errorf("multiple security groups match the specified
filters")
+ }
+
+ securityGroup := matches[0]
+ log.Printf("[DEBUG] Selected security group: %s", securityGroup.Name)
+
+ return securityGroupDescriptionAttributes(d, securityGroup)
+}
+
+func securityGroupDescriptionAttributes(d *schema.ResourceData, securityGroup
*cloudstack.SecurityGroup) error {
+ d.SetId(securityGroup.Id)
+
+ if err := d.Set("name", securityGroup.Name); err != nil {
+ return fmt.Errorf("failed to set security group name: %s", err)
+ }
+ if err := d.Set("description", securityGroup.Description); err != nil {
+ return fmt.Errorf("failed to set security group description:
%s", err)
+ }
+
+ setValueOrID(d, "project", securityGroup.Project,
securityGroup.Projectid)
+
+ return nil
+}
+
+func applySecurityGroupFilters(securityGroup *cloudstack.SecurityGroup,
filters *schema.Set) (bool, error) {
+ securityGroupJSON, err := json.Marshal(securityGroup)
+ if err != nil {
+ return false, fmt.Errorf("failed to encode security group: %s",
err)
+ }
+
+ var fields map[string]interface{}
+ if err := json.Unmarshal(securityGroupJSON, &fields); err != nil {
+ return false, fmt.Errorf("failed to decode security group: %s",
err)
+ }
+
+ for _, filter := range filters.List() {
+ values := filter.(map[string]interface{})
+ pattern, err := regexp.Compile(values["value"].(string))
+ if err != nil {
+ return false, fmt.Errorf("invalid regex: %s", err)
+ }
+
+ name := strings.ReplaceAll(values["name"].(string), "_", "")
+ value, ok := fields[name]
+ if !ok {
+ return false, fmt.Errorf("field %q does not exist in
security group", values["name"].(string))
+ }
+
+ if !pattern.MatchString(fmt.Sprint(value)) {
+ return false, nil
+ }
+ }
+
+ return true, nil
+}
diff --git a/cloudstack/data_source_cloudstack_security_group_test.go
b/cloudstack/data_source_cloudstack_security_group_test.go
new file mode 100644
index 0000000..6979578
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_security_group_test.go
@@ -0,0 +1,68 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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 cloudstack
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccSecurityGroupDataSource_basic(t *testing.T) {
+ resourceName := "cloudstack_security_group.security-group-resource"
+ dataSourceName :=
"data.cloudstack_security_group.security-group-data-source"
+ securityGroupName := "terraform-security-group-data-source-" +
id.UniqueId()
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config:
testSecurityGroupDataSourceConfig(securityGroupName),
+ Check: resource.ComposeTestCheckFunc(
+
resource.TestCheckResourceAttrPair(dataSourceName, "id", resourceName, "id"),
+
resource.TestCheckResourceAttrPair(dataSourceName, "name", resourceName,
"name"),
+
resource.TestCheckResourceAttrPair(dataSourceName, "description", resourceName,
"description"),
+ ),
+ },
+ },
+ })
+}
+
+func testSecurityGroupDataSourceConfig(name string) string {
+ return fmt.Sprintf(`
+resource "cloudstack_security_group" "security-group-resource" {
+ name = "%[1]s"
+ description = "Security group data source acceptance test"
+}
+
+data "cloudstack_security_group" "security-group-data-source" {
+ filter {
+ name = "name"
+ value = "^%[1]s$"
+ }
+ depends_on = [cloudstack_security_group.security-group-resource]
+}
+`,
+ name,
+ )
+}
diff --git a/cloudstack/provider.go b/cloudstack/provider.go
index 88756f6..dffb67a 100644
--- a/cloudstack/provider.go
+++ b/cloudstack/provider.go
@@ -107,6 +107,7 @@ func Provider() *schema.Provider {
"cloudstack_quota_tariff":
dataSourceCloudStackQuotaTariff(),
"cloudstack_user_data":
dataSourceCloudstackUserData(),
"cloudstack_kubernetes_cluster_config":
dataSourceCloudstackKubernetesClusterConfig(),
+ "cloudstack_security_group":
dataSourceCloudstackSecurityGroup(),
},
ResourcesMap: map[string]*schema.Resource{
diff --git a/website/docs/README.md b/website/docs/README.md
index 2fe8dd3..c99234a 100644
--- a/website/docs/README.md
+++ b/website/docs/README.md
@@ -62,6 +62,7 @@ The following arguments are supported:
- [instance](./d/instance.html.markdown)
- [ipaddress](./d/ipaddress.html.markdown)
- [network_offering](./d/network_offering.html.markdown)
+- [security_group](./d/security_group.html.markdown)
- [service_offering](./d/service_offering.html.markdown)
- [ssh_keypair](./d/ssh_keypair.html.markdown)
- [template](./d/template.html.markdown)
diff --git a/website/docs/d/security_group.html.markdown
b/website/docs/d/security_group.html.markdown
new file mode 100644
index 0000000..a31d11a
--- /dev/null
+++ b/website/docs/d/security_group.html.markdown
@@ -0,0 +1,41 @@
+---
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_security_group"
+description: |-
+ Gets information about a security group.
+---
+
+# cloudstack_security_group
+
+Use this data source to get information about a security group for use in
other resources.
+
+## Example Usage
+
+```hcl
+data "cloudstack_security_group" "web" {
+ project = "my-project"
+
+ filter {
+ name = "name"
+ value = "^web-servers$"
+ }
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `filter` - (Required) One or more name/regular-expression pairs used to
select the security group. Filter names use snake case, for example `name`,
`description`, or `project`.
+* `project` - (Optional) The name or ID of the project containing the security
group.
+
+The filters must identify exactly one security group.
+
+## Attributes Reference
+
+The following attributes are exported:
+
+* `id` - The ID of the security group.
+* `name` - The name of the security group.
+* `description` - The description of the security group.
+* `project` - The name of the project containing the security group, or its ID
when the data source was configured with a project ID.