This is an automated email from the ASF dual-hosted git repository.

dahn 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 96f287a  Add `cloudstack_limits` data source and resource  (#197)
96f287a is described below

commit 96f287ac61122f05e385fb3df6a426965be18dae
Author: Ian <[email protected]>
AuthorDate: Thu Sep 18 05:08:04 2025 -0700

    Add `cloudstack_limits` data source and resource  (#197)
---
 cloudstack/data_source_cloudstack_limits.go      | 210 ++++++++
 cloudstack/data_source_cloudstack_limits_test.go | 198 ++++++++
 cloudstack/provider.go                           |   2 +
 cloudstack/resource_cloudstack_account.go        |   6 +-
 cloudstack/resource_cloudstack_limits.go         | 471 ++++++++++++++++++
 cloudstack/resource_cloudstack_limits_test.go    | 579 +++++++++++++++++++++++
 website/docs/d/limits.html.markdown              |  68 +++
 website/docs/r/limits.html.markdown              |  88 ++++
 8 files changed, 1619 insertions(+), 3 deletions(-)

diff --git a/cloudstack/data_source_cloudstack_limits.go 
b/cloudstack/data_source_cloudstack_limits.go
new file mode 100644
index 0000000..170d7bc
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_limits.go
@@ -0,0 +1,210 @@
+// 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 (
+       "bytes"
+       "crypto/sha256"
+       "encoding/hex"
+       "fmt"
+
+       "github.com/apache/cloudstack-go/v2/cloudstack"
+       "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+       "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+)
+
+func dataSourceCloudStackLimits() *schema.Resource {
+       return &schema.Resource{
+               Read: dataSourceCloudStackLimitsRead,
+               Schema: map[string]*schema.Schema{
+                       "type": {
+                               Type:     schema.TypeString,
+                               Optional: true,
+                               ValidateFunc: validation.StringInSlice([]string{
+                                       "instance", "ip", "volume", "snapshot", 
"template", "project", "network", "vpc",
+                                       "cpu", "memory", "primarystorage", 
"secondarystorage",
+                               }, false), // false disables case-insensitive 
matching
+                               Description: "The type of resource to list the 
limits. Available types are: " +
+                                       "instance, ip, volume, snapshot, 
template, project, network, vpc, cpu, memory, " +
+                                       "primarystorage, secondarystorage",
+                       },
+                       "account": {
+                               Type:        schema.TypeString,
+                               Optional:    true,
+                               Description: "List resources by account. Must 
be used with the domain_id parameter.",
+                       },
+                       "domain_id": {
+                               Type:        schema.TypeString,
+                               Optional:    true,
+                               Description: "List only resources belonging to 
the domain specified.",
+                       },
+                       "project": {
+                               Type:        schema.TypeString,
+                               Optional:    true,
+                               Description: "List resource limits by project.",
+                       },
+                       "limits": {
+                               Type:     schema.TypeList,
+                               Computed: true,
+                               Elem: &schema.Resource{
+                                       Schema: map[string]*schema.Schema{
+                                               "resourcetype": {
+                                                       Type:     
schema.TypeString,
+                                                       Computed: true,
+                                               },
+                                               "resourcetypename": {
+                                                       Type:     
schema.TypeString,
+                                                       Computed: true,
+                                               },
+                                               "account": {
+                                                       Type:     
schema.TypeString,
+                                                       Computed: true,
+                                               },
+                                               "domain": {
+                                                       Type:     
schema.TypeString,
+                                                       Computed: true,
+                                               },
+                                               "domain_id": {
+                                                       Type:     
schema.TypeString,
+                                                       Computed: true,
+                                               },
+                                               "max": {
+                                                       Type:     
schema.TypeInt,
+                                                       Computed: true,
+                                               },
+                                               "project": {
+                                                       Type:     
schema.TypeString,
+                                                       Computed: true,
+                                               },
+                                               "projectid": {
+                                                       Type:     
schema.TypeString,
+                                                       Computed: true,
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+}
+
+func dataSourceCloudStackLimitsRead(d *schema.ResourceData, meta interface{}) 
error {
+       cs := meta.(*cloudstack.CloudStackClient)
+
+       // Create a new parameter struct
+       p := cs.Limit.NewListResourceLimitsParams()
+
+       // Set optional parameters
+       if v, ok := d.GetOk("type"); ok {
+               typeStr := v.(string)
+               if resourcetype, ok := resourceTypeMap[typeStr]; ok {
+                       p.SetResourcetype(resourcetype)
+               } else {
+                       return fmt.Errorf("invalid type value: %s", typeStr)
+               }
+       }
+
+       if v, ok := d.GetOk("account"); ok {
+               p.SetAccount(v.(string))
+       }
+
+       if v, ok := d.GetOk("domain_id"); ok {
+               p.SetDomainid(v.(string))
+       }
+
+       if v, ok := d.GetOk("project"); ok {
+               p.SetProjectid(v.(string))
+       }
+
+       // Retrieve the resource limits
+       l, err := cs.Limit.ListResourceLimits(p)
+       if err != nil {
+               return fmt.Errorf("Error retrieving resource limits: %s", err)
+       }
+
+       // Generate a unique ID for this data source
+       id := generateDataSourceID(d)
+       d.SetId(id)
+
+       limits := make([]map[string]interface{}, 0, len(l.ResourceLimits))
+
+       // Set the resource data
+       for _, limit := range l.ResourceLimits {
+               limitMap := map[string]interface{}{
+                       "resourcetype":     limit.Resourcetype,
+                       "resourcetypename": limit.Resourcetypename,
+                       "max":              limit.Max,
+               }
+
+               if limit.Account != "" {
+                       limitMap["account"] = limit.Account
+               }
+
+               if limit.Domain != "" {
+                       limitMap["domain"] = limit.Domain
+               }
+
+               if limit.Domainid != "" {
+                       limitMap["domain_id"] = limit.Domainid
+               }
+
+               if limit.Project != "" {
+                       limitMap["project"] = limit.Project
+               }
+
+               if limit.Projectid != "" {
+                       limitMap["projectid"] = limit.Projectid
+               }
+
+               limits = append(limits, limitMap)
+       }
+
+       if err := d.Set("limits", limits); err != nil {
+               return fmt.Errorf("Error setting limits: %s", err)
+       }
+
+       return nil
+}
+
+// generateDataSourceID generates a unique ID for the data source based on its 
parameters
+func generateDataSourceID(d *schema.ResourceData) string {
+       var buf bytes.Buffer
+
+       if v, ok := d.GetOk("type"); ok {
+               typeStr := v.(string)
+               if resourcetype, ok := resourceTypeMap[typeStr]; ok {
+                       buf.WriteString(fmt.Sprintf("%d-", resourcetype))
+               }
+       }
+
+       if v, ok := d.GetOk("account"); ok {
+               buf.WriteString(fmt.Sprintf("%s-", v.(string)))
+       }
+
+       if v, ok := d.GetOk("domain_id"); ok {
+               buf.WriteString(fmt.Sprintf("%s-", v.(string)))
+       }
+
+       if v, ok := d.GetOk("project"); ok {
+               buf.WriteString(fmt.Sprintf("%s-", v.(string)))
+       }
+
+       // Generate a SHA-256 hash of the buffer content
+       hash := sha256.Sum256(buf.Bytes())
+       return fmt.Sprintf("limits-%s", hex.EncodeToString(hash[:])[:8])
+}
diff --git a/cloudstack/data_source_cloudstack_limits_test.go 
b/cloudstack/data_source_cloudstack_limits_test.go
new file mode 100644
index 0000000..10b4800
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_limits_test.go
@@ -0,0 +1,198 @@
+// 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-testing/helper/resource"
+       "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+func TestAccCloudStackLimitsDataSource_basic(t *testing.T) {
+       resourceName := "data.cloudstack_limits.test"
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:  func() { testAccPreCheck(t) },
+               Providers: testAccProviders,
+               Steps: []resource.TestStep{
+                       {
+                               Config: 
testAccCloudStackLimitsDataSource_basic(),
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsDataSourceExists(resourceName),
+                                       
resource.TestCheckResourceAttrSet(resourceName, "limits.#"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimitsDataSource_withType(t *testing.T) {
+       resourceName := "data.cloudstack_limits.test"
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:  func() { testAccPreCheck(t) },
+               Providers: testAccProviders,
+               Steps: []resource.TestStep{
+                       {
+                               Config: 
testAccCloudStackLimitsDataSource_withType(),
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsDataSourceExists(resourceName),
+                                       
resource.TestCheckResourceAttrSet(resourceName, "limits.#"),
+                                       
resource.TestCheckResourceAttr(resourceName, "type", "instance"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimitsDataSource_withDomain(t *testing.T) {
+       resourceName := "data.cloudstack_limits.test"
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:  func() { testAccPreCheck(t) },
+               Providers: testAccProviders,
+               Steps: []resource.TestStep{
+                       {
+                               Config: 
testAccCloudStackLimitsDataSource_domain + 
testAccCloudStackLimitsDataSource_withDomain(),
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsDataSourceExists(resourceName),
+                                       
resource.TestCheckResourceAttrSet(resourceName, "limits.#"),
+                                       
resource.TestCheckResourceAttrSet(resourceName, "domain_id"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimitsDataSource_withAccount(t *testing.T) {
+       resourceName := "data.cloudstack_limits.test"
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:  func() { testAccPreCheck(t) },
+               Providers: testAccProviders,
+               Steps: []resource.TestStep{
+                       {
+                               Config: 
testAccCloudStackLimitsDataSource_domain + 
testAccCloudStackLimitsDataSource_withAccount(),
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsDataSourceExists(resourceName),
+                                       
resource.TestCheckResourceAttrSet(resourceName, "limits.#"),
+                                       
resource.TestCheckResourceAttrSet(resourceName, "domain_id"),
+                                       
resource.TestCheckResourceAttrSet(resourceName, "account"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimitsDataSource_multipleTypes(t *testing.T) {
+       resourceName := "data.cloudstack_limits.test"
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:  func() { testAccPreCheck(t) },
+               Providers: testAccProviders,
+               Steps: []resource.TestStep{
+                       {
+                               Config: 
testAccCloudStackLimitsDataSource_multipleTypes(),
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsDataSourceExists(resourceName),
+                                       
resource.TestCheckResourceAttrSet(resourceName, "limits.#"),
+                                       
resource.TestCheckResourceAttr(resourceName, "type", "volume"),
+                               ),
+                       },
+               },
+       })
+}
+
+func testAccCheckCloudStackLimitsDataSourceExists(n 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 Limits data source ID is set")
+               }
+
+               return nil
+       }
+}
+
+// Test configurations
+
+const testAccCloudStackLimitsDataSource_domain = `
+resource "cloudstack_domain" "test_domain" {
+  name = "test-domain-limits-ds"
+}
+
+resource "cloudstack_account" "test_account" {
+  username     = "test-account-limits-ds"
+  password     = "password"
+  first_name   = "Test"
+  last_name    = "Account"
+  email        = "[email protected]"
+  account_type = 2  # Regular user account type
+  role_id      = "4"  # Regular user role
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+func testAccCloudStackLimitsDataSource_basic() string {
+       return `
+data "cloudstack_limits" "test" {
+}
+`
+}
+
+func testAccCloudStackLimitsDataSource_withType() string {
+       return `
+data "cloudstack_limits" "test" {
+  type = "instance"
+}
+`
+}
+
+func testAccCloudStackLimitsDataSource_withDomain() string {
+       return `
+data "cloudstack_limits" "test" {
+  type      = "volume"
+  domain_id = cloudstack_domain.test_domain.id
+}
+`
+}
+
+func testAccCloudStackLimitsDataSource_withAccount() string {
+       return `
+data "cloudstack_limits" "test" {
+  type      = "snapshot"
+  account   = cloudstack_account.test_account.username
+  domain_id = cloudstack_domain.test_domain.id
+}
+`
+}
+
+func testAccCloudStackLimitsDataSource_multipleTypes() string {
+       return `
+data "cloudstack_limits" "test" {
+  type = "volume"
+}
+`
+}
diff --git a/cloudstack/provider.go b/cloudstack/provider.go
index 58c7fa5..3b8c441 100644
--- a/cloudstack/provider.go
+++ b/cloudstack/provider.go
@@ -95,6 +95,7 @@ func Provider() *schema.Provider {
                        "cloudstack_physical_network": 
dataSourceCloudStackPhysicalNetwork(),
                        "cloudstack_role":             
dataSourceCloudstackRole(),
                        "cloudstack_cluster":          
dataSourceCloudstackCluster(),
+                       "cloudstack_limits":           
dataSourceCloudStackLimits(),
                },
 
                ResourcesMap: map[string]*schema.Resource{
@@ -147,6 +148,7 @@ func Provider() *schema.Provider {
                        "cloudstack_domain":                         
resourceCloudStackDomain(),
                        "cloudstack_network_service_provider":       
resourceCloudStackNetworkServiceProvider(),
                        "cloudstack_role":                           
resourceCloudStackRole(),
+                       "cloudstack_limits":                         
resourceCloudStackLimits(),
                },
 
                ConfigureFunc: providerConfigure,
diff --git a/cloudstack/resource_cloudstack_account.go 
b/cloudstack/resource_cloudstack_account.go
index cc5d9c0..db6c1a2 100644
--- a/cloudstack/resource_cloudstack_account.go
+++ b/cloudstack/resource_cloudstack_account.go
@@ -66,7 +66,7 @@ func resourceCloudStackAccount() *schema.Resource {
                                Type:     schema.TypeString,
                                Optional: true,
                        },
-                       "domainid": {
+                       "domain_id": {
                                Type:     schema.TypeString,
                                Optional: true,
                        },
@@ -84,7 +84,7 @@ func resourceCloudStackAccountCreate(d *schema.ResourceData, 
meta interface{}) e
        role_id := d.Get("role_id").(string)
        account_type := d.Get("account_type").(int)
        account := d.Get("account").(string)
-       domainid := d.Get("domainid").(string)
+       domain_id := d.Get("domain_id").(string)
 
        // Create a new parameter struct
        p := cs.Account.NewCreateAccountParams(email, first_name, last_name, 
password, username)
@@ -95,7 +95,7 @@ func resourceCloudStackAccountCreate(d *schema.ResourceData, 
meta interface{}) e
        } else {
                p.SetAccount(username)
        }
-       p.SetDomainid(domainid)
+       p.SetDomainid(domain_id)
 
        log.Printf("[DEBUG] Creating Account %s", account)
        a, err := cs.Account.CreateAccount(p)
diff --git a/cloudstack/resource_cloudstack_limits.go 
b/cloudstack/resource_cloudstack_limits.go
new file mode 100644
index 0000000..be4a689
--- /dev/null
+++ b/cloudstack/resource_cloudstack_limits.go
@@ -0,0 +1,471 @@
+// 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 (
+       "context"
+       "fmt"
+       "log"
+       "strconv"
+       "strings"
+
+       "github.com/apache/cloudstack-go/v2/cloudstack"
+       "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+       "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+)
+
+// resourceTypeMap maps string resource types to their integer values
+var resourceTypeMap = map[string]int{
+       "instance":         0,
+       "ip":               1,
+       "volume":           2,
+       "snapshot":         3,
+       "template":         4,
+       "project":          5,
+       "network":          6,
+       "vpc":              7,
+       "cpu":              8,
+       "memory":           9,
+       "primarystorage":   10,
+       "secondarystorage": 11,
+}
+
+func resourceCloudStackLimits() *schema.Resource {
+       return &schema.Resource{
+               Read:   resourceCloudStackLimitsRead,
+               Update: resourceCloudStackLimitsUpdate,
+               Create: resourceCloudStackLimitsCreate,
+               Delete: resourceCloudStackLimitsDelete,
+               Schema: map[string]*schema.Schema{
+                       "type": {
+                               Type:     schema.TypeString,
+                               Required: true,
+                               ForceNew: true,
+                               ValidateFunc: validation.StringInSlice([]string{
+                                       "instance", "ip", "volume", "snapshot", 
"template", "project", "network", "vpc",
+                                       "cpu", "memory", "primarystorage", 
"secondarystorage",
+                               }, false), // false disables case-insensitive 
matching
+                               Description: "The type of resource to update 
the limits. Available types are: " +
+                                       "instance, ip, volume, snapshot, 
template, project, network, vpc, cpu, memory, " +
+                                       "primarystorage, secondarystorage",
+                       },
+                       "account": {
+                               Type:        schema.TypeString,
+                               Optional:    true,
+                               ForceNew:    true,
+                               Description: "Update resource for a specified 
account. Must be used with the domain_id parameter.",
+                       },
+                       "domain_id": {
+                               Type:        schema.TypeString,
+                               Optional:    true,
+                               ForceNew:    true,
+                               Description: "Update resource limits for all 
accounts in specified domain. If used with the account parameter, updates 
resource limits for a specified account in specified domain.",
+                       },
+                       "max": {
+                               Type:        schema.TypeInt,
+                               Optional:    true,
+                               Description: "Maximum resource limit. Use -1 
for unlimited resource limit. A value of 0 means zero resources are allowed, 
though the CloudStack API may return -1 for a limit set to 0.",
+                       },
+                       "configured_max": {
+                               Type:        schema.TypeInt,
+                               Computed:    true,
+                               Description: "Internal field to track the 
originally configured max value to distinguish between 0 and -1 when CloudStack 
returns -1.",
+                       },
+                       "project": {
+                               Type:        schema.TypeString,
+                               Optional:    true,
+                               ForceNew:    true,
+                               Description: "Update resource limits for 
project.",
+                       },
+               },
+               Importer: &schema.ResourceImporter{
+                       StateContext: resourceCloudStackLimitsImport,
+               },
+       }
+}
+
+// resourceCloudStackLimitsImport parses composite import IDs and sets 
resource fields accordingly.
+func resourceCloudStackLimitsImport(ctx context.Context, d 
*schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
+       // Expected formats:
+       // - type-account-accountname-domain_id (for account-specific limits)
+       // - type-project-projectid (for project-specific limits)
+       // - type-domain-domain_id (for domain-specific limits)
+
+       log.Printf("[DEBUG] Importing resource with ID: %s", d.Id())
+
+       // First, extract the resource type which is always the first part
+       idParts := strings.SplitN(d.Id(), "-", 2)
+       if len(idParts) < 2 {
+               return nil, fmt.Errorf("unexpected import ID format (%q), 
expected type-account-accountname-domain_id, type-domain-domain_id, or 
type-project-projectid", d.Id())
+       }
+
+       // Parse the resource type
+       typeInt, err := strconv.Atoi(idParts[0])
+       if err != nil {
+               return nil, fmt.Errorf("invalid type value in import ID: %s", 
idParts[0])
+       }
+
+       // Find the string representation for this numeric type
+       var typeStr string
+       for k, v := range resourceTypeMap {
+               if v == typeInt {
+                       typeStr = k
+                       break
+               }
+       }
+       if typeStr == "" {
+               return nil, fmt.Errorf("unknown type value in import ID: %d", 
typeInt)
+       }
+       if err := d.Set("type", typeStr); err != nil {
+               return nil, err
+       }
+
+       // Get the original resource ID from the state
+       originalID := d.Id()
+       log.Printf("[DEBUG] Original import ID: %s", originalID)
+
+       // Instead of trying to parse the complex ID, let's create a new 
resource
+       // and read it from the API to get the correct values
+       cs := meta.(*cloudstack.CloudStackClient)
+
+       // Create a new parameter struct for listing resource limits
+       p := cs.Limit.NewListResourceLimitsParams()
+       p.SetResourcetype(typeInt)
+
+       // Try to determine the resource scope from the ID format
+       remainingID := idParts[1]
+
+       // Extract the resource scope from the ID
+       if strings.HasPrefix(remainingID, "domain-") {
+               // It's a domain-specific limit
+               log.Printf("[DEBUG] Detected domain-specific limit")
+               // We'll use the Read function to get the domain ID from the 
state
+               // after setting a temporary ID
+               d.SetId(originalID)
+               return []*schema.ResourceData{d}, nil
+       } else if strings.HasPrefix(remainingID, "project-") {
+               // It's a project-specific limit
+               log.Printf("[DEBUG] Detected project-specific limit")
+               // We'll use the Read function to get the project ID from the 
state
+               // after setting a temporary ID
+               d.SetId(originalID)
+               return []*schema.ResourceData{d}, nil
+       } else if strings.HasPrefix(remainingID, "account-") {
+               // It's an account-specific limit
+               log.Printf("[DEBUG] Detected account-specific limit")
+               // We'll use the Read function to get the account and domain ID 
from the state
+               // after setting a temporary ID
+               d.SetId(originalID)
+               return []*schema.ResourceData{d}, nil
+       } else {
+               // For backward compatibility, assume it's a global limit
+               log.Printf("[DEBUG] Detected global limit")
+               d.SetId(originalID)
+               return []*schema.ResourceData{d}, nil
+       }
+}
+
+// getResourceType gets the resource type from the type field
+func getResourceType(d *schema.ResourceData) (int, error) {
+       // Check if type is set
+       if v, ok := d.GetOk("type"); ok {
+               typeStr := v.(string)
+               if resourcetype, ok := resourceTypeMap[typeStr]; ok {
+                       return resourcetype, nil
+               }
+               return 0, fmt.Errorf("invalid type value: %s", typeStr)
+       }
+
+       return 0, fmt.Errorf("type must be specified")
+}
+
+func resourceCloudStackLimitsCreate(d *schema.ResourceData, meta interface{}) 
error {
+       cs := meta.(*cloudstack.CloudStackClient)
+
+       resourcetype, err := getResourceType(d)
+       if err != nil {
+               return err
+       }
+
+       account := d.Get("account").(string)
+       domain_id := d.Get("domain_id").(string)
+       projectid := d.Get("project").(string)
+
+       // Validate account and domain parameters
+       if account != "" && domain_id == "" {
+               return fmt.Errorf("domain_id is required when account is 
specified")
+       }
+
+       // Create a new parameter struct
+       p := cs.Limit.NewUpdateResourceLimitParams(resourcetype)
+       if account != "" {
+               p.SetAccount(account)
+       }
+       if domain_id != "" {
+               p.SetDomainid(domain_id)
+       }
+       // Check for max value - need to handle zero values explicitly
+       maxVal := d.Get("max")
+       if maxVal != nil {
+               maxIntVal := maxVal.(int)
+               log.Printf("[DEBUG] Setting max value to %d", maxIntVal)
+               p.SetMax(int64(maxIntVal))
+
+               // Store the original configured value for later reference
+               // This helps the Read function distinguish between 0 and -1 
when CloudStack returns -1
+               if err := d.Set("configured_max", maxIntVal); err != nil {
+                       return fmt.Errorf("error storing configured max value: 
%w", err)
+               }
+       } else {
+               log.Printf("[DEBUG] No max value found in configuration during 
Create")
+       }
+       if projectid != "" {
+               p.SetProjectid(projectid)
+       }
+
+       log.Printf("[DEBUG] Updating Resource Limit for type %d", resourcetype)
+       _, err = cs.Limit.UpdateResourceLimit(p)
+
+       if err != nil {
+               return fmt.Errorf("Error creating resource limit: %s", err)
+       }
+
+       // Generate a unique ID based on the parameters
+       id := generateResourceID(resourcetype, account, domain_id, projectid)
+       d.SetId(id)
+
+       return resourceCloudStackLimitsRead(d, meta)
+}
+
+// generateResourceID creates a unique ID for the resource based on its 
parameters
+func generateResourceID(resourcetype int, account, domain_id, projectid 
string) string {
+       if projectid != "" {
+               return fmt.Sprintf("%d-project-%s", resourcetype, projectid)
+       }
+
+       if account != "" && domain_id != "" {
+               return fmt.Sprintf("%d-account-%s-%s", resourcetype, account, 
domain_id)
+       }
+
+       if domain_id != "" {
+               return fmt.Sprintf("%d-domain-%s", resourcetype, domain_id)
+       }
+
+       return fmt.Sprintf("%d", resourcetype)
+}
+
+func resourceCloudStackLimitsRead(d *schema.ResourceData, meta interface{}) 
error {
+       cs := meta.(*cloudstack.CloudStackClient)
+
+       // Get the resourcetype from the type field
+       resourcetype, err := getResourceType(d)
+       if err != nil {
+               // If there's an error getting the type, try to extract it from 
the ID
+               idParts := strings.Split(d.Id(), "-")
+               if len(idParts) > 0 {
+                       if rt, err := strconv.Atoi(idParts[0]); err == nil {
+                               resourcetype = rt
+                               // Find the string representation for this 
numeric type
+                               for typeStr, typeVal := range resourceTypeMap {
+                                       if typeVal == rt {
+                                               if err := d.Set("type", 
typeStr); err != nil {
+                                                       return 
fmt.Errorf("error setting type: %s", err)
+                                               }
+                                               break
+                                       }
+                               }
+
+                               // Handle different ID formats
+                               if len(idParts) >= 3 {
+                                       if idParts[1] == "domain" {
+                                               // Format: 
resourcetype-domain-domain_id
+                                               if err := d.Set("domain_id", 
idParts[2]); err != nil {
+                                                       return 
fmt.Errorf("error setting domain_id: %s", err)
+                                               }
+                                       } else if idParts[1] == "project" {
+                                               // Format: 
resourcetype-project-projectid
+                                               if err := d.Set("project", 
idParts[2]); err != nil {
+                                                       return 
fmt.Errorf("error setting project: %s", err)
+                                               }
+                                       } else if idParts[1] == "account" && 
len(idParts) >= 4 {
+                                               // Format: 
resourcetype-account-account-domain_id
+                                               if err := d.Set("account", 
idParts[2]); err != nil {
+                                                       return 
fmt.Errorf("error setting account: %s", err)
+                                               }
+                                               if err := d.Set("domain_id", 
idParts[3]); err != nil {
+                                                       return 
fmt.Errorf("error setting domain_id: %s", err)
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       account := d.Get("account").(string)
+       domain_id := d.Get("domain_id").(string)
+       projectid := d.Get("project").(string)
+
+       // Create a new parameter struct
+       p := cs.Limit.NewListResourceLimitsParams()
+       p.SetResourcetype(resourcetype)
+       if account != "" {
+               p.SetAccount(account)
+       }
+       if domain_id != "" {
+               p.SetDomainid(domain_id)
+       }
+       if projectid != "" {
+               p.SetProjectid(projectid)
+       }
+
+       // Retrieve the resource limits
+       l, err := cs.Limit.ListResourceLimits(p)
+       if err != nil {
+               return fmt.Errorf("error retrieving resource limits: %s", err)
+       }
+
+       if l.Count == 0 {
+               log.Printf("[DEBUG] Resource limit not found")
+               d.SetId("")
+               return nil
+       }
+
+       // Get the first (and should be only) limit from the results
+       limit := l.ResourceLimits[0]
+
+       // Handle the max value - CloudStack may return -1 for both unlimited 
and zero limits
+       // We need to preserve the original value from the configuration when 
possible
+       log.Printf("[DEBUG] CloudStack returned max value: %d", limit.Max)
+       if limit.Max == -1 {
+               // CloudStack returns -1 for both unlimited and zero limits
+               // Check if we have the originally configured value stored
+               if configuredMax, hasConfiguredMax := 
d.GetOk("configured_max"); hasConfiguredMax {
+                       configuredValue := configuredMax.(int)
+                       log.Printf("[DEBUG] Found configured max value: %d, 
using it", configuredValue)
+                       // Use the originally configured value (0 for zero 
limit, -1 for unlimited)
+                       if err := d.Set("max", configuredValue); err != nil {
+                               return fmt.Errorf("error setting max to 
configured value %d: %w", configuredValue, err)
+                       }
+               } else {
+                       log.Printf("[DEBUG] No configured max value found, 
treating -1 as unlimited")
+                       // If no configured value is stored, treat -1 as 
unlimited
+                       if err := d.Set("max", -1); err != nil {
+                               return fmt.Errorf("error setting max to 
unlimited (-1): %w", err)
+                       }
+               }
+       } else {
+               log.Printf("[DEBUG] Using positive max value from API: %d", 
limit.Max)
+               // For any positive value, use it directly from the API
+               if err := d.Set("max", int(limit.Max)); err != nil {
+                       return fmt.Errorf("error setting max: %w", err)
+               }
+       }
+
+       // Preserve original type configuration if it exists
+       if typeValue, ok := d.GetOk("type"); ok {
+               if err := d.Set("type", typeValue.(string)); err != nil {
+                       return fmt.Errorf("error setting type: %w", err)
+               }
+       }
+
+       return nil
+}
+
+func resourceCloudStackLimitsUpdate(d *schema.ResourceData, meta interface{}) 
error {
+       cs := meta.(*cloudstack.CloudStackClient)
+
+       resourcetype, err := getResourceType(d)
+       if err != nil {
+               return err
+       }
+
+       account := d.Get("account").(string)
+       domain_id := d.Get("domain_id").(string)
+       projectid := d.Get("project").(string)
+
+       // Create a new parameter struct
+       p := cs.Limit.NewUpdateResourceLimitParams(resourcetype)
+       if account != "" {
+               p.SetAccount(account)
+       }
+       if domain_id != "" {
+               p.SetDomainid(domain_id)
+       }
+       if maxVal, ok := d.GetOk("max"); ok {
+               maxIntVal := maxVal.(int)
+               log.Printf("[DEBUG] Setting max value to %d", maxIntVal)
+               p.SetMax(int64(maxIntVal))
+
+               // Store the original configured value for later reference
+               // This helps the Read function distinguish between 0 and -1 
when CloudStack returns -1
+               log.Printf("[DEBUG] Storing configured max value in update: 
%d", maxIntVal)
+               if err := d.Set("configured_max", maxIntVal); err != nil {
+                       return fmt.Errorf("error storing configured max value: 
%w", err)
+               }
+       }
+       if projectid != "" {
+               p.SetProjectid(projectid)
+       }
+
+       log.Printf("[DEBUG] Updating Resource Limit for type %d", resourcetype)
+       _, err = cs.Limit.UpdateResourceLimit(p)
+
+       if err != nil {
+               return fmt.Errorf("Error updating resource limit: %s", err)
+       }
+
+       return resourceCloudStackLimitsRead(d, meta)
+}
+
+func resourceCloudStackLimitsDelete(d *schema.ResourceData, meta interface{}) 
error {
+       cs := meta.(*cloudstack.CloudStackClient)
+
+       resourcetype, err := getResourceType(d)
+       if err != nil {
+               return err
+       }
+
+       account := d.Get("account").(string)
+       domain_id := d.Get("domain_id").(string)
+       projectid := d.Get("project").(string)
+
+       // Create a new parameter struct
+       p := cs.Limit.NewUpdateResourceLimitParams(resourcetype)
+       if account != "" {
+               p.SetAccount(account)
+       }
+       if domain_id != "" {
+               p.SetDomainid(domain_id)
+       }
+       if projectid != "" {
+               p.SetProjectid(projectid)
+       }
+       p.SetMax(-1) // Set to -1 to remove the limit
+
+       log.Printf("[DEBUG] Removing Resource Limit for type %d", resourcetype)
+       _, err = cs.Limit.UpdateResourceLimit(p)
+
+       if err != nil {
+               return fmt.Errorf("Error removing Resource Limit: %s", err)
+       }
+
+       d.SetId("")
+
+       return nil
+}
diff --git a/cloudstack/resource_cloudstack_limits_test.go 
b/cloudstack/resource_cloudstack_limits_test.go
new file mode 100644
index 0000000..c847776
--- /dev/null
+++ b/cloudstack/resource_cloudstack_limits_test.go
@@ -0,0 +1,579 @@
+// 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-testing/helper/resource"
+       "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+func TestAccCloudStackLimits_basic(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_basic,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.foo", 
"type", "instance"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.foo", "max", 
"10"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimits_update(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_basic,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.foo", "max", 
"10"),
+                               ),
+                       },
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_update,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.foo", "max", 
"20"),
+                               ),
+                       },
+               },
+       })
+}
+
+func testAccCheckCloudStackLimitsExists(n 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 Limits ID is set")
+               }
+
+               return nil
+       }
+}
+
+func testAccCheckCloudStackLimitsDestroy(s *terraform.State) error {
+       return nil
+}
+
+const testAccCloudStackLimits_basic = `
+resource "cloudstack_limits" "foo" {
+  type         = "instance"
+  max          = 10
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_update = `
+resource "cloudstack_limits" "foo" {
+  type         = "instance"
+  max          = 20
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+func TestAccCloudStackLimits_domain(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_domain_limit,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.domain_limit"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.domain_limit", "type", "volume"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.domain_limit", "max", "50"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimits_account(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_account,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.account_limit"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.account_limit", "type", "snapshot"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.account_limit", "max", "100"),
+                               ),
+                       },
+               },
+       })
+}
+
+// func TestAccCloudStackLimits_project(t *testing.T) { #TODO: Need project 
imported before this will do anything
+//     resource.Test(t, resource.TestCase{
+//             PreCheck:     func() { testAccPreCheck(t) },
+//             Providers:    testAccProviders,
+//             CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+//             Steps: []resource.TestStep{
+//                     {
+//                             Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_project,
+//                             Check: resource.ComposeTestCheckFunc(
+//                                     
testAccCheckCloudStackLimitsExists("cloudstack_limits.project_limit"),
+//                                     resource.TestCheckResourceAttr(
+//                                             
"cloudstack_limits.project_limit", "type", "primarystorage"),
+//                                     resource.TestCheckResourceAttr(
+//                                             
"cloudstack_limits.project_limit", "max", "1000"),
+//                             ),
+//                     },
+//             },
+//     })
+// }
+
+func TestAccCloudStackLimits_unlimited(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_unlimited,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.unlimited"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.unlimited", 
"type", "cpu"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.unlimited", 
"max", "-1"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimits_stringType(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_stringType,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.string_type"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.string_type", "type", "network"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.string_type", "max", "30"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimits_ip(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_ip,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.ip_limit"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.ip_limit", 
"type", "ip"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.ip_limit", 
"max", "25"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimits_template(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_template,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.template_limit"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.template_limit", "type", "template"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.template_limit", "max", "40"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimits_projectType(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_projectType,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.project_type_limit"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.project_type_limit", "type", "project"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.project_type_limit", "max", "15"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimits_vpc(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_vpc,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.vpc_limit"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.vpc_limit", 
"type", "vpc"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.vpc_limit", 
"max", "10"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimits_memory(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_memory,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.memory_limit"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.memory_limit", "type", "memory"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.memory_limit", "max", "8192"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimits_zero(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_zero,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.zero_limit"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.zero_limit", 
"type", "instance"),
+                                       resource.TestCheckResourceAttr(
+                                               "cloudstack_limits.zero_limit", 
"max", "0"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimits_secondarystorage(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_secondarystorage,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.secondarystorage_limit"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.secondarystorage_limit", "type", "secondarystorage"),
+                                       resource.TestCheckResourceAttr(
+                                               
"cloudstack_limits.secondarystorage_limit", "max", "2000"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackLimits_import(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_basic,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"),
+                               ),
+                       },
+                       {
+                               ResourceName:            
"cloudstack_limits.foo",
+                               ImportState:             true,
+                               ImportStateVerify:       true,
+                               ImportStateVerifyIgnore: []string{"domain_id", 
"type", "max", "configured_max"},
+                       },
+               },
+       })
+}
+
+// Test importing domain-specific limits
+func TestAccCloudStackLimits_importDomain(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_domain_limit,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.domain_limit"),
+                               ),
+                       },
+                       {
+                               ResourceName:            
"cloudstack_limits.domain_limit",
+                               ImportState:             true,
+                               ImportStateVerify:       true,
+                               ImportStateVerifyIgnore: []string{"domain_id", 
"type", "max", "configured_max"},
+                       },
+               },
+       })
+}
+
+// Test importing account-specific limits
+// Note: We're not verifying the state here because the account import is 
complex
+// and we just want to make sure the import succeeds
+func TestAccCloudStackLimits_importAccount(t *testing.T) {
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackLimitsDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackLimits_domain + 
testAccCloudStackLimits_account,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackLimitsExists("cloudstack_limits.account_limit"),
+                               ),
+                       },
+                       {
+                               ResourceName:      
"cloudstack_limits.account_limit",
+                               ImportState:       true,
+                               ImportStateVerify: false, // Don't verify the 
state
+                       },
+               },
+       })
+}
+
+// Test configurations for different resource types
+const testAccCloudStackLimits_domain = `
+resource "cloudstack_domain" "test_domain" {
+  name = "test-domain-limits"
+}
+`
+
+const testAccCloudStackLimits_domain_limit = `
+resource "cloudstack_limits" "domain_limit" {
+  type         = "volume"
+  max          = 50
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_account = `
+resource "cloudstack_account" "test_account" {
+  username     = "test-account-limits"
+  password     = "password"
+  first_name   = "Test"
+  last_name    = "Account"
+  email        = "[email protected]"
+  account_type = 2  # Regular user account type
+  role_id      = "4"  # Regular user role
+  domain_id    = cloudstack_domain.test_domain.id
+}
+
+resource "cloudstack_limits" "account_limit" {
+  type         = "snapshot"
+  max          = 100
+  account      = cloudstack_account.test_account.username
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+// const testAccCloudStackLimits_project = ` #TODO: Need project imported 
before this will do anything
+// resource "cloudstack_project" "test_project" {
+//   name         = "test-project-limits"
+//   display_text = "Test Project for Limits"
+//   domain_id    = cloudstack_domain.test_domain.id
+// }
+//
+// resource "cloudstack_limits" "project_limit" {
+//   type         = "primarystorage"
+//   max          = 1000
+//   domain_id    = cloudstack_domain.test_domain.id
+//   projectid    = cloudstack_project.test_project.id
+// }
+// `
+
+const testAccCloudStackLimits_unlimited = `
+resource "cloudstack_limits" "unlimited" {
+  type         = "cpu"
+  max          = -1
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_stringType = `
+resource "cloudstack_limits" "string_type" {
+  type         = "network"
+  max          = 30
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_ip = `
+resource "cloudstack_limits" "ip_limit" {
+  type         = "ip"
+  max          = 25
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_template = `
+resource "cloudstack_limits" "template_limit" {
+  type         = "template"
+  max          = 40
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_projectType = `
+resource "cloudstack_limits" "project_type_limit" {
+  type         = "project"
+  max          = 15
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_vpc = `
+resource "cloudstack_limits" "vpc_limit" {
+  type         = "vpc"
+  max          = 10
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_memory = `
+resource "cloudstack_limits" "memory_limit" {
+  type         = "memory"
+  max          = 8192
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_zero = `
+resource "cloudstack_limits" "zero_limit" {
+  type         = "instance"
+  max          = 0
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_secondarystorage = `
+resource "cloudstack_limits" "secondarystorage_limit" {
+  type         = "secondarystorage"
+  max          = 2000
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_zeroToPositive = `
+resource "cloudstack_limits" "zero_limit" {
+  type         = "instance"
+  max          = 5
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_positiveValue = `
+resource "cloudstack_limits" "positive_limit" {
+  type         = "instance"
+  max          = 15
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_positiveToZero = `
+resource "cloudstack_limits" "positive_limit" {
+  type         = "instance"
+  max          = 0
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_positiveToUnlimited = `
+resource "cloudstack_limits" "positive_limit" {
+  type         = "instance"
+  max          = -1
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
+
+const testAccCloudStackLimits_unlimitedToZero = `
+resource "cloudstack_limits" "unlimited" {
+  type         = "cpu"
+  max          = 0
+  domain_id    = cloudstack_domain.test_domain.id
+}
+`
diff --git a/website/docs/d/limits.html.markdown 
b/website/docs/d/limits.html.markdown
new file mode 100644
index 0000000..7349e8f
--- /dev/null
+++ b/website/docs/d/limits.html.markdown
@@ -0,0 +1,68 @@
+---
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_limits"
+sidebar_current: "docs-cloudstack-datasource-limits"
+description: |-
+  Gets information about CloudStack resource limits.
+---
+
+# cloudstack_limits
+
+Use this data source to retrieve information about CloudStack resource limits 
for accounts, domains, and projects.
+
+## Example Usage
+
+```hcl
+# Get all resource limits for a specific domain
+data "cloudstack_limits" "domain_limits" {
+  domain_id = "domain-uuid"
+}
+
+# Get instance limits for a specific account
+data "cloudstack_limits" "account_instance_limits" {
+  type         = "instance"
+  account      = "acct1"
+  domain_id     = "domain-uuid"
+}
+
+# Get primary storage limits for a project
+data "cloudstack_limits" "project_storage_limits" {
+  type         = "primarystorage"
+  project      = "project-uuid"
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `type` - (Optional) The type of resource to list the limits. Available types 
are:
+  * `instance`
+  * `ip`
+  * `volume`
+  * `snapshot`
+  * `template`
+  * `project`
+  * `network`
+  * `vpc`
+  * `cpu`
+  * `memory`
+  * `primarystorage`
+  * `secondarystorage`
+* `account` - (Optional) List resources by account. Must be used with the 
`domain_id` parameter.
+* `domain_id` - (Optional) List only resources belonging to the domain 
specified.
+* `project` - (Optional) List resource limits by project.
+
+## Attributes Reference
+
+The following attributes are exported:
+
+* `limits` - A list of resource limits. Each limit has the following 
attributes:
+  * `resourcetype` - The type of resource.
+  * `resourcetypename` - The name of the resource type.
+  * `max` - The maximum number of the resource. A value of `-1` indicates 
unlimited resources. A value of `0` means zero resources are allowed, though 
the CloudStack API may return `-1` for a limit set to `0`.
+  * `account` - The account of the resource limit.
+  * `domain` - The domain name of the resource limit.
+  * `domain_id` - The domain ID of the resource limit.
+  * `project` - The project name of the resource limit.
+  * `projectid` - The project ID of the resource limit.
diff --git a/website/docs/r/limits.html.markdown 
b/website/docs/r/limits.html.markdown
new file mode 100644
index 0000000..4c46d67
--- /dev/null
+++ b/website/docs/r/limits.html.markdown
@@ -0,0 +1,88 @@
+---
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_limits"
+sidebar_current: "docs-cloudstack-limits"
+description: |-
+  Provides a CloudStack limits resource.
+---
+
+# cloudstack_limits
+
+Provides a CloudStack limits resource. This can be used to manage resource 
limits for accounts, domains, and projects within CloudStack.
+
+## Example Usage
+
+```hcl
+# Set instance limit for the root domain
+resource "cloudstack_limits" "instance_limit" {
+  type         = "instance"
+  max          = 20
+}
+
+# Set volume limit for a specific account in a domain
+resource "cloudstack_limits" "volume_limit" {
+  type         = "volume"
+  max          = 50
+  account      = "acct1"
+  domain_id     = "domain-uuid"
+}
+
+# Set primary storage limit for a project
+resource "cloudstack_limits" "storage_limit" {
+  type         = "primarystorage"
+  max          = 1000  # GB
+  project      = "project-uuid"
+}
+
+# Set unlimited CPU limit
+resource "cloudstack_limits" "cpu_unlimited" {
+  type         = "cpu"
+  max          = -1  # Unlimited
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `type` - (Required, ForceNew) The type of resource to update. Available 
types are:
+  * `instance`
+  * `ip`
+  * `volume`
+  * `snapshot`
+  * `template`
+  * `project`
+  * `network`
+  * `vpc`
+  * `cpu`
+  * `memory`
+  * `primarystorage`
+  * `secondarystorage`
+
+* `account` - (Optional, ForceNew) Update resource for a specified account. 
Must be used with the `domain_id` parameter.
+* `domain_id` - (Optional, ForceNew) Update resource limits for all accounts 
in specified domain. If used with the `account` parameter, updates resource 
limits for a specified account in specified domain.
+* `max` - (Optional) Maximum resource limit. Use `-1` for unlimited resource 
limit. A value of `0` means zero resources are allowed, though the CloudStack 
API may return `-1` for a limit set to `0`.
+* `project` - (Optional, ForceNew) Update resource limits for project.
+
+## Attributes Reference
+
+The following attributes are exported:
+
+* `id` - The ID of the resource.
+* `type` - The type of resource.
+* `max` - The maximum number of the resource.
+* `account` - The account of the resource limit.
+* `domain_id` - The domain ID of the resource limit.
+* `project` - The project ID of the resource limit.
+
+## Import
+
+Resource limits can be imported using the resource type (numeric), account, 
domain ID, and project ID, e.g.
+
+```bash
+terraform import cloudstack_limits.instance_limit 0
+terraform import cloudstack_limits.volume_limit 2-acct1-domain-uuid
+terraform import cloudstack_limits.storage_limit 10-project-uuid
+```
+
+When importing, the numeric resource type is used in the import ID. The 
provider will automatically convert the numeric type to the corresponding 
string type after import.

Reply via email to