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 5105978  Add support for snapshot policies (#223)
5105978 is described below

commit 5105978d386edd76a89a53e74c291ba664490953
Author: Pearl Dsilva <pearl1...@gmail.com>
AuthorDate: Mon Sep 22 13:28:45 2025 -0400

    Add support for snapshot policies (#223)
    
    * Add support for snapshot policies
    
    * fix test
    
    * update doc
    
    * fix test
    
    * fix test
    
    * update test
    
    * update test to use cardinals as opposed to frequency of interval type
    
    * add forceNew if the policy is modified to recreate the resource + fix test
    
    * update test
    
    * update test
    
    * test
    
    * handle tags
---
 cloudstack/provider.go                             |   1 +
 cloudstack/resource_cloudstack_snapshot_policy.go  | 246 +++++++++++
 .../resource_cloudstack_snapshot_policy_test.go    | 458 +++++++++++++++++++++
 website/docs/r/snapshot_policy.html.markdown       | 148 +++++++
 4 files changed, 853 insertions(+)

diff --git a/cloudstack/provider.go b/cloudstack/provider.go
index 3b8c441..91c0f5c 100644
--- a/cloudstack/provider.go
+++ b/cloudstack/provider.go
@@ -149,6 +149,7 @@ func Provider() *schema.Provider {
                        "cloudstack_network_service_provider":       
resourceCloudStackNetworkServiceProvider(),
                        "cloudstack_role":                           
resourceCloudStackRole(),
                        "cloudstack_limits":                         
resourceCloudStackLimits(),
+                       "cloudstack_snapshot_policy":                
resourceCloudStackSnapshotPolicy(),
                },
 
                ConfigureFunc: providerConfigure,
diff --git a/cloudstack/resource_cloudstack_snapshot_policy.go 
b/cloudstack/resource_cloudstack_snapshot_policy.go
new file mode 100644
index 0000000..8340b14
--- /dev/null
+++ b/cloudstack/resource_cloudstack_snapshot_policy.go
@@ -0,0 +1,246 @@
+//
+// 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"
+       "log"
+
+       "github.com/apache/cloudstack-go/v2/cloudstack"
+       "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func intervalTypeToString(intervalType int) string {
+       switch intervalType {
+       case 0:
+               return "HOURLY"
+       case 1:
+               return "DAILY"
+       case 2:
+               return "WEEKLY"
+       case 3:
+               return "MONTHLY"
+       default:
+               return fmt.Sprintf("%d", intervalType)
+       }
+}
+
+func resourceCloudStackSnapshotPolicy() *schema.Resource {
+       return &schema.Resource{
+               Create: resourceCloudstackSnapshotPolicyCreate,
+               Read:   resourceCloudstackSnapshotPolicyRead,
+               Update: resourceCloudstackSnapshotPolicyUpdate,
+               Delete: resourceCloudstackSnapshotPolicyDelete,
+
+               Schema: map[string]*schema.Schema{
+                       "volume_id": {
+                               Type:     schema.TypeString,
+                               Required: true,
+                               ForceNew: true,
+                       },
+                       "interval_type": {
+                               Type:     schema.TypeString,
+                               Required: true,
+                               ForceNew: true,
+                       },
+                       "max_snaps": {
+                               Type:     schema.TypeInt,
+                               Required: true,
+                               ForceNew: true,
+                       },
+                       "schedule": {
+                               Type:     schema.TypeString,
+                               Required: true,
+                               ForceNew: true,
+                       },
+                       "timezone": {
+                               Type:     schema.TypeString,
+                               Required: true,
+                               ForceNew: true,
+                       },
+                       "zone_ids": {
+                               Type:     schema.TypeList,
+                               Optional: true,
+                               ForceNew: true,
+                               Elem: &schema.Schema{
+                                       Type: schema.TypeString,
+                               },
+                       },
+                       "custom_id": {
+                               Type:     schema.TypeString,
+                               Optional: true,
+                               ForceNew: true,
+                       },
+                       "tags": tagsSchema(),
+               },
+       }
+}
+
+func resourceCloudstackSnapshotPolicyCreate(d *schema.ResourceData, meta 
interface{}) error {
+       cs := meta.(*cloudstack.CloudStackClient)
+
+       p := cs.Snapshot.NewCreateSnapshotPolicyParams(
+               d.Get("interval_type").(string),
+               d.Get("max_snaps").(int),
+               d.Get("schedule").(string),
+               d.Get("timezone").(string),
+               d.Get("volume_id").(string),
+       )
+
+       if v, ok := d.GetOk("zone_ids"); ok && v != nil {
+               zoneIDs := []string{}
+               for _, id := range v.([]interface{}) {
+                       zoneIDs = append(zoneIDs, id.(string))
+               }
+               p.SetZoneids(zoneIDs)
+       }
+
+       snapshotPolicy, err := cs.Snapshot.CreateSnapshotPolicy(p)
+       if err != nil {
+               return fmt.Errorf("Error creating snapshot policy: %s", err)
+       }
+
+       log.Printf("[DEBUG] CreateSnapshotPolicy response: %+v", snapshotPolicy)
+
+       if snapshotPolicy.Id == "" {
+               log.Printf("[DEBUG] CloudStack returned empty ID, trying to 
find created policy by volume ID")
+
+               listParams := cs.Snapshot.NewListSnapshotPoliciesParams()
+               listParams.SetVolumeid(d.Get("volume_id").(string))
+
+               resp, listErr := cs.Snapshot.ListSnapshotPolicies(listParams)
+               if listErr != nil {
+                       return fmt.Errorf("Error listing snapshot policies to 
find created policy: %s", listErr)
+               }
+
+               if resp.Count == 0 {
+                       return fmt.Errorf("No snapshot policies found for 
volume after creation")
+               }
+
+               foundPolicy := resp.SnapshotPolicies[resp.Count-1]
+               log.Printf("[DEBUG] Found policy with ID: %s", foundPolicy.Id)
+               d.SetId(foundPolicy.Id)
+       } else {
+               d.SetId(snapshotPolicy.Id)
+       }
+
+       log.Printf("[DEBUG] Snapshot policy created with ID: %s", d.Id())
+
+       // Set tags if provided
+       if err := setTags(cs, d, "SnapshotPolicy"); err != nil {
+               return fmt.Errorf("Error setting tags on snapshot policy %s: 
%s", d.Id(), err)
+       }
+
+       return resourceCloudstackSnapshotPolicyRead(d, meta)
+}
+
+func resourceCloudstackSnapshotPolicyRead(d *schema.ResourceData, meta 
interface{}) error {
+       cs := meta.(*cloudstack.CloudStackClient)
+
+       if d.Id() == "" {
+               log.Printf("[DEBUG] Snapshot policy ID is empty")
+               return fmt.Errorf("Snapshot policy ID is empty")
+       }
+
+       p := cs.Snapshot.NewListSnapshotPoliciesParams()
+       p.SetId(d.Id())
+
+       resp, err := cs.Snapshot.ListSnapshotPolicies(p)
+       if err != nil {
+               return fmt.Errorf("Failed to list snapshot policies: %s", err)
+       }
+
+       if resp.Count == 0 {
+               log.Printf("[DEBUG] Snapshot policy %s not found, removing from 
state", d.Id())
+               d.SetId("")
+               return nil
+       }
+
+       snapshotPolicy := resp.SnapshotPolicies[0]
+
+       d.Set("volume_id", snapshotPolicy.Volumeid)
+       d.Set("interval_type", 
intervalTypeToString(snapshotPolicy.Intervaltype))
+       d.Set("max_snaps", snapshotPolicy.Maxsnaps)
+       d.Set("schedule", snapshotPolicy.Schedule)
+       d.Set("timezone", snapshotPolicy.Timezone)
+
+       if snapshotPolicy.Zone != nil {
+               zoneIDs := []string{}
+               for _, zone := range snapshotPolicy.Zone {
+                       if zoneMap, ok := zone.(map[string]interface{}); ok {
+                               if id, ok := zoneMap["id"].(string); ok {
+                                       zoneIDs = append(zoneIDs, id)
+                               }
+                       }
+               }
+               d.Set("zone_ids", zoneIDs)
+       } else {
+               d.Set("zone_ids", nil)
+       }
+
+       // Handle tags
+       tags := make(map[string]interface{})
+       for _, tag := range snapshotPolicy.Tags {
+               tags[tag.Key] = tag.Value
+       }
+       d.Set("tags", tags)
+
+       return nil
+}
+
+func resourceCloudstackSnapshotPolicyUpdate(d *schema.ResourceData, meta 
interface{}) error {
+       cs := meta.(*cloudstack.CloudStackClient)
+
+       p := cs.Snapshot.NewUpdateSnapshotPolicyParams()
+       p.SetId(d.Id())
+       if v, ok := d.GetOk("custom_id"); ok {
+               p.SetCustomid(v.(string))
+       }
+
+       _, err := cs.Snapshot.UpdateSnapshotPolicy(p)
+       if err != nil {
+               return err
+       }
+
+       // Handle tags
+       if d.HasChange("tags") {
+               if err := updateTags(cs, d, "SnapshotPolicy"); err != nil {
+                       return fmt.Errorf("Error updating tags on snapshot 
policy %s: %s", d.Id(), err)
+               }
+       }
+
+       return resourceCloudstackSnapshotPolicyRead(d, meta)
+}
+
+func resourceCloudstackSnapshotPolicyDelete(d *schema.ResourceData, meta 
interface{}) error {
+       cs := meta.(*cloudstack.CloudStackClient)
+
+       p := cs.Snapshot.NewDeleteSnapshotPoliciesParams()
+       p.SetId(d.Id())
+
+       _, err := cs.Snapshot.DeleteSnapshotPolicies(p)
+       if err != nil {
+               return fmt.Errorf("Failed to delete snapshot policy %s: %s", 
d.Id(), err)
+       }
+
+       d.SetId("")
+
+       return nil
+}
diff --git a/cloudstack/resource_cloudstack_snapshot_policy_test.go 
b/cloudstack/resource_cloudstack_snapshot_policy_test.go
new file mode 100644
index 0000000..acb2801
--- /dev/null
+++ b/cloudstack/resource_cloudstack_snapshot_policy_test.go
@@ -0,0 +1,458 @@
+//
+// 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 TestAccCloudStackSnapshotPolicy_basic(t *testing.T) {
+       var snapshotPolicy cloudstack.SnapshotPolicy
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackSnapshotPolicyDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackSnapshotPolicy_basic,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackSnapshotPolicyExists("cloudstack_snapshot_policy.foo", 
&snapshotPolicy),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", 
"interval_type", "DAILY"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", "max_snaps", 
"7"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", "schedule", 
"02:30"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", "timezone", 
"UTC"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", "tags.%", "2"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", 
"tags.Environment", "test"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", 
"tags.Purpose", "backup"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackSnapshotPolicy_hourly(t *testing.T) {
+       var snapshotPolicy cloudstack.SnapshotPolicy
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackSnapshotPolicyDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackSnapshotPolicy_hourly,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackSnapshotPolicyExists("cloudstack_snapshot_policy.hourly", 
&snapshotPolicy),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.hourly", 
"interval_type", "HOURLY"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.hourly", 
"max_snaps", "6"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.hourly", "schedule", 
"0"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.hourly", "timezone", 
"UTC"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.hourly", 
"custom_id", "test-hourly"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.hourly", "tags.%", 
"0"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackSnapshotPolicy_update(t *testing.T) {
+       var snapshotPolicy cloudstack.SnapshotPolicy
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackSnapshotPolicyDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackSnapshotPolicy_basic,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackSnapshotPolicyExists("cloudstack_snapshot_policy.foo", 
&snapshotPolicy),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", "max_snaps", 
"7"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", 
"tags.Environment", "test"),
+                               ),
+                       },
+                       {
+                               Config: testAccCloudStackSnapshotPolicy_update,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackSnapshotPolicyExists("cloudstack_snapshot_policy.foo", 
&snapshotPolicy),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", "max_snaps", 
"8"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", 
"tags.Environment", "production"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.foo", 
"tags.Updated", "true"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackSnapshotPolicy_weekly(t *testing.T) {
+       var snapshotPolicy cloudstack.SnapshotPolicy
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackSnapshotPolicyDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackSnapshotPolicy_weekly,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackSnapshotPolicyExists("cloudstack_snapshot_policy.weekly", 
&snapshotPolicy),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.weekly", 
"interval_type", "WEEKLY"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.weekly", "schedule", 
"1:03:00"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.weekly", "tags.%", 
"0"),
+                               ),
+                       },
+               },
+       })
+}
+
+func TestAccCloudStackSnapshotPolicy_monthly(t *testing.T) {
+       var snapshotPolicy cloudstack.SnapshotPolicy
+
+       resource.Test(t, resource.TestCase{
+               PreCheck:     func() { testAccPreCheck(t) },
+               Providers:    testAccProviders,
+               CheckDestroy: testAccCheckCloudStackSnapshotPolicyDestroy,
+               Steps: []resource.TestStep{
+                       {
+                               Config: testAccCloudStackSnapshotPolicy_monthly,
+                               Check: resource.ComposeTestCheckFunc(
+                                       
testAccCheckCloudStackSnapshotPolicyExists("cloudstack_snapshot_policy.monthly",
 &snapshotPolicy),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.monthly", 
"interval_type", "MONTHLY"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.monthly", 
"schedule", "15:01:00"),
+                                       
resource.TestCheckResourceAttr("cloudstack_snapshot_policy.monthly", "tags.%", 
"0"),
+                               ),
+                       },
+               },
+       })
+}
+
+func testAccCheckCloudStackSnapshotPolicyExists(n string, snapshotPolicy 
*cloudstack.SnapshotPolicy) 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 snapshot policy ID is set")
+               }
+
+               cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
+               sp, _, err := cs.Snapshot.GetSnapshotPolicyByID(rs.Primary.ID)
+               if err != nil {
+                       return err
+               }
+
+               if sp.Id != rs.Primary.ID {
+                       return fmt.Errorf("Snapshot policy not found")
+               }
+
+               *snapshotPolicy = *sp
+               return nil
+       }
+}
+
+func testAccCheckCloudStackSnapshotPolicyDestroy(s *terraform.State) error {
+       cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
+
+       for _, rs := range s.RootModule().Resources {
+               if rs.Type != "cloudstack_snapshot_policy" {
+                       continue
+               }
+
+               if rs.Primary.ID == "" {
+                       return fmt.Errorf("No snapshot policy ID is set")
+               }
+
+               _, _, err := cs.Snapshot.GetSnapshotPolicyByID(rs.Primary.ID)
+               if err == nil {
+                       return fmt.Errorf("Snapshot policy %s still exists", 
rs.Primary.ID)
+               }
+       }
+
+       return nil
+}
+
+const testAccCloudStackSnapshotPolicy_basic = `
+data "cloudstack_zone" "zone" {
+  filter {
+    name   = "name"
+    value  = "Sandbox-simulator"
+  }
+}
+
+resource "cloudstack_network" "foo" {
+  name = "terraform-network"
+  display_text = "terraform-network"
+  cidr = "10.1.1.0/24"
+  network_offering = "DefaultIsolatedNetworkOfferingWithSourceNatService"
+  zone = data.cloudstack_zone.zone.name
+}
+
+resource "cloudstack_instance" "foobar" {
+  name = "terraform-test"
+  display_name = "terraform"
+  service_offering = "Small Instance"
+  network_id = cloudstack_network.foo.id
+  template = "CentOS 5.6 (64-bit) no GUI (Simulator)"
+  zone = data.cloudstack_zone.zone.name
+  expunge = true
+}
+
+resource "cloudstack_disk_offering" "foo" {
+  name               = "terraform-disk-offering"
+  display_text       = "terraform-disk-offering"
+  disk_size          = 10
+}
+
+resource "cloudstack_disk" "foo" {
+  name             = "terraform-disk"
+  attach           = true
+  disk_offering    = cloudstack_disk_offering.foo.name
+  virtual_machine_id = cloudstack_instance.foobar.id
+  zone             = data.cloudstack_zone.zone.name
+}
+
+resource "cloudstack_snapshot_policy" "foo" {
+  volume_id     = cloudstack_disk.foo.id
+  interval_type = "DAILY"
+  max_snaps     = 7
+  schedule      = "02:30"
+  timezone      = "UTC"
+  zone_ids      = [data.cloudstack_zone.zone.id]
+
+  tags = {
+    Environment = "test"
+    Purpose     = "backup"
+  }
+}
+`
+
+const testAccCloudStackSnapshotPolicy_update = `
+data "cloudstack_zone" "zone" {
+  filter {
+    name   = "name"
+    value  = "Sandbox-simulator"
+  }
+}
+
+resource "cloudstack_network" "foo" {
+  name = "terraform-network"
+  display_text = "terraform-network"
+  cidr = "10.1.1.0/24"
+  network_offering = "DefaultIsolatedNetworkOfferingWithSourceNatService"
+  zone = data.cloudstack_zone.zone.name
+}
+
+resource "cloudstack_instance" "foobar" {
+  name = "terraform-test"
+  display_name = "terraform"
+  service_offering = "Small Instance"
+  network_id = cloudstack_network.foo.id
+  template = "CentOS 5.6 (64-bit) no GUI (Simulator)"
+  zone = data.cloudstack_zone.zone.name
+  expunge = true
+}
+
+resource "cloudstack_disk_offering" "foo" {
+  name               = "terraform-disk-offering"
+  display_text       = "terraform-disk-offering"
+  disk_size          = 10
+}
+
+resource "cloudstack_disk" "foo" {
+  name             = "terraform-disk"
+  attach           = true
+  disk_offering    = cloudstack_disk_offering.foo.name
+  virtual_machine_id = cloudstack_instance.foobar.id
+  zone             = data.cloudstack_zone.zone.name
+}
+
+resource "cloudstack_snapshot_policy" "foo" {
+  volume_id     = cloudstack_disk.foo.id
+  interval_type = "DAILY"
+  max_snaps     = 8
+  schedule      = "02:30"
+  timezone      = "UTC"
+  zone_ids      = [data.cloudstack_zone.zone.id]
+
+  tags = {
+    Environment = "production"
+    Purpose     = "backup"
+    Updated     = "true"
+  }
+}
+`
+
+const testAccCloudStackSnapshotPolicy_hourly = `
+data "cloudstack_zone" "zone" {
+  filter {
+    name   = "name"
+    value  = "Sandbox-simulator"
+  }
+}
+
+resource "cloudstack_network" "foo" {
+  name = "terraform-network-hourly"
+  display_text = "terraform-network-hourly"
+  cidr = "10.1.1.0/24"
+  network_offering = "DefaultIsolatedNetworkOfferingWithSourceNatService"
+  zone = data.cloudstack_zone.zone.name
+}
+
+resource "cloudstack_instance" "foobar" {
+  name = "terraform-test-hourly"
+  display_name = "terraform-hourly"
+  service_offering = "Small Instance"
+  network_id = cloudstack_network.foo.id
+  template = "CentOS 5.6 (64-bit) no GUI (Simulator)"
+  zone = data.cloudstack_zone.zone.name
+  expunge = true
+}
+
+resource "cloudstack_disk_offering" "foo" {
+  name               = "terraform-disk-offering-hourly"
+  display_text       = "terraform-disk-offering-hourly"
+  disk_size          = 10
+}
+
+resource "cloudstack_disk" "foo" {
+  name             = "terraform-disk-hourly"
+  attach           = true
+  disk_offering    = cloudstack_disk_offering.foo.name
+  virtual_machine_id = cloudstack_instance.foobar.id
+  zone             = data.cloudstack_zone.zone.name
+}
+
+resource "cloudstack_snapshot_policy" "hourly" {
+  volume_id     = cloudstack_disk.foo.id
+  interval_type = "HOURLY"
+  max_snaps     = 6
+  schedule      = "0"
+  timezone      = "UTC"
+  zone_ids      = [data.cloudstack_zone.zone.id]
+  custom_id     = "test-hourly"
+}
+`
+
+const testAccCloudStackSnapshotPolicy_weekly = `
+data "cloudstack_zone" "zone" {
+  filter {
+    name   = "name"
+    value  = "Sandbox-simulator"
+  }
+}
+
+resource "cloudstack_network" "foo" {
+  name = "terraform-network-weekly"
+  display_text = "terraform-network-weekly"
+  cidr = "10.1.1.0/24"
+  network_offering = "DefaultIsolatedNetworkOfferingWithSourceNatService"
+  zone = data.cloudstack_zone.zone.name
+}
+
+resource "cloudstack_instance" "foobar" {
+  name = "terraform-test-weekly"
+  display_name = "terraform-weekly"
+  service_offering = "Small Instance"
+  network_id = cloudstack_network.foo.id
+  template = "CentOS 5.6 (64-bit) no GUI (Simulator)"
+  zone = data.cloudstack_zone.zone.name
+  expunge = true
+}
+
+resource "cloudstack_disk_offering" "foo" {
+  name               = "terraform-disk-offering-weekly"
+  display_text       = "terraform-disk-offering-weekly"
+  disk_size          = 10
+}
+
+resource "cloudstack_disk" "foo" {
+  name             = "terraform-disk-weekly"
+  attach           = true
+  disk_offering    = cloudstack_disk_offering.foo.name
+  virtual_machine_id = cloudstack_instance.foobar.id
+  zone             = data.cloudstack_zone.zone.name
+}
+
+resource "cloudstack_snapshot_policy" "weekly" {
+  volume_id     = cloudstack_disk.foo.id
+  interval_type = "WEEKLY"
+  max_snaps     = 4
+  schedule      = "1:03:00"  # Monday at 3:00 AM
+  timezone      = "UTC"
+  zone_ids      = [data.cloudstack_zone.zone.id]
+}
+`
+
+const testAccCloudStackSnapshotPolicy_monthly = `
+data "cloudstack_zone" "zone" {
+  filter {
+    name   = "name"
+    value  = "Sandbox-simulator"
+  }
+}
+
+resource "cloudstack_network" "foo" {
+  name = "terraform-network-monthly"
+  display_text = "terraform-network-monthly"
+  cidr = "10.1.1.0/24"
+  network_offering = "DefaultIsolatedNetworkOfferingWithSourceNatService"
+  zone = data.cloudstack_zone.zone.name
+}
+
+resource "cloudstack_instance" "foobar" {
+  name = "terraform-test-monthly"
+  display_name = "terraform-monthly"
+  service_offering = "Small Instance"
+  network_id = cloudstack_network.foo.id
+  template = "CentOS 5.6 (64-bit) no GUI (Simulator)"
+  zone = data.cloudstack_zone.zone.name
+  expunge = true
+}
+
+resource "cloudstack_disk_offering" "foo" {
+  name               = "terraform-disk-offering-monthly"
+  display_text       = "terraform-disk-offering-monthly"
+  disk_size          = 10
+}
+
+resource "cloudstack_disk" "foo" {
+  name             = "terraform-disk-monthly"
+  attach           = true
+  disk_offering    = cloudstack_disk_offering.foo.name
+  virtual_machine_id = cloudstack_instance.foobar.id
+  zone             = data.cloudstack_zone.zone.name
+}
+
+resource "cloudstack_snapshot_policy" "monthly" {
+  volume_id     = cloudstack_disk.foo.id
+  interval_type = "MONTHLY"
+  max_snaps     = 8
+  schedule      = "15:01:00"  # 15th day at 1:00 AM
+  timezone      = "UTC"
+  zone_ids      = [data.cloudstack_zone.zone.id]
+}
+`
diff --git a/website/docs/r/snapshot_policy.html.markdown 
b/website/docs/r/snapshot_policy.html.markdown
new file mode 100644
index 0000000..6e7a389
--- /dev/null
+++ b/website/docs/r/snapshot_policy.html.markdown
@@ -0,0 +1,148 @@
+---
+subcategory: "Snapshot"
+layout: "cloudstack"
+page_title: "CloudStack: cloudstack_snapshot_policy"
+sidebar_current: "docs-cloudstack-resource-snapshot-policy"
+description: |-
+  Creates and manages snapshot policies for volumes.
+---
+
+# cloudstack_snapshot_policy
+
+Provides a CloudStack snapshot policy resource. This can be used to create, 
modify, and delete snapshot policies for volumes.
+
+## Example Usage
+
+### Basic Snapshot Policy
+
+```hcl
+resource "cloudstack_snapshot_policy" "daily" {
+  volume_id     = cloudstack_disk.data.id
+  interval_type = "DAILY"
+  max_snaps     = 7
+  schedule      = "02:30"
+  timezone      = "UTC"
+  zone_ids      = [data.cloudstack_zone.zone1.id]
+
+  tags = {
+    Environment = "production"
+    Purpose     = "backup"
+  }
+}
+```
+
+### Hourly Snapshot Policy
+
+```hcl
+resource "cloudstack_snapshot_policy" "hourly" {
+  volume_id     = cloudstack_disk.database.id
+  interval_type = "HOURLY"
+  max_snaps     = 6
+  schedule      = "0"  # Top of every hour
+  timezone      = "America/New_York"
+  zone_ids      = [data.cloudstack_zone.zone1.id]
+  
+  custom_id = "hourly-db-backup"
+}
+```
+
+### Multiple Zone Snapshot Policy
+
+```hcl
+resource "cloudstack_snapshot_policy" "multi_zone" {
+  volume_id     = cloudstack_disk.shared.id
+  interval_type = "WEEKLY"
+  max_snaps     = 4
+  schedule      = "1:03:00"  # Monday at 3:00 AM
+  timezone      = "UTC"
+  zone_ids      = [
+    data.cloudstack_zone.zone1.id,
+    data.cloudstack_zone.zone2.id
+  ]
+}
+```
+
+### Monthly Archive Policy
+
+```hcl
+resource "cloudstack_snapshot_policy" "monthly_archive" {
+  volume_id     = cloudstack_disk.archive.id
+  interval_type = "MONTHLY"
+  max_snaps     = 12
+  schedule      = "1:01:00"  # 1st day of month at 1:00 AM
+  timezone      = "UTC"
+  zone_ids      = [data.cloudstack_zone.zone1.id]
+
+  tags = {
+    Type        = "archive"
+    Retention   = "1-year"
+    Environment = "production"
+  }
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `volume_id` - (Required) The ID of the volume for which the snapshot policy 
is being created.
+
+* `interval_type` - (Required) The interval type for the snapshot policy. 
Valid values are:
+  * `HOURLY` - Take snapshots every hour
+  * `DAILY` - Take snapshots daily
+  * `WEEKLY` - Take snapshots weekly
+  * `MONTHLY` - Take snapshots monthly
+
+* `max_snaps` - (Required) Maximum number of snapshots to retain. When this 
limit is reached, older snapshots are automatically deleted.
+
+* `schedule` - (Required) The schedule for taking snapshots. The format 
depends on the interval type:
+  * **HOURLY**: Minute (0-59), e.g., `"30"` for 30 minutes past every hour
+  * **DAILY**: Time in HH:MM format, e.g., `"02:30"` for 2:30 AM daily
+  * **WEEKLY**: Day and time in D:HH:MM format, e.g., `"1:02:30"` for Monday 
at 2:30 AM (1=Monday, 7=Sunday)
+  * **MONTHLY**: Day and time in DD:HH:MM format, e.g., `"15:02:30"` for 15th 
day at 2:30 AM
+
+* `timezone` - (Required) The timezone for the schedule. Use standard timezone 
names like `UTC`, `America/New_York`, `Europe/London`, etc.
+
+* `zone_ids` - (Optional) List of zone IDs where the snapshot policy should be 
applied. If not specified, the policy applies to all zones.
+
+* `custom_id` - (Optional) A custom ID for the snapshot policy. This is useful 
for identification purposes and cannot be changed after creation.
+
+* `tags` - (Optional) A mapping of tags to assign to the resource.
+
+## Attribute Reference
+
+In addition to all arguments above, the following attributes are exported:
+
+* `id` - The ID of the snapshot policy.
+
+## Schedule Format Examples
+
+### Hourly Schedules
+```hcl
+schedule = "0"   # Top of every hour (XX:00)
+schedule = "15"  # 15 minutes past every hour (XX:15)
+schedule = "30"  # 30 minutes past every hour (XX:30)
+schedule = "45"  # 45 minutes past every hour (XX:45)
+```
+
+### Daily Schedules
+```hcl
+schedule = "01:00"  # 1:00 AM daily
+schedule = "02:30"  # 2:30 AM daily
+schedule = "14:00"  # 2:00 PM daily
+schedule = "23:59"  # 11:59 PM daily
+```
+
+### Weekly Schedules
+```hcl
+schedule = "1:02:00"  # Monday at 2:00 AM
+schedule = "2:03:30"  # Tuesday at 3:30 AM
+schedule = "7:01:00"  # Sunday at 1:00 AM
+```
+
+### Monthly Schedules
+```hcl
+schedule = "1:02:00"   # 1st day of month at 2:00 AM
+schedule = "15:03:30"  # 15th day of month at 3:30 AM
+schedule = "28:01:00"  # 28th day of month at 1:00 AM
+```

Reply via email to