diff --git a/azurerm/internal/services/desktopvirtualization/resourceids.go b/azurerm/internal/services/desktopvirtualization/resourcesid.go similarity index 100% rename from azurerm/internal/services/desktopvirtualization/resourceids.go rename to azurerm/internal/services/desktopvirtualization/resourcesid.go diff --git a/azurerm/internal/services/media/client/client.go b/azurerm/internal/services/media/client/client.go index 4f97fa557586..4e5deb1b3956 100644 --- a/azurerm/internal/services/media/client/client.go +++ b/azurerm/internal/services/media/client/client.go @@ -6,8 +6,9 @@ import ( ) type Client struct { - ServicesClient *media.MediaservicesClient - AssetsClient *media.AssetsClient + ServicesClient *media.MediaservicesClient + AssetsClient *media.AssetsClient + TransformsClient *media.TransformsClient } func NewClient(o *common.ClientOptions) *Client { @@ -17,8 +18,12 @@ func NewClient(o *common.ClientOptions) *Client { AssetsClient := media.NewAssetsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&AssetsClient.Client, o.ResourceManagerAuthorizer) + TransformsClient := media.NewTransformsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&TransformsClient.Client, o.ResourceManagerAuthorizer) + return &Client{ - ServicesClient: &ServicesClient, - AssetsClient: &AssetsClient, + ServicesClient: &ServicesClient, + AssetsClient: &AssetsClient, + TransformsClient: &TransformsClient, } } diff --git a/azurerm/internal/services/media/media_services_account_resource.go b/azurerm/internal/services/media/media_services_account_resource.go index 868889a19359..8c8a59aa45c7 100644 --- a/azurerm/internal/services/media/media_services_account_resource.go +++ b/azurerm/internal/services/media/media_services_account_resource.go @@ -147,7 +147,7 @@ func resourceMediaServicesAccountCreateUpdate(d *schema.ResourceData, meta inter parameters.Identity = expandAzureRmMediaServiceIdentity(d) } - if v, ok := d.GetOk("storage_authentication"); ok { + if v, ok := d.GetOk("storage_authentication_type"); ok { parameters.StorageAuthentication = media.StorageAuthentication(v.(string)) } diff --git a/azurerm/internal/services/media/media_transform_resource.go b/azurerm/internal/services/media/media_transform_resource.go new file mode 100644 index 000000000000..2402af9dc291 --- /dev/null +++ b/azurerm/internal/services/media/media_transform_resource.go @@ -0,0 +1,459 @@ +package media + +import ( + "fmt" + "log" + "regexp" + "time" + + "github.com/Azure/azure-sdk-for-go/services/mediaservices/mgmt/2020-05-01/media" + "github.com/hashicorp/go-azure-helpers/response" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-azuread/azuread/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "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 resourceMediaTransform() *schema.Resource { + return &schema.Resource{ + Create: resourceMediaTransformCreateUpdate, + Read: resourceMediaTransformRead, + Update: resourceMediaTransformCreateUpdate, + Delete: resourceMediaTransformDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { + _, err := parse.TransformID(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}$"), + "Transform 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: validation.StringMatch( + regexp.MustCompile("^[-a-z0-9]{3,24}$"), + "Media Services Account name must be 3 - 24 characters long, contain only lowercase letters and numbers.", + ), + }, + + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "output": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "on_error_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(media.ContinueJob), string(media.StopProcessingJob), + }, true), + }, + "builtin_preset": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "preset_name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(media.AACGoodQualityAudio), string(media.AdaptiveStreaming), + string(media.ContentAwareEncoding), string(media.ContentAwareEncodingExperimental), + string(media.CopyAllBitrateNonInterleaved), string(media.H264MultipleBitrate1080p), + string(media.H264MultipleBitrate720p), string(media.H264MultipleBitrateSD), + string(media.H264SingleBitrate1080p), string(media.H264SingleBitrate720p), + string(media.H264MultipleBitrateSD), + }, true), + }, + }, + }, + }, + "audio_analyzer_preset": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "audio_language": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "ar-EG", "ar-SY", "de-DE", "en-AU", "en-GB", "en-US", "es-ES", "es-MX", + "fr-FR", "hi-IN", "it-IT", "ja-JP", "ko-KR", "pt-BR", "ru-RU", "zh-CN", + }, true), + }, + "audio_analysis_mode": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(media.Basic), string(media.Standard), + }, true), + }, + }, + }, + }, + "video_analyzer_preset": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "audio_language": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "ar-EG", "ar-SY", "de-DE", "en-AU", "en-GB", "en-US", "es-ES", "es-MX", + "fr-FR", "hi-IN", "it-IT", "ja-JP", "ko-KR", "pt-BR", "ru-RU", "zh-CN", + }, true), + }, + "audio_analysis_mode": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(media.Basic), string(media.Standard), + }, true), + }, + "insights_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(media.AllInsights), string(media.AudioInsightsOnly), string(media.VideoInsightsOnly), + }, true), + }, + }, + }, + }, + "face_detector_preset": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "analysis_resolution": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(media.SourceResolution), string(media.StandardDefinition), + }, true), + }, + }, + }, + }, + "relative_priority": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(media.High), string(media.Normal), string(media.Low), + }, true), + }, + }, + }, + }, + }, + } +} + +func resourceMediaTransformCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Media.TransformsClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + transformName := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + accountName := d.Get("media_services_account_name").(string) + description := d.Get("description").(string) + + parameters := media.Transform{ + TransformProperties: &media.TransformProperties{ + Description: utils.String(description), + }, + } + + if v, ok := d.GetOk("output"); ok { + transformOutput, err := expandTransformOuputs(v.([]interface{})) + if err != nil { + return err + } + parameters.Outputs = transformOutput + } + + if _, err := client.CreateOrUpdate(ctx, resourceGroup, accountName, transformName, parameters); err != nil { + return fmt.Errorf("Error creating Transform %q in Media Services Account %q (Resource Group %q): %+v", transformName, accountName, resourceGroup, err) + } + + transform, err := client.Get(ctx, resourceGroup, accountName, transformName) + if err != nil { + return fmt.Errorf("Error retrieving Transform %q from Media Services Account %q (Resource Group %q): %+v", transformName, accountName, resourceGroup, err) + } + + d.SetId(*transform.ID) + + return resourceMediaTransformRead(d, meta) +} + +func resourceMediaTransformRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Media.TransformsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.TransformID(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] Transform %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 Transform %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.TransformProperties; props != nil { + if description := props.Description; description != nil { + d.Set("description", description) + } + + outputs := flattenTransformOutputs(props.Outputs) + if err := d.Set("output", outputs); err != nil { + return fmt.Errorf("Error flattening `output`: %s", err) + } + } + + return nil +} + +func resourceMediaTransformDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Media.TransformsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.TransformID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Delete(ctx, id.ResourceGroup, id.MediaserviceName, id.Name) + if err != nil { + if response.WasNotFound(resp.Response) { + return nil + } + return fmt.Errorf("Error deleting Transform %q in Media Services Account %q (Resource Group %q): %+v", id.Name, id.MediaserviceName, id.ResourceGroup, err) + } + + return nil +} + +func expandTransformOuputs(input []interface{}) (*[]media.TransformOutput, error) { + results := make([]media.TransformOutput, 0) + + for _, transformOuputRaw := range input { + transform := transformOuputRaw.(map[string]interface{}) + + preset, err := expandPreset(transform) + if err != nil { + return nil, err + } + + transformOuput := media.TransformOutput{ + Preset: preset, + } + + if transform["on_error_action"] != nil { + transformOuput.OnError = media.OnErrorType(transform["on_error_action"].(string)) + } + + if transform["relative_priority"] != nil { + transformOuput.RelativePriority = media.Priority(transform["relative_priority"].(string)) + } + + results = append(results, transformOuput) + } + + return &results, nil +} + +func flattenTransformOutputs(input *[]media.TransformOutput) []interface{} { + if input == nil { + return []interface{}{} + } + + results := make([]interface{}, 0) + for _, transformOuput := range *input { + output := make(map[string]interface{}) + output["on_error_action"] = string(transformOuput.OnError) + output["relative_priority"] = string(transformOuput.RelativePriority) + attribute, preset := flattenPreset(transformOuput.Preset) + if attribute != "" { + output[attribute] = preset + } + results = append(results, output) + } + + return results +} + +func expandPreset(transform map[string]interface{}) (media.BasicPreset, error) { + presetsCount := 0 + presetType := "" + if transform["builtin_preset"] != nil && len(transform["builtin_preset"].([]interface{})) > 0 { + presetsCount++ + presetType = string(media.OdataTypeMicrosoftMediaBuiltInStandardEncoderPreset) + } + if transform["audio_analyzer_preset"] != nil && len(transform["audio_analyzer_preset"].([]interface{})) > 0 { + presetsCount++ + presetType = string(media.OdataTypeMicrosoftMediaAudioAnalyzerPreset) + } + if transform["video_analyzer_preset"] != nil && len(transform["video_analyzer_preset"].([]interface{})) > 0 { + presetsCount++ + presetType = string(media.OdataTypeMicrosoftMediaVideoAnalyzerPreset) + } + if transform["face_detector_preset"] != nil && len(transform["face_detector_preset"].([]interface{})) > 0 { + presetsCount++ + presetType = string(media.OdataTypeMicrosoftMediaFaceDetectorPreset) + } + + if presetsCount == 0 { + return nil, fmt.Errorf("output must contain at least one type of preset: builtin_preset,face_detector_preset,video_analyzer_preset or audio_analyzer_preset.") + } + + if presetsCount > 1 { + return nil, fmt.Errorf("more than one type of preset in the same output is not allowed.") + } + + switch presetType { + case string(media.OdataTypeMicrosoftMediaBuiltInStandardEncoderPreset): + presets := transform["builtin_preset"].([]interface{}) + preset := presets[0].(map[string]interface{}) + if preset["preset_name"] == nil { + return nil, fmt.Errorf("preset_name is required for BuiltInStandardEncoderPreset") + } + presetName := preset["preset_name"].(string) + builtInPreset := &media.BuiltInStandardEncoderPreset{ + PresetName: media.EncoderNamedPreset(presetName), + OdataType: media.OdataTypeMicrosoftMediaBuiltInStandardEncoderPreset, + } + return builtInPreset, nil + case string(media.OdataTypeMicrosoftMediaAudioAnalyzerPreset): + presets := transform["audio_analyzer_preset"].([]interface{}) + preset := presets[0].(map[string]interface{}) + audioAnalyzerPreset := &media.AudioAnalyzerPreset{ + OdataType: media.OdataTypeMicrosoftMediaAudioAnalyzerPreset, + } + if preset["audio_language"] != nil && preset["audio_language"].(string) != "" { + audioAnalyzerPreset.AudioLanguage = utils.String(preset["audio_language"].(string)) + } + if preset["audio_analysis_mode"] != nil { + audioAnalyzerPreset.Mode = media.AudioAnalysisMode(preset["audio_analysis_mode"].(string)) + } + return audioAnalyzerPreset, nil + case string(media.OdataTypeMicrosoftMediaFaceDetectorPreset): + presets := transform["face_detector_preset"].([]interface{}) + preset := presets[0].(map[string]interface{}) + faceDetectorPreset := &media.FaceDetectorPreset{ + OdataType: media.OdataTypeMicrosoftMediaFaceDetectorPreset, + } + if preset["analysis_resolution"] != nil { + faceDetectorPreset.Resolution = media.AnalysisResolution(preset["analysis_resolution"].(string)) + } + return faceDetectorPreset, nil + case string(media.OdataTypeMicrosoftMediaVideoAnalyzerPreset): + presets := transform["video_analyzer_preset"].([]interface{}) + preset := presets[0].(map[string]interface{}) + videoAnalyzerPreset := &media.VideoAnalyzerPreset{ + OdataType: media.OdataTypeMicrosoftMediaVideoAnalyzerPreset, + } + if preset["audio_language"] != nil { + videoAnalyzerPreset.AudioLanguage = utils.String(preset["audio_language"].(string)) + } + if preset["audio_analysis_mode"] != nil { + videoAnalyzerPreset.Mode = media.AudioAnalysisMode(preset["audio_analysis_mode"].(string)) + } + if preset["insights_type"] != nil { + videoAnalyzerPreset.InsightsToExtract = media.InsightsType(preset["insights_type"].(string)) + } + return videoAnalyzerPreset, nil + default: + return nil, fmt.Errorf("output must contain at least one type of preset: builtin_preset,face_detector_preset,video_analyzer_preset or audio_analyzer_preset") + } +} + +func flattenPreset(preset media.BasicPreset) (string, []interface{}) { + if preset == nil { + return "", []interface{}{} + } + + results := make([]interface{}, 0) + result := make(map[string]interface{}) + switch preset.(type) { + case media.AudioAnalyzerPreset: + mediaAudioAnalyzerPreset, _ := preset.AsAudioAnalyzerPreset() + result["audio_analysis_mode"] = string(mediaAudioAnalyzerPreset.Mode) + if mediaAudioAnalyzerPreset.AudioLanguage != nil { + result["audio_language"] = mediaAudioAnalyzerPreset.AudioLanguage + } + results = append(results, result) + return "audio_analyzer_preset", results + case media.BuiltInStandardEncoderPreset: + builtInStandardEncoderPreset, _ := preset.AsBuiltInStandardEncoderPreset() + result["preset_name"] = string(builtInStandardEncoderPreset.PresetName) + results = append(results, result) + return "builtin_preset", results + case media.FaceDetectorPreset: + faceDetectorPreset, _ := preset.AsFaceDetectorPreset() + result["analysis_resolution"] = string(faceDetectorPreset.Resolution) + results = append(results, result) + return "face_detector_preset", results + case media.VideoAnalyzerPreset: + videoAnalyzerPreset, _ := preset.AsVideoAnalyzerPreset() + result["audio_analysis_mode"] = string(videoAnalyzerPreset.Mode) + result["insights_type"] = string(videoAnalyzerPreset.InsightsToExtract) + if videoAnalyzerPreset.AudioLanguage != nil { + result["audio_language"] = videoAnalyzerPreset.AudioLanguage + } + results = append(results, result) + return "video_analyzer_preset", results + } + + return "", results +} diff --git a/azurerm/internal/services/media/media_transform_resource_test.go b/azurerm/internal/services/media/media_transform_resource_test.go new file mode 100644 index 000000000000..385d395787f2 --- /dev/null +++ b/azurerm/internal/services/media/media_transform_resource_test.go @@ -0,0 +1,199 @@ +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 MediaTransformResource struct { +} + +func TestAccMediaTransform_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_media_transform", "test") + r := MediaTransformResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).Key("name").HasValue("Transform-1"), + check.That(data.ResourceName).Key("output.#").HasValue("1"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccMediaTransform_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_media_transform", "test") + r := MediaTransformResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.complete(data), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).Key("description").HasValue("Transform description"), + check.That(data.ResourceName).Key("output.#").HasValue("4"), + check.That(data.ResourceName).Key("name").HasValue("Transform-1"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccMediaTransform_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_media_transform", "test") + r := MediaTransformResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).Key("name").HasValue("Transform-1"), + check.That(data.ResourceName).Key("output.#").HasValue("1"), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).Key("description").HasValue("Transform description"), + check.That(data.ResourceName).Key("output.#").HasValue("4"), + check.That(data.ResourceName).Key("name").HasValue("Transform-1"), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).Key("name").HasValue("Transform-1"), + check.That(data.ResourceName).Key("output.#").HasValue("1"), + check.That(data.ResourceName).Key("description").HasValue(""), + ), + }, + data.ImportStep(), + }) +} + +func (r MediaTransformResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { + id, err := parse.TransformID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.Media.TransformsClient.Get(ctx, id.ResourceGroup, id.MediaserviceName, id.Name) + if err != nil { + return nil, fmt.Errorf("retrieving Transform %s (Media Account %s) (resource group: %s): %v", id.Name, id.MediaserviceName, id.ResourceGroup, err) + } + + return utils.Bool(resp.TransformProperties != nil), nil +} + +func (r MediaTransformResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_media_transform" "test" { + name = "Transform-1" + resource_group_name = azurerm_resource_group.test.name + media_services_account_name = azurerm_media_services_account.test.name + output { + relative_priority = "High" + on_error_action = "ContinueJob" + builtin_preset { + preset_name = "AACGoodQualityAudio" + } + } +} + +`, r.template(data)) +} + +func (r MediaTransformResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_media_transform" "test" { + name = "Transform-1" + resource_group_name = azurerm_resource_group.test.name + media_services_account_name = azurerm_media_services_account.test.name + description = "Transform description" + output { + relative_priority = "High" + on_error_action = "ContinueJob" + builtin_preset { + preset_name = "AACGoodQualityAudio" + } + } + + output { + relative_priority = "High" + on_error_action = "StopProcessingJob" + audio_analyzer_preset { + audio_language = "en-US" + audio_analysis_mode = "Basic" + } + } + + output { + relative_priority = "Low" + on_error_action = "StopProcessingJob" + face_detector_preset { + analysis_resolution = "StandardDefinition" + } + } + + output { + relative_priority = "Normal" + on_error_action = "StopProcessingJob" + video_analyzer_preset { + audio_language = "en-US" + audio_analysis_mode = "Basic" + insights_type = "AllInsights" + } + } +} + +`, r.template(data)) +} + +func (r MediaTransformResource) 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 + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString) +} diff --git a/azurerm/internal/services/media/parse/transform.go b/azurerm/internal/services/media/parse/transform.go new file mode 100644 index 000000000000..760479ccf7da --- /dev/null +++ b/azurerm/internal/services/media/parse/transform.go @@ -0,0 +1,74 @@ +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 TransformId struct { + SubscriptionId string + ResourceGroup string + MediaserviceName string + Name string +} + +func NewTransformID(subscriptionId, resourceGroup, mediaserviceName, name string) TransformId { + return TransformId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + MediaserviceName: mediaserviceName, + Name: name, + } +} + +func (id TransformId) String() string { + segments := []string{ + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + fmt.Sprintf("Mediaservice Name %q", id.MediaserviceName), + fmt.Sprintf("Name %q", id.Name), + } + return strings.Join(segments, " / ") +} + +func (id TransformId) ID(_ string) string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Media/mediaservices/%s/transforms/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.MediaserviceName, id.Name) +} + +// TransformID parses a Transform ID into an TransformId struct +func TransformID(input string) (*TransformId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := TransformId{ + 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("transforms"); 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/transform_test.go b/azurerm/internal/services/media/parse/transform_test.go new file mode 100644 index 000000000000..2c5fbfcd0777 --- /dev/null +++ b/azurerm/internal/services/media/parse/transform_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 = TransformId{} + +func TestTransformIDFormatter(t *testing.T) { + actual := NewTransformID("12345678-1234-9876-4563-123456789012", "resGroup1", "account1", "transform1").ID("") + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/transforms/transform1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestTransformID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *TransformId + }{ + + { + // 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/transforms/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/transforms/transform1", + Expected: &TransformId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + MediaserviceName: "account1", + Name: "transform1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.MEDIA/MEDIASERVICES/ACCOUNT1/TRANSFORMS/TRANSFORM1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := TransformID(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 cddc8763b4be..707fb9994568 100644 --- a/azurerm/internal/services/media/registration.go +++ b/azurerm/internal/services/media/registration.go @@ -28,5 +28,6 @@ func (r Registration) SupportedResources() map[string]*schema.Resource { return map[string]*schema.Resource{ "azurerm_media_services_account": resourceMediaServicesAccount(), "azurerm_media_asset": resourceMediaAsset(), + "azurerm_media_transform": resourceMediaTransform(), } } diff --git a/azurerm/internal/services/media/resourceids.go b/azurerm/internal/services/media/resourceids.go index a6b6a701e49f..2ebdb0ed34aa 100644 --- a/azurerm/internal/services/media/resourceids.go +++ b/azurerm/internal/services/media/resourceids.go @@ -1,4 +1,5 @@ package media //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=MediaService -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Transform -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/transforms/transform1 //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 diff --git a/azurerm/internal/services/media/validate/transform_id.go b/azurerm/internal/services/media/validate/transform_id.go new file mode 100644 index 000000000000..1755cc5f65ad --- /dev/null +++ b/azurerm/internal/services/media/validate/transform_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 TransformID(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.TransformID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/azurerm/internal/services/media/validate/transform_id_test.go b/azurerm/internal/services/media/validate/transform_id_test.go new file mode 100644 index 000000000000..478094b07966 --- /dev/null +++ b/azurerm/internal/services/media/validate/transform_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 TestTransformID(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/transforms/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Media/mediaservices/account1/transforms/transform1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.MEDIA/MEDIASERVICES/ACCOUNT1/TRANSFORMS/TRANSFORM1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := TransformID(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/azurerm.erb b/website/azurerm.erb index cd08fdde7a73..2893427db9c0 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -2190,6 +2190,9 @@