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.

Reply via email to