diff --git a/azurerm/internal/services/media/client/client.go b/azurerm/internal/services/media/client/client.go index a6371997426f..8c827eb0767a 100644 --- a/azurerm/internal/services/media/client/client.go +++ b/azurerm/internal/services/media/client/client.go @@ -11,6 +11,7 @@ type Client struct { TransformsClient *media.TransformsClient StreamingEndpointsClient *media.StreamingEndpointsClient JobsClient *media.JobsClient + StreamingLocatorsClient *media.StreamingLocatorsClient } func NewClient(o *common.ClientOptions) *Client { @@ -29,11 +30,15 @@ func NewClient(o *common.ClientOptions) *Client { JobsClient := media.NewJobsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&JobsClient.Client, o.ResourceManagerAuthorizer) + StreamingLocatorsClient := media.NewStreamingLocatorsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&StreamingLocatorsClient.Client, o.ResourceManagerAuthorizer) + return &Client{ ServicesClient: &ServicesClient, AssetsClient: &AssetsClient, TransformsClient: &TransformsClient, StreamingEndpointsClient: &StreamingEndpointsClient, JobsClient: &JobsClient, + StreamingLocatorsClient: &StreamingLocatorsClient, } } diff --git a/azurerm/internal/services/media/media_streaming_locator_resource.go b/azurerm/internal/services/media/media_streaming_locator_resource.go new file mode 100644 index 000000000000..6424ee70d0e7 --- /dev/null +++ b/azurerm/internal/services/media/media_streaming_locator_resource.go @@ -0,0 +1,389 @@ +package media + +import ( + "fmt" + "log" + "regexp" + "time" + + "github.com/Azure/azure-sdk-for-go/services/mediaservices/mgmt/2020-05-01/media" + "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + uuid "github.com/satori/go.uuid" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/media/parse" + azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceMediaStreamingLocator() *schema.Resource { + return &schema.Resource{ + Create: resourceMediaStreamingLocatorCreate, + Read: resourceMediaStreamingLocatorRead, + Delete: resourceMediaStreamingLocatorDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { + _, err := parse.StreamingLocatorID(id) + return err + }), + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile("^[-a-zA-Z0-9(_)]{1,128}$"), + "Steraming Locator name must be 1 - 128 characters long, can contain letters, numbers, underscores, and hyphens (but the first and last character must be a letter or number).", + ), + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "media_services_account_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: ValidateMediaServicesAccountName, + }, + + "asset_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile("^[-a-zA-Z0-9]{1,128}$"), + "Asset name must be 1 - 128 characters long, contain only letters, hyphen and numbers.", + ), + }, + + "streaming_policy_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "alternative_media_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "content_key": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "content_key_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, + }, + + "label_reference_in_streaming_policy": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "policy_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(media.StreamingLocatorContentKeyTypeCommonEncryptionCbcs), + string(media.StreamingLocatorContentKeyTypeCommonEncryptionCenc), + string(media.StreamingLocatorContentKeyTypeEnvelopeEncryption), + }, false), + }, + + "value": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "default_content_key_policy_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "end_time": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.IsRFC3339Time, + }, + + "start_time": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IsRFC3339Time, + }, + + "streaming_locator_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, + }, + }, + } +} + +func resourceMediaStreamingLocatorCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Media.StreamingLocatorsClient + subscriptionID := meta.(*clients.Client).Account.SubscriptionId + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + resourceID := parse.NewStreamingLocatorID(subscriptionID, d.Get("resource_group_name").(string), d.Get("media_services_account_name").(string), d.Get("name").(string)) + if d.IsNewResource() { + existing, err := client.Get(ctx, resourceID.ResourceGroup, resourceID.MediaserviceName, resourceID.Name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Media Job %q (Media Service account %q) (ResourceGroup %q): %s", resourceID.Name, resourceID.MediaserviceName, resourceID.ResourceGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_media_streaming_locator", *existing.ID) + } + } + + parameters := media.StreamingLocator{ + StreamingLocatorProperties: &media.StreamingLocatorProperties{ + AssetName: utils.String(d.Get("asset_name").(string)), + StreamingPolicyName: utils.String(d.Get("streaming_policy_name").(string)), + }, + } + + if alternativeMediaID, ok := d.GetOk("alternative_media_id"); ok { + parameters.StreamingLocatorProperties.AlternativeMediaID = utils.String(alternativeMediaID.(string)) + } + + if contentKeys, ok := d.GetOk("content_key"); ok { + parameters.StreamingLocatorProperties.ContentKeys = expandContentKeys(contentKeys.([]interface{})) + } + + if defaultContentKeyPolicyName, ok := d.GetOk("default_content_key_policy_name"); ok { + parameters.StreamingLocatorProperties.DefaultContentKeyPolicyName = utils.String(defaultContentKeyPolicyName.(string)) + } + + if endTimeRaw, ok := d.GetOk("end_time"); ok { + if endTimeRaw.(string) != "" { + endTime, err := date.ParseTime(time.RFC3339, endTimeRaw.(string)) + if err != nil { + return err + } + parameters.StreamingLocatorProperties.EndTime = &date.Time{ + Time: endTime, + } + } + } + + if startTimeRaw, ok := d.GetOk("start_time"); ok { + if startTimeRaw.(string) != "" { + startTime, err := date.ParseTime(time.RFC3339, startTimeRaw.(string)) + if err != nil { + return err + } + parameters.StreamingLocatorProperties.StartTime = &date.Time{ + Time: startTime, + } + } + } + + if idRaw, ok := d.GetOk("streaming_locator_id"); ok { + id := uuid.FromStringOrNil(idRaw.(string)) + parameters.StreamingLocatorProperties.StreamingLocatorID = &id + } + + if _, err := client.Create(ctx, resourceID.ResourceGroup, resourceID.MediaserviceName, resourceID.Name, parameters); err != nil { + return fmt.Errorf("Error creating Streaming Locator %q in Media Services Account %q (Resource Group %q): %+v", resourceID.Name, resourceID.MediaserviceName, resourceID.ResourceGroup, err) + } + + d.SetId(resourceID.ID()) + + return resourceMediaStreamingLocatorRead(d, meta) +} + +func resourceMediaStreamingLocatorRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Media.StreamingLocatorsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.StreamingLocatorID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.MediaserviceName, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Streaming Locator %q was not found in Media Services Account %q and Resource Group %q - removing from state", id.Name, id.MediaserviceName, id.ResourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving Streaming Locator %q in Media Services Account %q (Resource Group %q): %+v", id.Name, id.MediaserviceName, id.ResourceGroup, err) + } + + d.Set("name", id.Name) + d.Set("resource_group_name", id.ResourceGroup) + d.Set("media_services_account_name", id.MediaserviceName) + + if props := resp.StreamingLocatorProperties; props != nil { + d.Set("asset_name", props.AssetName) + d.Set("streaming_policy_name", props.StreamingPolicyName) + d.Set("alternative_media_id", props.AlternativeMediaID) + d.Set("default_content_key_policy_name", props.DefaultContentKeyPolicyName) + + contentKeys := flattenContentKeys(resp.ContentKeys) + if err := d.Set("content_key", contentKeys); err != nil { + return fmt.Errorf("Error flattening `content_key`: %s", err) + } + + endTime := "" + if props.EndTime != nil { + endTime = props.EndTime.Format(time.RFC3339) + } + d.Set("end_time", endTime) + + startTime := "" + if props.StartTime != nil { + startTime = props.StartTime.Format(time.RFC3339) + } + d.Set("start_time", startTime) + + id := "" + if props.StreamingLocatorID != nil { + id = props.StreamingLocatorID.String() + } + d.Set("streaming_locator_id", id) + } + + return nil +} + +func resourceMediaStreamingLocatorDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Media.StreamingLocatorsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.StreamingLocatorID(d.Id()) + if err != nil { + return err + } + + if _, err = client.Delete(ctx, id.ResourceGroup, id.MediaserviceName, id.Name); err != nil { + return fmt.Errorf("Error deleting Streaming Locator %q in Media Services Account %q (Resource Group %q): %+v", id.Name, id.MediaserviceName, id.ResourceGroup, err) + } + + return nil +} + +func expandContentKeys(input []interface{}) *[]media.StreamingLocatorContentKey { + results := make([]media.StreamingLocatorContentKey, 0) + + for _, contentKeyRaw := range input { + contentKey := contentKeyRaw.(map[string]interface{}) + + streamingLocatorContentKey := media.StreamingLocatorContentKey{} + + if contentKey["content_key_id"] != nil { + id := uuid.FromStringOrNil(contentKey["content_key_id"].(string)) + streamingLocatorContentKey.ID = &id + } + + if contentKey["label_reference_in_streaming_policy"] != nil { + streamingLocatorContentKey.LabelReferenceInStreamingPolicy = utils.String(contentKey["label_reference_in_streaming_policy"].(string)) + } + + if contentKey["policy_name"] != nil { + streamingLocatorContentKey.PolicyName = utils.String(contentKey["policy_name"].(string)) + } + + if contentKey["type"] != nil { + streamingLocatorContentKey.Type = media.StreamingLocatorContentKeyType(contentKey["type"].(string)) + } + + if contentKey["value"] != nil { + streamingLocatorContentKey.Value = utils.String(contentKey["value"].(string)) + } + + results = append(results, streamingLocatorContentKey) + } + + return &results +} + +func flattenContentKeys(input *[]media.StreamingLocatorContentKey) []interface{} { + if input == nil { + return []interface{}{} + } + + results := make([]interface{}, 0) + for _, contentKey := range *input { + id := "" + if contentKey.ID != nil { + id = contentKey.ID.String() + } + + labelReferenceInStreamingPolicy := "" + if contentKey.LabelReferenceInStreamingPolicy != nil { + labelReferenceInStreamingPolicy = *contentKey.LabelReferenceInStreamingPolicy + } + + policyName := "" + if contentKey.PolicyName != nil { + policyName = *contentKey.PolicyName + } + + value := "" + if contentKey.Value != nil { + value = *contentKey.Value + } + + results = append(results, map[string]interface{}{ + "content_key_id": id, + "label_reference_in_streaming_policy": labelReferenceInStreamingPolicy, + "policy_name": policyName, + "type": string(contentKey.Type), + "value": value, + }) + } + + return results +} diff --git a/azurerm/internal/services/media/media_streaming_locator_resource_test.go b/azurerm/internal/services/media/media_streaming_locator_resource_test.go new file mode 100644 index 000000000000..db6dbd572c67 --- /dev/null +++ b/azurerm/internal/services/media/media_streaming_locator_resource_test.go @@ -0,0 +1,198 @@ +package media_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance/check" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/media/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +type StreamingLocatorResource struct{} + +func TestAccStreamingLocator_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_media_streaming_locator", "test") + r := StreamingLocatorResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).Key("name").HasValue("Locator-1"), + check.That(data.ResourceName).Key("asset_name").HasValue("test"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccStreamingLocator_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_media_streaming_locator", "test") + r := StreamingLocatorResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).Key("name").HasValue("Locator-1"), + check.That(data.ResourceName).Key("asset_name").HasValue("test"), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccStreamingLocator_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_media_streaming_locator", "test") + r := StreamingLocatorResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.complete(data), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).Key("start_time").HasValue("2018-03-01T00:00:00Z"), + check.That(data.ResourceName).Key("end_time").HasValue("2028-12-31T23:59:59Z"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccStreamingLocator_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_media_streaming_locator", "test") + r := StreamingLocatorResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).Key("name").HasValue("Locator-1"), + check.That(data.ResourceName).Key("asset_name").HasValue("test"), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).Key("start_time").HasValue("2018-03-01T00:00:00Z"), + check.That(data.ResourceName).Key("end_time").HasValue("2028-12-31T23:59:59Z"), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).Key("name").HasValue("Locator-1"), + check.That(data.ResourceName).Key("asset_name").HasValue("test"), + ), + }, + data.ImportStep(), + }) +} + +func (StreamingLocatorResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { + id, err := parse.StreamingLocatorID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.Media.StreamingLocatorsClient.Get(ctx, id.ResourceGroup, id.MediaserviceName, id.Name) + if err != nil { + return nil, fmt.Errorf("retrieving Content Key Policy %s (Media Services Account %s) (resource group: %s): %v", id.Name, id.MediaserviceName, id.ResourceGroup, err) + } + + return utils.Bool(resp.StreamingLocatorProperties != nil), nil +} + +func (r StreamingLocatorResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_media_streaming_locator" "test" { + name = "Locator-1" + resource_group_name = azurerm_resource_group.test.name + media_services_account_name = azurerm_media_services_account.test.name + streaming_policy_name = "Predefined_ClearStreamingOnly" + asset_name = azurerm_media_asset.test.name +} + +`, r.template(data)) +} + +func (r StreamingLocatorResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_media_streaming_locator" "import" { + name = azurerm_media_streaming_locator.test.name + resource_group_name = azurerm_media_streaming_locator.test.resource_group_name + media_services_account_name = azurerm_media_streaming_locator.test.media_services_account_name + streaming_policy_name = "Predefined_ClearStreamingOnly" + asset_name = azurerm_media_asset.test.name +} + +`, r.basic(data)) +} + +func (r StreamingLocatorResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_media_streaming_locator" "test" { + name = "Job-1" + resource_group_name = azurerm_resource_group.test.name + media_services_account_name = azurerm_media_services_account.test.name + streaming_policy_name = "Predefined_DownloadOnly" + asset_name = azurerm_media_asset.test.name + start_time = "2018-03-01T00:00:00Z" + end_time = "2028-12-31T23:59:59Z" + streaming_locator_id = "90000000-0000-0000-0000-000000000000" + alternative_media_id = "my-Alternate-MediaID" +} + +`, r.template(data)) +} + +func (StreamingLocatorResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-media-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa1%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "GRS" +} + +resource "azurerm_media_services_account" "test" { + name = "acctestmsa%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + storage_account { + id = azurerm_storage_account.test.id + is_primary = true + } +} + +resource "azurerm_media_asset" "test" { + name = "test" + resource_group_name = azurerm_resource_group.test.name + media_services_account_name = azurerm_media_services_account.test.name +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString) +} diff --git a/azurerm/internal/services/media/parse/streaming_locator.go b/azurerm/internal/services/media/parse/streaming_locator.go new file mode 100644 index 000000000000..2761f94b3f78 --- /dev/null +++ b/azurerm/internal/services/media/parse/streaming_locator.go @@ -0,0 +1,75 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type StreamingLocatorId struct { + SubscriptionId string + ResourceGroup string + MediaserviceName string + Name string +} + +func NewStreamingLocatorID(subscriptionId, resourceGroup, mediaserviceName, name string) StreamingLocatorId { + return StreamingLocatorId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + MediaserviceName: mediaserviceName, + Name: name, + } +} + +func (id StreamingLocatorId) String() string { + segments := []string{ + fmt.Sprintf("Name %q", id.Name), + fmt.Sprintf("Mediaservice Name %q", id.MediaserviceName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Streaming Locator", segmentsStr) +} + +func (id StreamingLocatorId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Media/mediaservices/%s/streaminglocators/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.MediaserviceName, id.Name) +} + +// StreamingLocatorID parses a StreamingLocator ID into an StreamingLocatorId struct +func StreamingLocatorID(input string) (*StreamingLocatorId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := StreamingLocatorId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.MediaserviceName, err = id.PopSegment("mediaservices"); err != nil { + return nil, err + } + if resourceId.Name, err = id.PopSegment("streaminglocators"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/azurerm/internal/services/media/parse/streaming_locator_test.go b/azurerm/internal/services/media/parse/streaming_locator_test.go new file mode 100644 index 000000000000..081df8a34af3 --- /dev/null +++ b/azurerm/internal/services/media/parse/streaming_locator_test.go @@ -0,0 +1,128 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/resourceid" +) + +var _ resourceid.Formatter = StreamingLocatorId{} + +func TestStreamingLocatorIDFormatter(t *testing.T) { + actual := NewStreamingLocatorID("12345678-1234-9876-4563-123456789012", "resGroup1", "account1", "locator1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/streaminglocators/locator1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestStreamingLocatorID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *StreamingLocatorId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing MediaserviceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/", + Error: true, + }, + + { + // missing value for MediaserviceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/", + Error: true, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/", + Error: true, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/streaminglocators/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/streaminglocators/locator1", + Expected: &StreamingLocatorId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + MediaserviceName: "account1", + Name: "locator1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.MEDIA/MEDIASERVICES/ACCOUNT1/STREAMINGLOCATORS/LOCATOR1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := StreamingLocatorID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.MediaserviceName != v.Expected.MediaserviceName { + t.Fatalf("Expected %q but got %q for MediaserviceName", v.Expected.MediaserviceName, actual.MediaserviceName) + } + if actual.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + } +} diff --git a/azurerm/internal/services/media/registration.go b/azurerm/internal/services/media/registration.go index 184a8ec36c28..ec336b9e6960 100644 --- a/azurerm/internal/services/media/registration.go +++ b/azurerm/internal/services/media/registration.go @@ -31,5 +31,6 @@ func (r Registration) SupportedResources() map[string]*schema.Resource { "azurerm_media_job": resourceMediaJob(), "azurerm_media_streaming_endpoint": resourceMediaStreamingEndpoint(), "azurerm_media_transform": resourceMediaTransform(), + "azurerm_media_streaming_locator": resourceMediaStreamingLocator(), } } diff --git a/azurerm/internal/services/media/resourceids.go b/azurerm/internal/services/media/resourceids.go index ccfcfdf295d4..4d11e9e97446 100644 --- a/azurerm/internal/services/media/resourceids.go +++ b/azurerm/internal/services/media/resourceids.go @@ -5,3 +5,4 @@ package media //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Asset -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/assets/asset1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=StreamingEndpoint -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/streamingendpoints/endpoint1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Job -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/transforms/transform1/jobs/job1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=StreamingLocator -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/streaminglocators/locator1 diff --git a/azurerm/internal/services/media/validate/streaming_locator_id.go b/azurerm/internal/services/media/validate/streaming_locator_id.go new file mode 100644 index 000000000000..d5ef2300ae98 --- /dev/null +++ b/azurerm/internal/services/media/validate/streaming_locator_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/media/parse" +) + +func StreamingLocatorID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.StreamingLocatorID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/azurerm/internal/services/media/validate/streaming_locator_id_test.go b/azurerm/internal/services/media/validate/streaming_locator_id_test.go new file mode 100644 index 000000000000..3f069520c444 --- /dev/null +++ b/azurerm/internal/services/media/validate/streaming_locator_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestStreamingLocatorID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing MediaserviceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/", + Valid: false, + }, + + { + // missing value for MediaserviceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/", + Valid: false, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/", + Valid: false, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/streaminglocators/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/streaminglocators/locator1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.MEDIA/MEDIASERVICES/ACCOUNT1/STREAMINGLOCATORS/LOCATOR1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := StreamingLocatorID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/website/docs/r/media_streaming_locator.html.markdown b/website/docs/r/media_streaming_locator.html.markdown new file mode 100644 index 000000000000..2ddf31d2dcf5 --- /dev/null +++ b/website/docs/r/media_streaming_locator.html.markdown @@ -0,0 +1,118 @@ +--- +subcategory: "Media" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_media_streaming_locator" +description: |- + Manages a Media Streaming Locator. +--- + +# azurerm_media_streaming_locator + +Manages a Media Streaming Locator. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "media-resources" + location = "West Europe" +} + +resource "azurerm_storage_account" "example" { + name = "examplestoracc" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + account_tier = "Standard" + account_replication_type = "GRS" +} + +resource "azurerm_media_services_account" "example" { + name = "examplemediaacc" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + + storage_account { + id = azurerm_storage_account.example.id + is_primary = true + } +} + +resource "azurerm_media_asset" "example" { + name = "Asset1" + resource_group_name = azurerm_resource_group.example.name + media_services_account_name = azurerm_media_services_account.example.name + description = "Asset description" +} + +resource "azurerm_media_streaming_locator" "example" { + name = "example" + resource_group_name = azurerm_resource_group.example.name + media_services_account_name = azurerm_media_services_account.example.name + asset_name = azurerm_media_asset.example.name + streaming_policy_name = "Predefined_ClearStreamingOnly" +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `asset_name` - (Required) Asset Name. Changing this forces a new Streaming Locator to be created. + +* `media_services_account_name` - (Required) The Media Services account name. Changing this forces a new Streaming Locator to be created. + +* `name` - (Required) The name which should be used for this Streaming Locator. Changing this forces a new Streaming Locator to be created. + +* `resource_group_name` - (Required) The name of the Resource Group where the Streaming Locator should exist. Changing this forces a new Streaming Locator to be created. + +* `streaming_policy_name` - (Required) Name of the Streaming Policy used by this Streaming Locator. Either specify the name of Streaming Policy you created or use one of the predefined Streaming Policies. The predefined Streaming Policies available are: `Predefined_DownloadOnly`, `Predefined_ClearStreamingOnly`, `Predefined_DownloadAndClearStreaming`, `Predefined_ClearKey`, `Predefined_MultiDrmCencStreaming` and `Predefined_MultiDrmStreaming`. Changing this forces a new Streaming Locator to be created. + +--- + +* `alternative_media_id` - (Optional) Alternative Media ID of this Streaming Locator. Changing this forces a new Streaming Locator to be created. + +* `content_key` - (Optional) One or more `content_key` blocks as defined below. Changing this forces a new Streaming Locator to be created. + +* `default_content_key_policy_name` - (Optional) Name of the default Content Key Policy used by this Streaming Locator.Changing this forces a new Streaming Locator to be created. + +* `end_time` - (Optional) The end time of the Streaming Locator. Changing this forces a new Streaming Locator to be created. + +* `start_time` - (Optional) The start time of the Streaming Locator. Changing this forces a new Streaming Locator to be created. + +* `streaming_locator_id` - (Optional) The ID of the Streaming Locator. Changing this forces a new Streaming Locator to be created. + +--- + +A `content_key` block supports the following: + +* `content_key_id` - (Optional) ID of Content Key. Changing this forces a new Streaming Locator to be created. + +* `label_reference_in_streaming_policy` - (Optional) Label of Content Key as specified in the Streaming Policy. Changing this forces a new Streaming Locator to be created. + +* `policy_name` - (Optional) Content Key Policy used by Content Key. Changing this forces a new Streaming Locator to be created. + +* `type` - (Optional) Encryption type of Content Key. Supported values are `CommonEncryptionCbcs`, `CommonEncryptionCenc` or `EnvelopeEncryption`. Changing this forces a new Streaming Locator to be created. + +* `value` - (Optional) Value of Content Key. Changing this forces a new Streaming Locator to be created. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Streaming Locator. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Streaming Locator. +* `read` - (Defaults to 5 minutes) Used when retrieving the Streaming Locator. +* `delete` - (Defaults to 30 minutes) Used when deleting the Streaming Locator. + +## Import + +Streaming Locators can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_media_streaming_locator.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Media/mediaservices/account1/streaminglocators/locator1 +```