diff --git a/builtin/providers/azurerm/config.go b/builtin/providers/azurerm/config.go index e25cd96fe470..be504c223cd5 100644 --- a/builtin/providers/azurerm/config.go +++ b/builtin/providers/azurerm/config.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/Godeps/_workspace/src/github.com/Azure/go-autorest/autorest" "github.com/Azure/azure-sdk-for-go/Godeps/_workspace/src/github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/azure-sdk-for-go/arm/cdn" "github.com/Azure/azure-sdk-for-go/arm/compute" "github.com/Azure/azure-sdk-for-go/arm/network" "github.com/Azure/azure-sdk-for-go/arm/resources/resources" @@ -40,6 +41,8 @@ type ArmClient struct { routeTablesClient network.RouteTablesClient routesClient network.RoutesClient + cdnProfilesClient cdn.ProfilesClient + providers resources.ProvidersClient resourceGroupClient resources.GroupsClient tagsClient resources.TagsClient @@ -246,5 +249,11 @@ func (c *Config) getArmClient() (*ArmClient, error) { suc.Sender = autorest.CreateSender(withRequestLogging()) client.storageUsageClient = suc + cpc := cdn.NewProfilesClient(c.SubscriptionID) + setUserAgent(&cpc.Client) + cpc.Authorizer = spt + cpc.Sender = autorest.CreateSender(withRequestLogging()) + client.cdnProfilesClient = cpc + return &client, nil } diff --git a/builtin/providers/azurerm/provider.go b/builtin/providers/azurerm/provider.go index 44cecf7733d2..42135a50ee3b 100644 --- a/builtin/providers/azurerm/provider.go +++ b/builtin/providers/azurerm/provider.go @@ -51,6 +51,7 @@ func Provider() terraform.ResourceProvider { "azurerm_network_interface": resourceArmNetworkInterface(), "azurerm_route_table": resourceArmRouteTable(), "azurerm_route": resourceArmRoute(), + "azurerm_cdn_profile": resourceArmCdnProfile(), }, ConfigureFunc: providerConfigure, } @@ -95,7 +96,7 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { func registerAzureResourceProvidersWithSubscription(config *Config, client *ArmClient) error { providerClient := client.providers - providers := []string{"Microsoft.Network", "Microsoft.Compute"} + providers := []string{"Microsoft.Network", "Microsoft.Compute", "Microsoft.Cdn"} for _, v := range providers { res, err := providerClient.Register(v) diff --git a/builtin/providers/azurerm/resource_arm_availability_set_test.go b/builtin/providers/azurerm/resource_arm_availability_set_test.go index fb392795f333..488347b56c9a 100644 --- a/builtin/providers/azurerm/resource_arm_availability_set_test.go +++ b/builtin/providers/azurerm/resource_arm_availability_set_test.go @@ -119,7 +119,7 @@ func testCheckAzureRMAvailabilitySetExists(name string) resource.TestCheckFunc { } func testCheckAzureRMAvailabilitySetDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).vnetClient + conn := testAccProvider.Meta().(*ArmClient).availSetClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_availability_set" { @@ -129,7 +129,7 @@ func testCheckAzureRMAvailabilitySetDestroy(s *terraform.State) error { name := rs.Primary.Attributes["name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - resp, err := conn.Get(resourceGroup, name, "") + resp, err := conn.Get(resourceGroup, name) if err != nil { return nil diff --git a/builtin/providers/azurerm/resource_arm_cdn_profile.go b/builtin/providers/azurerm/resource_arm_cdn_profile.go new file mode 100644 index 000000000000..b5254fc22f99 --- /dev/null +++ b/builtin/providers/azurerm/resource_arm_cdn_profile.go @@ -0,0 +1,186 @@ +package azurerm + +import ( + "fmt" + "log" + "net/http" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/arm/cdn" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceArmCdnProfile() *schema.Resource { + return &schema.Resource{ + Create: resourceArmCdnProfileCreate, + Read: resourceArmCdnProfileRead, + Update: resourceArmCdnProfileUpdate, + Delete: resourceArmCdnProfileDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "location": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + StateFunc: azureRMNormalizeLocation, + }, + + "resource_group_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "sku": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateCdnProfileSku, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceArmCdnProfileCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient) + cdnProfilesClient := client.cdnProfilesClient + + log.Printf("[INFO] preparing arguments for Azure ARM CDN Profile creation.") + + name := d.Get("name").(string) + location := d.Get("location").(string) + resGroup := d.Get("resource_group_name").(string) + sku := d.Get("sku").(string) + tags := d.Get("tags").(map[string]interface{}) + + properties := cdn.ProfilePropertiesCreateParameters{ + Sku: &cdn.Sku{ + Name: cdn.SkuName(sku), + }, + } + + cdnProfile := cdn.ProfileCreateParameters{ + Location: &location, + Properties: &properties, + Tags: expandTags(tags), + } + + resp, err := cdnProfilesClient.Create(name, cdnProfile, resGroup) + if err != nil { + return err + } + + d.SetId(*resp.ID) + + log.Printf("[DEBUG] Waiting for CDN Profile (%s) to become available", name) + stateConf := &resource.StateChangeConf{ + Pending: []string{"Accepted", "Updating", "Creating"}, + Target: "Succeeded", + Refresh: cdnProfileStateRefreshFunc(client, resGroup, name), + Timeout: 10 * time.Minute, + } + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for CDN Profile (%s) to become available: %s", name, err) + } + + return resourceArmCdnProfileRead(d, meta) +} + +func resourceArmCdnProfileRead(d *schema.ResourceData, meta interface{}) error { + cdnProfilesClient := meta.(*ArmClient).cdnProfilesClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + name := id.Path["Profiles"] + + resp, err := cdnProfilesClient.Get(name, resGroup) + if resp.StatusCode == http.StatusNotFound { + d.SetId("") + return nil + } + if err != nil { + return fmt.Errorf("Error making Read request on Azure CDN Profile %s: %s", name, err) + } + + if resp.Properties != nil && resp.Properties.Sku != nil { + d.Set("sku", string(resp.Properties.Sku.Name)) + } + + flattenAndSetTags(d, resp.Tags) + + return nil +} + +func resourceArmCdnProfileUpdate(d *schema.ResourceData, meta interface{}) error { + cdnProfilesClient := meta.(*ArmClient).cdnProfilesClient + + if !d.HasChange("tags") { + return nil + } + + name := d.Get("name").(string) + resGroup := d.Get("resource_group_name").(string) + newTags := d.Get("tags").(map[string]interface{}) + + props := cdn.ProfileUpdateParameters{ + Tags: expandTags(newTags), + } + + _, err := cdnProfilesClient.Update(name, props, resGroup) + if err != nil { + return fmt.Errorf("Error issuing Azure ARM update request to update CDN Profile %q: %s", name, err) + } + + return resourceArmCdnProfileRead(d, meta) +} + +func resourceArmCdnProfileDelete(d *schema.ResourceData, meta interface{}) error { + cdnProfilesClient := meta.(*ArmClient).cdnProfilesClient + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resGroup := id.ResourceGroup + name := id.Path["Profiles"] + + _, err = cdnProfilesClient.DeleteIfExists(name, resGroup) + + return err +} + +func cdnProfileStateRefreshFunc(client *ArmClient, resourceGroupName string, cdnProfileName string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + res, err := client.cdnProfilesClient.Get(cdnProfileName, resourceGroupName) + if err != nil { + return nil, "", fmt.Errorf("Error issuing read request in cdnProfileStateRefreshFunc to Azure ARM for CND Profile '%s' (RG: '%s'): %s", cdnProfileName, resourceGroupName, err) + } + return res, string(res.Properties.ProvisioningState), nil + } +} + +func validateCdnProfileSku(v interface{}, k string) (ws []string, errors []error) { + value := strings.ToLower(v.(string)) + skus := map[string]bool{ + "standard": true, + "premium": true, + } + + if !skus[value] { + errors = append(errors, fmt.Errorf("CDN Profile SKU can only be Standard or Premium")) + } + return +} diff --git a/builtin/providers/azurerm/resource_arm_cdn_profile_test.go b/builtin/providers/azurerm/resource_arm_cdn_profile_test.go new file mode 100644 index 000000000000..24cc2f4f3763 --- /dev/null +++ b/builtin/providers/azurerm/resource_arm_cdn_profile_test.go @@ -0,0 +1,199 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestResourceAzureRMCdnProfileSKU_validation(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + { + Value: "Random", + ErrCount: 1, + }, + { + Value: "Standard", + ErrCount: 0, + }, + { + Value: "Premium", + ErrCount: 0, + }, + { + Value: "STANDARD", + ErrCount: 0, + }, + { + Value: "PREMIUM", + ErrCount: 0, + }, + } + + for _, tc := range cases { + _, errors := validateCdnProfileSku(tc.Value, "azurerm_cdn_profile") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the Azure RM CDN Profile SKU to trigger a validation error") + } + } +} + +func TestAccAzureRMCdnProfile_basic(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMCdnProfileDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAzureRMCdnProfile_basic, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMCdnProfileExists("azurerm_cdn_profile.test"), + ), + }, + }, + }) +} + +func TestAccAzureRMCdnProfile_withTags(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMCdnProfileDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAzureRMCdnProfile_withTags, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMCdnProfileExists("azurerm_cdn_profile.test"), + resource.TestCheckResourceAttr( + "azurerm_cdn_profile.test", "tags.#", "2"), + resource.TestCheckResourceAttr( + "azurerm_cdn_profile.test", "tags.environment", "Production"), + resource.TestCheckResourceAttr( + "azurerm_cdn_profile.test", "tags.cost_center", "MSFT"), + ), + }, + + resource.TestStep{ + Config: testAccAzureRMCdnProfile_withTagsUpdate, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMCdnProfileExists("azurerm_cdn_profile.test"), + resource.TestCheckResourceAttr( + "azurerm_cdn_profile.test", "tags.#", "1"), + resource.TestCheckResourceAttr( + "azurerm_cdn_profile.test", "tags.environment", "staging"), + ), + }, + }, + }) +} + +func testCheckAzureRMCdnProfileExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + name := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for availability set: %s", name) + } + + conn := testAccProvider.Meta().(*ArmClient).cdnProfilesClient + + resp, err := conn.Get(name, resourceGroup) + if err != nil { + return fmt.Errorf("Bad: Get on cdnProfilesClient: %s", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: CDN Profile %q (resource group: %q) does not exist", name, resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMCdnProfileDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).cdnProfilesClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_cdn_profile" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.Get(name, resourceGroup) + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("CDN Profile still exists:\n%#v", resp.Properties) + } + } + + return nil +} + +var testAccAzureRMCdnProfile_basic = ` +resource "azurerm_resource_group" "test" { + name = "acceptanceTestResourceGroup1" + location = "West US" +} +resource "azurerm_cdn_profile" "test" { + name = "acceptanceTestCdnProfile1" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" +} +` + +var testAccAzureRMCdnProfile_withTags = ` +resource "azurerm_resource_group" "test" { + name = "acceptanceTestResourceGroup1" + location = "West US" +} +resource "azurerm_cdn_profile" "test" { + name = "acceptanceTestCdnProfile1" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + + tags { + environment = "Production" + cost_center = "MSFT" + } +} +` + +var testAccAzureRMCdnProfile_withTagsUpdate = ` +resource "azurerm_resource_group" "test" { + name = "acceptanceTestResourceGroup1" + location = "West US" +} +resource "azurerm_cdn_profile" "test" { + name = "acceptanceTestCdnProfile1" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + + tags { + environment = "staging" + } +} +` diff --git a/website/source/docs/providers/azurerm/r/cdn_profile.html.markdown b/website/source/docs/providers/azurerm/r/cdn_profile.html.markdown new file mode 100644 index 000000000000..fb5c010595dd --- /dev/null +++ b/website/source/docs/providers/azurerm/r/cdn_profile.html.markdown @@ -0,0 +1,54 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_cdn_profile" +sidebar_current: "docs-azurerm-resource-cdn-profile" +description: |- + Create a CDN Profile to create a collection of CDN Endpoints. +--- + +# azurerm\_cdn\_profile + +Create a CDN Profile to create a collection of CDN Endpoints. + +## Example Usage + +``` +resource "azurerm_resource_group" "test" { + name = "resourceGroup1" + location = "West US" +} + +resource "azurerm_cdn_profile" "test" { + name = "acceptanceTestCdnProfile1" + location = "West US" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Standard" + + tags { + environment = "Production" + cost_center = "MSFT" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the CDN Profile. Changing this forces a + new resource to be created. + +* `resource_group_name` - (Required) The name of the resource group in which to + create the CDN Profile. + +* `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. + +* `sku` - (Required) The pricing related information of current CDN profile. Accepted values are `Standard` or `Premium`. + +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The CDN Profile ID. \ No newline at end of file diff --git a/website/source/layouts/azurerm.erb b/website/source/layouts/azurerm.erb index e6e0418575c5..29d4d6506bbb 100644 --- a/website/source/layouts/azurerm.erb +++ b/website/source/layouts/azurerm.erb @@ -15,6 +15,17 @@ azurerm_resource_group + > + CDN Resources + + + > Network Resources