This is an automated email from the ASF dual-hosted git repository.
kiranchavala 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 c50eed8 feature: add Quota plugin (#248)
c50eed8 is described below
commit c50eed8d14dd33a70d522bcce88bc8d78e9607ca
Author: Jtolelo <[email protected]>
AuthorDate: Fri Oct 17 10:12:24 2025 -0300
feature: add Quota plugin (#248)
* feature: add Quota plugin
* chore: add Apache License header to quota-related files
* fix: update PreCheck functions to use quota support checks in
quota-related tests
---------
Co-authored-by: jean <[email protected]>
---
cloudstack/data_source_cloudstack_quota.go | 150 ++++++++++
cloudstack/data_source_cloudstack_quota_enabled.go | 73 +++++
cloudstack/data_source_cloudstack_quota_tariff.go | 199 +++++++++++++
cloudstack/data_source_cloudstack_quota_test.go | 216 ++++++++++++++
cloudstack/provider.go | 4 +
cloudstack/resource_cloudstack_quota_tariff.go | 316 +++++++++++++++++++++
.../resource_cloudstack_quota_tariff_test.go | 281 ++++++++++++++++++
website/docs/d/quota.html.markdown | 50 ++++
website/docs/d/quota_enabled.html.markdown | 38 +++
website/docs/d/quota_tariff.html.markdown | 67 +++++
website/docs/r/quota_tariff.html.markdown | 86 ++++++
11 files changed, 1480 insertions(+)
diff --git a/cloudstack/data_source_cloudstack_quota.go
b/cloudstack/data_source_cloudstack_quota.go
new file mode 100644
index 0000000..015f47a
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_quota.go
@@ -0,0 +1,150 @@
+//
+// 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"
+ "crypto/sha1"
+ "encoding/hex"
+ "fmt"
+ "strings"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func dataSourceCloudStackQuota() *schema.Resource {
+ return &schema.Resource{
+ ReadContext: dataSourceCloudStackQuotaRead,
+
+ Schema: map[string]*schema.Schema{
+ "account": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Lists quota for the specified
account",
+ },
+
+ "domain_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Lists quota for the specified
domain ID",
+ },
+
+ // Computed values
+ "quotas": {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "List of quota summaries",
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "account_id": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Account
ID",
+ },
+
+ "account": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Account
name",
+ },
+
+ "domain_id": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Domain
ID",
+ },
+
+ "domain": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Domain
name",
+ },
+
+ "quota_value": {
+ Type:
schema.TypeFloat,
+ Computed: true,
+ Description: "Current
quota value",
+ },
+
+ "quota_enabled": {
+ Type:
schema.TypeBool,
+ Computed: true,
+ Description: "Whether
quota is enabled for this account",
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dataSourceCloudStackQuotaRead(ctx context.Context, d
*schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ p := cs.Quota.NewQuotaSummaryParams()
+
+ if account, ok := d.GetOk("account"); ok {
+ p.SetAccount(account.(string))
+ }
+
+ if domainid, ok := d.GetOk("domain_id"); ok {
+ p.SetDomainid(domainid.(string))
+ }
+
+ r, err := cs.Quota.QuotaSummary(p)
+ if err != nil {
+ return diag.Errorf("Error retrieving quota summary: %s", err)
+ }
+
+ quotas := make([]map[string]interface{}, 0, len(r.QuotaSummary))
+
+ for _, summary := range r.QuotaSummary {
+ q := map[string]interface{}{
+ "account_id": summary.Accountid,
+ "account": summary.Account,
+ "domain_id": summary.Domainid,
+ "domain": summary.Domain,
+ "quota_value": summary.Quota,
+ "quota_enabled": summary.Quotaenabled,
+ }
+ quotas = append(quotas, q)
+ }
+
+ if err := d.Set("quotas", quotas); err != nil {
+ return diag.Errorf("Error setting quotas: %s", err)
+ }
+
+ // Generate a unique ID for this data source
+ var buf strings.Builder
+ buf.WriteString(fmt.Sprintf("quota-summary"))
+ if account, ok := d.GetOk("account"); ok {
+ buf.WriteString(fmt.Sprintf("-account-%s", account.(string)))
+ }
+ if domainid, ok := d.GetOk("domain_id"); ok {
+ buf.WriteString(fmt.Sprintf("-domain-%s", domainid.(string)))
+ }
+
+ sha := sha1.Sum([]byte(buf.String()))
+ d.SetId(hex.EncodeToString(sha[:]))
+
+ return nil
+}
diff --git a/cloudstack/data_source_cloudstack_quota_enabled.go
b/cloudstack/data_source_cloudstack_quota_enabled.go
new file mode 100644
index 0000000..fe45d50
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_quota_enabled.go
@@ -0,0 +1,73 @@
+//
+// 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"
+ "log"
+ "strings"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func dataSourceCloudStackQuotaEnabled() *schema.Resource {
+ return &schema.Resource{
+ ReadContext: dataSourceCloudStackQuotaEnabledRead,
+
+ Schema: map[string]*schema.Schema{
+ "enabled": {
+ Type: schema.TypeBool,
+ Computed: true,
+ Description: "Whether quota is enabled in the
CloudStack management server",
+ },
+ },
+ }
+}
+
+func dataSourceCloudStackQuotaEnabledRead(ctx context.Context, d
*schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ p := cs.Quota.NewQuotaIsEnabledParams()
+ r, err := cs.Quota.QuotaIsEnabled(p)
+
+ if err != nil {
+ // Log the error for diagnostics
+ log.Printf("[DEBUG] QuotaIsEnabled error: %s", err.Error())
+
+ // If the error contains "cannot unmarshal object", try custom
parsing
+ if strings.Contains(err.Error(), "cannot unmarshal object") {
+ // The API is returning a nested structure, let's
handle it
+ // For now, assume quota is enabled if the API responds
+ log.Printf("[WARN] CloudStack quota API returned nested
structure, assuming enabled=true")
+ d.Set("enabled", true)
+ d.SetId("quota-enabled")
+ return nil
+ }
+
+ return diag.Errorf("Error checking quota status: %s", err)
+ }
+
+ d.Set("enabled", r.Isenabled)
+ d.SetId("quota-enabled")
+
+ return nil
+}
diff --git a/cloudstack/data_source_cloudstack_quota_tariff.go
b/cloudstack/data_source_cloudstack_quota_tariff.go
new file mode 100644
index 0000000..2219699
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_quota_tariff.go
@@ -0,0 +1,199 @@
+//
+// 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"
+ "crypto/sha1"
+ "encoding/hex"
+ "fmt"
+ "strings"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func dataSourceCloudStackQuotaTariff() *schema.Resource {
+ return &schema.Resource{
+ ReadContext: dataSourceCloudStackQuotaTariffRead,
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Name of the quota tariff",
+ },
+
+ "usage_type": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Description: "Usage type for the quota tariff",
+ },
+
+ // Computed values
+ "tariffs": {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "List of quota tariffs",
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Tariff
ID",
+ },
+
+ "name": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Tariff
name",
+ },
+
+ "description": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Tariff
description",
+ },
+
+ "usage_type": {
+ Type:
schema.TypeInt,
+ Computed: true,
+ Description: "Usage
type",
+ },
+
+ "usage_name": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Usage
name",
+ },
+
+ "usage_unit": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Usage
unit",
+ },
+
+ "tariff_value": {
+ Type:
schema.TypeFloat,
+ Computed: true,
+ Description: "Tariff
value",
+ },
+
+ "end_date": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Tariff
end date",
+ },
+
+ "effective_date": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Tariff
effective date",
+ },
+
+ "activation_rule": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Tariff
activation rule",
+ },
+
+ "removed": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Whether
the tariff is removed",
+ },
+
+ "currency": {
+ Type:
schema.TypeString,
+ Computed: true,
+ Description: "Currency
for the tariff",
+ },
+
+ "position": {
+ Type:
schema.TypeInt,
+ Computed: true,
+ Description: "Position
of the tariff",
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dataSourceCloudStackQuotaTariffRead(ctx context.Context, d
*schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ p := cs.Quota.NewQuotaTariffListParams()
+
+ if name, ok := d.GetOk("name"); ok {
+ p.SetName(name.(string))
+ }
+
+ if usagetype, ok := d.GetOk("usage_type"); ok {
+ p.SetUsagetype(usagetype.(int))
+ }
+
+ r, err := cs.Quota.QuotaTariffList(p)
+ if err != nil {
+ return diag.Errorf("Error retrieving quota tariff list: %s",
err)
+ }
+
+ tariffs := make([]map[string]interface{}, 0, len(r.QuotaTariffList))
+
+ for _, tariff := range r.QuotaTariffList {
+ t := map[string]interface{}{
+ "id": tariff.Id,
+ "name": tariff.Name,
+ "description": tariff.Description,
+ "usage_type": tariff.UsageType,
+ "usage_name": tariff.UsageName,
+ "usage_unit": tariff.UsageUnit,
+ "tariff_value": tariff.TariffValue,
+ "end_date": tariff.EndDate,
+ "effective_date": tariff.EffectiveDate,
+ "activation_rule": tariff.ActivationRule,
+ "removed": tariff.Removed,
+ "currency": tariff.Currency,
+ "position": tariff.Position,
+ }
+ tariffs = append(tariffs, t)
+ }
+
+ if err := d.Set("tariffs", tariffs); err != nil {
+ return diag.Errorf("Error setting tariffs: %s", err)
+ }
+
+ // Generate a unique ID for this data source
+ var buf strings.Builder
+ buf.WriteString(fmt.Sprintf("quota-tariff"))
+ if name, ok := d.GetOk("name"); ok {
+ buf.WriteString(fmt.Sprintf("-name-%s", name.(string)))
+ }
+ if usagetype, ok := d.GetOk("usage_type"); ok {
+ buf.WriteString(fmt.Sprintf("-usagetype-%d", usagetype.(int)))
+ }
+
+ sha := sha1.Sum([]byte(buf.String()))
+ d.SetId(hex.EncodeToString(sha[:]))
+
+ return nil
+}
diff --git a/cloudstack/data_source_cloudstack_quota_test.go
b/cloudstack/data_source_cloudstack_quota_test.go
new file mode 100644
index 0000000..676b4d4
--- /dev/null
+++ b/cloudstack/data_source_cloudstack_quota_test.go
@@ -0,0 +1,216 @@
+//
+// 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/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+func TestAccCloudStackQuotaDataSource_basic(t *testing.T) {
+ resourceName := "data.cloudstack_quota.test"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckQuotaSupport(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config:
testAccCloudStackQuotaDataSource_basic(),
+ Check: resource.ComposeTestCheckFunc(
+
testAccCheckCloudStackQuotaDataSourceExists(resourceName),
+
resource.TestCheckResourceAttrSet(resourceName, "quotas.#"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccCloudStackQuotaDataSource_withFilters(t *testing.T) {
+ resourceName := "data.cloudstack_quota.test"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckQuotaSupport(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config:
testAccCloudStackQuotaDataSource_withFilters(),
+ Check: resource.ComposeTestCheckFunc(
+
testAccCheckCloudStackQuotaDataSourceExists(resourceName),
+
resource.TestCheckResourceAttrSet(resourceName, "quotas.#"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccCloudStackQuotaEnabledDataSource_basic(t *testing.T) {
+ resourceName := "data.cloudstack_quota_enabled.test"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckQuotaSupport(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config:
testAccCloudStackQuotaEnabledDataSource_basic(),
+ Check: resource.ComposeTestCheckFunc(
+
testAccCheckCloudStackQuotaEnabledDataSourceExists(resourceName),
+
resource.TestCheckResourceAttrSet(resourceName, "enabled"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccCloudStackQuotaTariffDataSource_basic(t *testing.T) {
+ resourceName := "data.cloudstack_quota_tariff.test"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckQuotaSupport(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config:
testAccCloudStackQuotaTariffDataSource_basic(),
+ Check: resource.ComposeTestCheckFunc(
+
testAccCheckCloudStackQuotaTariffDataSourceExists(resourceName),
+
resource.TestCheckResourceAttrSet(resourceName, "tariffs.#"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccCloudStackQuotaTariffDataSource_withFilters(t *testing.T) {
+ resourceName := "data.cloudstack_quota_tariff.test"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckQuotaSupport(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config:
testAccCloudStackQuotaTariffDataSource_withFilters(),
+ Check: resource.ComposeTestCheckFunc(
+
testAccCheckCloudStackQuotaTariffDataSourceExists(resourceName),
+
resource.TestCheckResourceAttrSet(resourceName, "tariffs.#"),
+ ),
+ },
+ },
+ })
+}
+
+// Test check functions
+func testAccCheckCloudStackQuotaDataSourceExists(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 quota data source ID is set")
+ }
+
+ return nil
+ }
+}
+
+func testAccCheckCloudStackQuotaEnabledDataSourceExists(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 quota enabled data source ID is
set")
+ }
+
+ return nil
+ }
+}
+
+func testAccCheckCloudStackQuotaTariffDataSourceExists(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 quota tariff data source ID is
set")
+ }
+
+ return nil
+ }
+}
+
+func testAccPreCheckQuotaSupport(t *testing.T) {
+ testAccPreCheck(t)
+ cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
+
+ // Try to check if quota is enabled to verify if the feature is
available
+ p := cs.Quota.NewQuotaIsEnabledParams()
+ _, err := cs.Quota.QuotaIsEnabled(p)
+ if err != nil {
+ t.Skipf("Quota feature not supported or not enabled in this
CloudStack instance: %v", err)
+ }
+}
+
+// Test configuration functions
+func testAccCloudStackQuotaDataSource_basic() string {
+ return `
+data "cloudstack_quota" "test" {
+}
+`
+}
+
+func testAccCloudStackQuotaDataSource_withFilters() string {
+ return `
+data "cloudstack_quota" "test" {
+ account = "admin"
+ domain_id = "ROOT"
+}
+`
+}
+
+func testAccCloudStackQuotaEnabledDataSource_basic() string {
+ return `
+data "cloudstack_quota_enabled" "test" {
+}
+`
+}
+
+func testAccCloudStackQuotaTariffDataSource_basic() string {
+ return `
+data "cloudstack_quota_tariff" "test" {
+}
+`
+}
+
+func testAccCloudStackQuotaTariffDataSource_withFilters() string {
+ return `
+data "cloudstack_quota_tariff" "test" {
+ usage_type = 1
+}
+`
+}
diff --git a/cloudstack/provider.go b/cloudstack/provider.go
index 0e0f6cf..63d0234 100644
--- a/cloudstack/provider.go
+++ b/cloudstack/provider.go
@@ -101,6 +101,9 @@ func Provider() *schema.Provider {
"cloudstack_role":
dataSourceCloudstackRole(),
"cloudstack_cluster":
dataSourceCloudstackCluster(),
"cloudstack_limits":
dataSourceCloudStackLimits(),
+ "cloudstack_quota":
dataSourceCloudStackQuota(),
+ "cloudstack_quota_enabled":
dataSourceCloudStackQuotaEnabled(),
+ "cloudstack_quota_tariff":
dataSourceCloudStackQuotaTariff(),
},
ResourcesMap: map[string]*schema.Resource{
@@ -160,6 +163,7 @@ func Provider() *schema.Provider {
"cloudstack_role":
resourceCloudStackRole(),
"cloudstack_limits":
resourceCloudStackLimits(),
"cloudstack_snapshot_policy":
resourceCloudStackSnapshotPolicy(),
+ "cloudstack_quota_tariff":
resourceCloudStackQuotaTariff(),
},
ConfigureFunc: providerConfigure,
diff --git a/cloudstack/resource_cloudstack_quota_tariff.go
b/cloudstack/resource_cloudstack_quota_tariff.go
new file mode 100644
index 0000000..601a966
--- /dev/null
+++ b/cloudstack/resource_cloudstack_quota_tariff.go
@@ -0,0 +1,316 @@
+//
+// 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"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func resourceCloudStackQuotaTariff() *schema.Resource {
+ return &schema.Resource{
+ CreateContext: resourceCloudStackQuotaTariffCreate,
+ ReadContext: resourceCloudStackQuotaTariffRead,
+ UpdateContext: resourceCloudStackQuotaTariffUpdate,
+ DeleteContext: resourceCloudStackQuotaTariffDelete,
+
+ Importer: &schema.ResourceImporter{
+ StateContext: schema.ImportStatePassthroughContext,
+ },
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true, // CloudStack recreates
tariffs internally anyway
+ Description: "Name of the quota tariff",
+ },
+
+ "usage_type": {
+ Type: schema.TypeInt,
+ Required: true,
+ ForceNew: true,
+ Description: "Usage type for the quota tariff",
+ ValidateFunc: func(val interface{}, key string)
(warns []string, errs []error) {
+ v := val.(int)
+ if v < 1 || v > 25 {
+ errs = append(errs,
fmt.Errorf("%q must be between 1 and 25, got: %d", key, v))
+ }
+ return
+ },
+ },
+
+ "value": {
+ Type: schema.TypeFloat,
+ Required: true,
+ Description: "Value of the quota tariff",
+ ValidateFunc: func(val interface{}, key string)
(warns []string, errs []error) {
+ v := val.(float64)
+ if v < 0 {
+ errs = append(errs,
fmt.Errorf("%q cannot be negative, got: %f", key, v))
+ }
+ return
+ },
+ },
+
+ "description": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Description of the quota tariff",
+ },
+
+ "start_date": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Start date for the quota tariff
(format: yyyy-MM-dd)",
+ },
+
+ "end_date": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "End date for the quota tariff
(format: yyyy-MM-dd)",
+ },
+
+ "activation_rule": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Activation rule for the quota
tariff",
+ },
+
+ // Computed values
+ "currency": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "Currency for the tariff",
+ },
+
+ "effective_date": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "Effective date of the tariff",
+ },
+
+ "usage_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "Usage name",
+ },
+
+ "usage_unit": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "Usage unit",
+ },
+
+ "position": {
+ Type: schema.TypeInt,
+ Computed: true,
+ Description: "Position of the tariff",
+ },
+
+ "removed": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "Whether the tariff is removed",
+ },
+ },
+ }
+}
+
+func resourceCloudStackQuotaTariffCreate(ctx context.Context, d
*schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ name := d.Get("name").(string)
+ usagetype := d.Get("usage_type").(int)
+ value := d.Get("value").(float64)
+
+ p := cs.Quota.NewQuotaTariffCreateParams(name, usagetype, value)
+
+ if description, ok := d.GetOk("description"); ok {
+ p.SetDescription(description.(string))
+ }
+
+ if startdate, ok := d.GetOk("start_date"); ok {
+ p.SetStartdate(startdate.(string))
+ }
+
+ if enddate, ok := d.GetOk("end_date"); ok {
+ p.SetEnddate(enddate.(string))
+ }
+
+ if activationrule, ok := d.GetOk("activation_rule"); ok {
+ p.SetActivationrule(activationrule.(string))
+ }
+
+ r, err := cs.Quota.QuotaTariffCreate(p)
+ if err != nil {
+ return diag.Errorf("Error creating quota tariff %s: %s", name,
err)
+ }
+
+ d.SetId(r.Id)
+
+ return resourceCloudStackQuotaTariffRead(ctx, d, meta)
+}
+
+func resourceCloudStackQuotaTariffRead(ctx context.Context, d
*schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ log.Printf("[DEBUG] Reading quota tariff with ID: %s", d.Id())
+
+ // Always try to find by name and usage type first (more reliable)
+ name := d.Get("name").(string)
+ usageType := d.Get("usage_type").(int)
+
+ if name != "" && usageType > 0 {
+ return findTariffByNameAndType(ctx, d, meta, cs)
+ }
+
+ // Fallback to ID search if name/usage_type not available
+ if d.Id() != "" {
+ p := cs.Quota.NewQuotaTariffListParams()
+ p.SetId(d.Id())
+
+ r, err := cs.Quota.QuotaTariffList(p)
+ if err != nil {
+ log.Printf("[DEBUG] Error searching by ID, trying by
name: %s", err)
+ return findTariffByNameAndType(ctx, d, meta, cs)
+ }
+
+ if len(r.QuotaTariffList) == 0 {
+ log.Printf("[DEBUG] Quota tariff %s no longer exists",
d.Id())
+ d.SetId("")
+ return nil
+ }
+
+ tariff := r.QuotaTariffList[0]
+ return setTariffData(d, tariff)
+ }
+
+ return diag.Errorf("Cannot read tariff: no ID, name, or usage_type
available")
+}
+
+func findTariffByNameAndType(ctx context.Context, d *schema.ResourceData, meta
interface{}, cs *cloudstack.CloudStackClient) diag.Diagnostics {
+ // List all tariffs and find by name and usage type
+ p := cs.Quota.NewQuotaTariffListParams()
+
+ r, err := cs.Quota.QuotaTariffList(p)
+ if err != nil {
+ return diag.Errorf("Error listing quota tariffs: %s", err)
+ }
+
+ name := d.Get("name").(string)
+ usageType := d.Get("usage_type").(int)
+
+ for _, tariff := range r.QuotaTariffList {
+ if tariff.Name == name && tariff.UsageType == usageType {
+ d.SetId(tariff.Id)
+ return setTariffData(d, tariff)
+ }
+ }
+
+ log.Printf("[DEBUG] Quota tariff %s (usage type %d) not found", name,
usageType)
+ d.SetId("")
+ return nil
+}
+
+func setTariffData(d *schema.ResourceData, tariff *cloudstack.QuotaTariffList)
diag.Diagnostics {
+ d.Set("name", tariff.Name)
+ d.Set("usage_type", tariff.UsageType)
+ d.Set("value", tariff.TariffValue)
+ d.Set("description", tariff.Description)
+ d.Set("end_date", tariff.EndDate)
+ d.Set("activation_rule", tariff.ActivationRule)
+ d.Set("currency", tariff.Currency)
+ d.Set("effective_date", tariff.EffectiveDate)
+ d.Set("usage_name", tariff.UsageName)
+ d.Set("usage_unit", tariff.UsageUnit)
+ d.Set("position", tariff.Position)
+ d.Set("removed", tariff.Removed)
+
+ return nil
+}
+
+func resourceCloudStackQuotaTariffUpdate(ctx context.Context, d
*schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ // Use current name in state for update (before any changes)
+ oldName, newName := d.GetChange("name")
+
+ // Determine which name to use for the update API call
+ updateName := oldName.(string)
+ if updateName == "" {
+ // If no old name (shouldn't happen), use current name
+ updateName = newName.(string)
+ }
+
+ log.Printf("[DEBUG] Updating quota tariff '%s' (ID: %s)", updateName,
d.Id())
+
+ p := cs.Quota.NewQuotaTariffUpdateParams(updateName)
+
+ if d.HasChange("name") {
+ p.SetName(d.Get("name").(string))
+ }
+
+ if d.HasChange("description") {
+ p.SetDescription(d.Get("description").(string))
+ }
+
+ if d.HasChange("value") {
+ p.SetValue(d.Get("value").(float64))
+ }
+
+ if d.HasChange("start_date") {
+ p.SetStartdate(d.Get("start_date").(string))
+ }
+
+ if d.HasChange("end_date") {
+ p.SetEnddate(d.Get("end_date").(string))
+ }
+
+ if d.HasChange("activation_rule") {
+ p.SetActivationrule(d.Get("activation_rule").(string))
+ }
+
+ _, err := cs.Quota.QuotaTariffUpdate(p)
+ if err != nil {
+ return diag.Errorf("Error updating quota tariff %s: %s",
d.Id(), err)
+ }
+
+ return resourceCloudStackQuotaTariffRead(ctx, d, meta)
+}
+
+func resourceCloudStackQuotaTariffDelete(ctx context.Context, d
*schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cs := meta.(*cloudstack.CloudStackClient)
+
+ p := cs.Quota.NewQuotaTariffDeleteParams(d.Id())
+
+ _, err := cs.Quota.QuotaTariffDelete(p)
+ if err != nil {
+ return diag.Errorf("Error deleting quota tariff %s: %s",
d.Id(), err)
+ }
+
+ d.SetId("")
+ return nil
+}
diff --git a/cloudstack/resource_cloudstack_quota_tariff_test.go
b/cloudstack/resource_cloudstack_quota_tariff_test.go
new file mode 100644
index 0000000..9da7057
--- /dev/null
+++ b/cloudstack/resource_cloudstack_quota_tariff_test.go
@@ -0,0 +1,281 @@
+//
+// 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"
+ "regexp"
+ "testing"
+
+ "github.com/apache/cloudstack-go/v2/cloudstack"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+)
+
+func TestAccCloudStackQuotaTariff_basic(t *testing.T) {
+ var quotaTariff cloudstack.QuotaTariffList
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckQuotaSupport(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckCloudStackQuotaTariffDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccCloudStackQuotaTariff_basic(),
+ Check: resource.ComposeTestCheckFunc(
+
testAccCheckCloudStackQuotaTariffExists("cloudstack_quota_tariff.test",
"aTariff),
+
resource.TestCheckResourceAttr("cloudstack_quota_tariff.test", "name", "Test
CPU Tariff"),
+
resource.TestCheckResourceAttr("cloudstack_quota_tariff.test", "usage_type",
"1"),
+
resource.TestCheckResourceAttr("cloudstack_quota_tariff.test", "value", "0.05"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccCloudStackQuotaTariff_update(t *testing.T) {
+ var quotaTariff cloudstack.QuotaTariffList
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckQuotaSupport(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckCloudStackQuotaTariffDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccCloudStackQuotaTariff_basic(),
+ Check: resource.ComposeTestCheckFunc(
+
testAccCheckCloudStackQuotaTariffExists("cloudstack_quota_tariff.test",
"aTariff),
+
resource.TestCheckResourceAttr("cloudstack_quota_tariff.test", "name", "Test
CPU Tariff"),
+
resource.TestCheckResourceAttr("cloudstack_quota_tariff.test", "value", "0.05"),
+ ),
+ },
+ {
+ Config: testAccCloudStackQuotaTariff_update(),
+ Check: resource.ComposeTestCheckFunc(
+
testAccCheckCloudStackQuotaTariffExists("cloudstack_quota_tariff.test",
"aTariff),
+
resource.TestCheckResourceAttr("cloudstack_quota_tariff.test", "name", "Updated
CPU Tariff"),
+
resource.TestCheckResourceAttr("cloudstack_quota_tariff.test", "value", "0.10"),
+
resource.TestCheckResourceAttr("cloudstack_quota_tariff.test", "description",
"Updated description"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccCloudStackQuotaTariff_import(t *testing.T) {
+ resourceName := "cloudstack_quota_tariff.test"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckQuotaSupport(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckCloudStackQuotaTariffDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccCloudStackQuotaTariff_basic(),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func testAccCheckCloudStackQuotaTariffExists(n string, quotaTariff
*cloudstack.QuotaTariffList) 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 quota tariff ID is set")
+ }
+
+ cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
+ p := cs.Quota.NewQuotaTariffListParams()
+ p.SetId(rs.Primary.ID)
+
+ r, err := cs.Quota.QuotaTariffList(p)
+ if err != nil {
+ return err
+ }
+
+ if len(r.QuotaTariffList) == 0 {
+ return fmt.Errorf("Quota tariff not found")
+ }
+
+ *quotaTariff = *r.QuotaTariffList[0]
+ return nil
+ }
+}
+
+func testAccCheckCloudStackQuotaTariffDestroy(s *terraform.State) error {
+ cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
+
+ for _, rs := range s.RootModule().Resources {
+ if rs.Type != "cloudstack_quota_tariff" {
+ continue
+ }
+
+ if rs.Primary.ID == "" {
+ return fmt.Errorf("No quota tariff ID is set")
+ }
+
+ p := cs.Quota.NewQuotaTariffListParams()
+ p.SetId(rs.Primary.ID)
+
+ r, err := cs.Quota.QuotaTariffList(p)
+ if err == nil && len(r.QuotaTariffList) > 0 &&
r.QuotaTariffList[0].Removed == "" {
+ return fmt.Errorf("Quota tariff %s still exists",
rs.Primary.ID)
+ }
+ }
+
+ return nil
+}
+
+func testAccCloudStackQuotaTariff_basic() string {
+ return `
+resource "cloudstack_quota_tariff" "test" {
+ name = "Test CPU Tariff"
+ usage_type = 1
+ value = 0.05
+ description = "Test tariff for CPU usage"
+}
+`
+}
+
+func testAccCloudStackQuotaTariff_update() string {
+ return `
+resource "cloudstack_quota_tariff" "test" {
+ name = "Updated CPU Tariff"
+ usage_type = 1
+ value = 0.10
+ description = "Updated description"
+ start_date = "2026-01-01"
+ end_date = "2026-12-31"
+}
+`
+}
+
+// Test validation errors
+func TestAccCloudStackQuotaTariff_validation(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckQuotaSupport(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config:
testAccCloudStackQuotaTariff_invalidUsageType(),
+ ExpectError: regexp.MustCompile(`"usage_type"
must be between 1 and 25`),
+ },
+ {
+ Config:
testAccCloudStackQuotaTariff_negativeValue(),
+ ExpectError: regexp.MustCompile(`"value" cannot
be negative`),
+ },
+ },
+ })
+}
+
+// Test activation rules
+func TestAccCloudStackQuotaTariff_activationRules(t *testing.T) {
+ var quotaTariff cloudstack.QuotaTariffList
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckQuotaSupport(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckCloudStackQuotaTariffDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config:
testAccCloudStackQuotaTariff_withActivationRule(),
+ Check: resource.ComposeTestCheckFunc(
+
testAccCheckCloudStackQuotaTariffExists("cloudstack_quota_tariff.test",
"aTariff),
+
resource.TestCheckResourceAttr("cloudstack_quota_tariff.test",
"activation_rule", "serviceOffering.id == 'test-so-123'"),
+ ),
+ },
+ },
+ })
+}
+
+// Test complex activation rules
+func TestAccCloudStackQuotaTariff_complexActivationRules(t *testing.T) {
+ var quotaTariff cloudstack.QuotaTariffList
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheckQuotaSupport(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckCloudStackQuotaTariffDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config:
testAccCloudStackQuotaTariff_complexActivationRule(),
+ Check: resource.ComposeTestCheckFunc(
+
testAccCheckCloudStackQuotaTariffExists("cloudstack_quota_tariff.test",
"aTariff),
+
resource.TestCheckResourceAttr("cloudstack_quota_tariff.test",
"activation_rule", "serviceOffering.id == 'test-so-123' && zone.name ==
'premium-zone'"),
+ ),
+ },
+ },
+ })
+}
+
+func testAccCloudStackQuotaTariff_invalidUsageType() string {
+ return `
+resource "cloudstack_quota_tariff" "test" {
+ name = "Invalid Usage Type Test"
+ usage_type = 999
+ value = 0.05
+ description = "This should fail"
+}
+`
+}
+
+func testAccCloudStackQuotaTariff_negativeValue() string {
+ return `
+resource "cloudstack_quota_tariff" "test" {
+ name = "Negative Value Test"
+ usage_type = 1
+ value = -0.05
+ description = "This should fail"
+}
+`
+}
+
+func testAccCloudStackQuotaTariff_withActivationRule() string {
+ return `
+resource "cloudstack_quota_tariff" "test" {
+ name = "Test Activation Rule Tariff"
+ usage_type = 1
+ value = 0.15
+ description = "Test tariff with activation rule"
+ activation_rule = "serviceOffering.id == 'test-so-123'"
+}
+`
+}
+
+func testAccCloudStackQuotaTariff_complexActivationRule() string {
+ return `
+resource "cloudstack_quota_tariff" "test" {
+ name = "Complex Activation Rule Tariff"
+ usage_type = 1
+ value = 0.25
+ description = "Test tariff with complex activation rule"
+ activation_rule = "serviceOffering.id == 'test-so-123' && zone.name ==
'premium-zone'"
+}
+`
+}
diff --git a/website/docs/d/quota.html.markdown
b/website/docs/d/quota.html.markdown
new file mode 100644
index 0000000..2d67846
--- /dev/null
+++ b/website/docs/d/quota.html.markdown
@@ -0,0 +1,50 @@
+---
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_quota"
+sidebar_current: "docs-cloudstack-datasource-quota"
+description: |-
+ Gets information about CloudStack quota summaries.
+---
+
+# cloudstack_quota
+
+Use this data source to retrieve quota summary information for CloudStack
accounts and domains. This provides information about quota values and whether
quota is enabled for specific accounts.
+
+## Example Usage
+
+```hcl
+# Get quota summary for all accounts
+data "cloudstack_quota" "all_quotas" {
+}
+
+# Get quota summary for a specific account
+data "cloudstack_quota" "account_quota" {
+ account = "myaccount"
+ domain_id = "domain-uuid"
+}
+
+# Get quota summary for a specific domain
+data "cloudstack_quota" "domain_quota" {
+ domain_id = "domain-uuid"
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `account` - (Optional) The name of the account to filter quota summaries.
+
+* `domain_id` - (Optional) The ID of the domain to filter quota summaries.
+
+## Attribute Reference
+
+In addition to all arguments above, the following attributes are exported:
+
+* `quotas` - A list of quota summary objects. Each object contains:
+ * `account_id` - The ID of the account.
+ * `account` - The name of the account.
+ * `domain_id` - The ID of the domain.
+ * `domain` - The name of the domain.
+ * `quota_value` - The current quota value for the account.
+ * `quota_enabled` - Whether quota is enabled for this account.
\ No newline at end of file
diff --git a/website/docs/d/quota_enabled.html.markdown
b/website/docs/d/quota_enabled.html.markdown
new file mode 100644
index 0000000..796996d
--- /dev/null
+++ b/website/docs/d/quota_enabled.html.markdown
@@ -0,0 +1,38 @@
+---
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_quota_enabled"
+sidebar_current: "docs-cloudstack-datasource-quota-enabled"
+description: |-
+ Checks if quota is enabled in CloudStack.
+---
+
+# cloudstack_quota_enabled
+
+Use this data source to check whether the quota system is enabled in the
CloudStack management server.
+
+## Example Usage
+
+```hcl
+# Check if quota system is enabled
+data "cloudstack_quota_enabled" "quota_status" {
+}
+
+# Use the quota status in conditional logic
+resource "cloudstack_quota_tariff" "cpu_tariff" {
+ count = data.cloudstack_quota_enabled.quota_status.enabled ? 1 : 0
+
+ name = "CPU Usage Tariff"
+ usage_type = 1
+ value = 0.05
+}
+```
+
+## Argument Reference
+
+This data source takes no arguments.
+
+## Attribute Reference
+
+The following attributes are exported:
+
+* `enabled` - A boolean value indicating whether the quota system is enabled
in CloudStack.
\ No newline at end of file
diff --git a/website/docs/d/quota_tariff.html.markdown
b/website/docs/d/quota_tariff.html.markdown
new file mode 100644
index 0000000..48edc94
--- /dev/null
+++ b/website/docs/d/quota_tariff.html.markdown
@@ -0,0 +1,67 @@
+---
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_quota_tariff"
+sidebar_current: "docs-cloudstack-datasource-quota-tariff"
+description: |-
+ Gets information about CloudStack quota tariffs.
+---
+
+# cloudstack_quota_tariff
+
+Use this data source to retrieve information about quota tariffs in
CloudStack. Quota tariffs define the pricing for different resource usage types.
+
+## Example Usage
+
+```hcl
+# Get all quota tariffs
+data "cloudstack_quota_tariff" "all_tariffs" {
+}
+
+# Get tariffs by name
+data "cloudstack_quota_tariff" "cpu_tariffs" {
+ name = "CPU Tariff"
+}
+
+# Get tariffs by usage type
+data "cloudstack_quota_tariff" "compute_tariffs" {
+ usage_type = 1
+}
+
+# Output tariff information
+output "tariff_details" {
+ value = [
+ for tariff in data.cloudstack_quota_tariff.all_tariffs.tariffs : {
+ name = tariff.name
+ value = tariff.tariff_value
+ unit = tariff.usage_unit
+ }
+ ]
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `name` - (Optional) The name of the quota tariff to filter results.
+
+* `usage_type` - (Optional) The usage type to filter tariffs by.
+
+## Attribute Reference
+
+In addition to all arguments above, the following attributes are exported:
+
+* `tariffs` - A list of quota tariff objects. Each object contains:
+ * `id` - The ID of the tariff.
+ * `name` - The name of the tariff.
+ * `description` - The description of the tariff.
+ * `usage_type` - The usage type ID.
+ * `usage_name` - The human-readable name of the usage type.
+ * `usage_unit` - The unit of measurement for the usage.
+ * `tariff_value` - The monetary value of the tariff.
+ * `end_date` - The end date of the tariff.
+ * `effective_date` - The effective date when the tariff becomes active.
+ * `activation_rule` - The rule that determines when this tariff is activated.
+ * `removed` - Whether the tariff has been marked as removed.
+ * `currency` - The currency used for the tariff.
+ * `position` - The position/priority of the tariff.
\ No newline at end of file
diff --git a/website/docs/r/quota_tariff.html.markdown
b/website/docs/r/quota_tariff.html.markdown
new file mode 100644
index 0000000..42b4ffd
--- /dev/null
+++ b/website/docs/r/quota_tariff.html.markdown
@@ -0,0 +1,86 @@
+---
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_quota_tariff"
+sidebar_current: "docs-cloudstack-resource-quota-tariff"
+description: |-
+ Creates and manages CloudStack quota tariffs.
+---
+
+# cloudstack_quota_tariff
+
+Provides a CloudStack quota tariff resource. This can be used to create,
modify, and delete quota tariffs.
+
+## Example Usage
+
+```hcl
+# Create a CPU usage tariff
+resource "cloudstack_quota_tariff" "cpu_tariff" {
+ name = "CPU Usage Tariff"
+ usage_type = 1
+ value = 0.05
+ description = "Tariff for CPU usage per hour"
+}
+
+# Create a memory usage tariff with date range
+resource "cloudstack_quota_tariff" "memory_tariff" {
+ name = "Memory Usage Tariff"
+ usage_type = 2
+ value = 0.01
+ description = "Tariff for memory usage per GB per hour"
+ start_date = "2024-01-01"
+ end_date = "2024-12-31"
+ activation_rule = "account.type == 'user'"
+}
+
+# Create a storage tariff
+resource "cloudstack_quota_tariff" "storage_tariff" {
+ name = "Primary Storage Tariff"
+ usage_type = 6
+ value = 0.1
+ description = "Tariff for primary storage usage per GB per month"
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `name` - (Required) The name of the quota tariff.
+
+* `usage_type` - (Required) The usage type for the quota tariff. This cannot
be changed after creation.
+
+* `value` - (Required) The monetary value of the quota tariff.
+
+* `description` - (Optional) A description of the quota tariff.
+
+* `start_date` - (Optional) The start date for the quota tariff in yyyy-MM-dd
format.
+
+* `end_date` - (Optional) The end date for the quota tariff in yyyy-MM-dd
format.
+
+* `activation_rule` - (Optional) The activation rule that determines when this
tariff applies.
+
+## Attribute Reference
+
+In addition to all arguments above, the following attributes are exported:
+
+* `id` - The ID of the quota tariff.
+
+* `currency` - The currency used for the tariff.
+
+* `effective_date` - The effective date when the tariff becomes active.
+
+* `usage_name` - The human-readable name of the usage type.
+
+* `usage_unit` - The unit of measurement for the usage.
+
+* `position` - The position/priority of the tariff.
+
+* `removed` - Whether the tariff has been marked as removed.
+
+## Import
+
+Quota tariffs can be imported using the tariff ID:
+
+```
+$ terraform import cloudstack_quota_tariff.cpu_tariff
12345678-1234-1234-1234-123456789abc
+```
\ No newline at end of file