Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Resource: azurerm_cognitive_account_rai_policy #28013

Merged
merged 10 commits into from
Jan 30, 2025
9 changes: 9 additions & 0 deletions internal/services/cognitive/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import (
"github.com/hashicorp/go-azure-sdk/resource-manager/cognitive/2024-10-01/cognitiveservicesaccounts"
"github.com/hashicorp/go-azure-sdk/resource-manager/cognitive/2024-10-01/deployments"
"github.com/hashicorp/go-azure-sdk/resource-manager/cognitive/2024-10-01/raiblocklists"
"github.com/hashicorp/go-azure-sdk/resource-manager/cognitive/2024-10-01/raipolicies"
"github.com/hashicorp/terraform-provider-azurerm/internal/common"
)

type Client struct {
AccountsClient *cognitiveservicesaccounts.CognitiveServicesAccountsClient
DeploymentsClient *deployments.DeploymentsClient
RaiBlocklistsClient *raiblocklists.RaiBlocklistsClient
RaiPoliciesClient *raipolicies.RaiPoliciesClient
}

func NewClient(o *common.ClientOptions) (*Client, error) {
Expand All @@ -31,6 +33,12 @@ func NewClient(o *common.ClientOptions) (*Client, error) {
}
o.Configure(deploymentsClient.Client, o.Authorizers.ResourceManager)

raiPoliciesClient, err := raipolicies.NewRaiPoliciesClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building Rai Policies client: %+v", err)
}
o.Configure(raiPoliciesClient.Client, o.Authorizers.ResourceManager)

raiBlobklistsClient, err := raiblocklists.NewRaiBlocklistsClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building Rai Blocklists client: %+v", err)
Expand All @@ -41,5 +49,6 @@ func NewClient(o *common.ClientOptions) (*Client, error) {
AccountsClient: accountsClient,
DeploymentsClient: deploymentsClient,
RaiBlocklistsClient: raiBlobklistsClient,
RaiPoliciesClient: raiPoliciesClient,
}, nil
}
338 changes: 338 additions & 0 deletions internal/services/cognitive/cognitive_account_rai_policy_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
package cognitive

import (
"context"
"fmt"
"time"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-sdk/resource-manager/cognitive/2024-10-01/raipolicies"
"github.com/hashicorp/terraform-provider-azurerm/internal/locks"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
)

var _ sdk.ResourceWithUpdate = &CognitiveAccountRaiPolicyResource{}

type CognitiveAccountRaiPolicyResource struct{}

type AccountRaiPolicyContentFilter struct {
Name string `tfschema:"name"`
FilterEnabled bool `tfschema:"filter_enabled"`
BlockEnabled bool `tfschema:"block_enabled"`
SeverityThreshold string `tfschema:"severity_threshold"`
Source string `tfschema:"source"`
}

type AccountRaiPolicyCustomBlock struct {
Id string `tfschema:"rai_blocklist_id"`
BlockEnabled bool `tfschema:"block_enabled"`
Source string `tfschema:"source"`
}

type AccountRaiPolicyResourceModel struct {
Name string `tfschema:"name"`
AccountId string `tfschema:"cognitive_account_id"`
BasePolicyName string `tfschema:"base_policy_name"`
ContentFilter []AccountRaiPolicyContentFilter `tfschema:"content_filter"`
Mode string `tfschema:"mode"`
Tags map[string]string `tfschema:"tags"`
}

func (r CognitiveAccountRaiPolicyResource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"cognitive_account_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: raipolicies.ValidateAccountID,
},

"base_policy_name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"content_filter": {
Type: pluginsdk.TypeList,
Required: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
},
"filter_enabled": {
Type: pluginsdk.TypeBool,
Required: true,
},
"block_enabled": {
Type: pluginsdk.TypeBool,
Required: true,
},
"severity_threshold": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(raipolicies.PossibleValuesForContentLevel(), false),
},
"source": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(raipolicies.PossibleValuesForRaiPolicyContentSource(), false),
},
},
},
},

"mode": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice(raipolicies.PossibleValuesForRaiPolicyMode(), false),
},

"tags": commonschema.Tags(),
}
}

func (r CognitiveAccountRaiPolicyResource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{}
}

func (r CognitiveAccountRaiPolicyResource) ModelObject() interface{} {
return &AccountRaiPolicyResourceModel{}
}

func (r CognitiveAccountRaiPolicyResource) ResourceType() string {
return "azurerm_cognitive_account_rai_policy"
}

func (r CognitiveAccountRaiPolicyResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Cognitive.RaiPoliciesClient
subscriptionId := metadata.Client.Account.SubscriptionId

var model AccountRaiPolicyResourceModel
if err := metadata.Decode(&model); err != nil {
return fmt.Errorf("decoding: %+v", err)
}

cognitiveAccountId, err := raipolicies.ParseAccountID(model.AccountId)
if err != nil {
return err
}

id := raipolicies.NewRaiPolicyID(subscriptionId, cognitiveAccountId.ResourceGroupName, cognitiveAccountId.AccountName, model.Name)
existing, err := client.Get(ctx, id)
if err != nil {
if !response.WasNotFound(existing.HttpResponse) {
return fmt.Errorf("checking for presence of existing %s: %+v", id, err)
}
}
if !response.WasNotFound(existing.HttpResponse) {
return metadata.ResourceRequiresImport(r.ResourceType(), id)
}

locks.ByID(cognitiveAccountId.ID())
defer locks.UnlockByID(cognitiveAccountId.ID())

raiPolicy := raipolicies.RaiPolicy{
Name: pointer.To(model.Name),
Properties: &raipolicies.RaiPolicyProperties{
BasePolicyName: pointer.To(model.BasePolicyName),
ContentFilters: expandRaiPolicyContentFilters(model.ContentFilter),
},
Tags: pointer.To(model.Tags),
}

if model.Mode != "" {
raiPolicy.Properties.Mode = pointer.To(raipolicies.RaiPolicyMode(model.Mode))
}

if _, err := client.CreateOrUpdate(ctx, id, raiPolicy); err != nil {
return fmt.Errorf("creating %s: %+v", id, err)
}

metadata.SetID(id)

return nil
},
}
}

func (r CognitiveAccountRaiPolicyResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Cognitive.RaiPoliciesClient

id, err := raipolicies.ParseRaiPolicyID(metadata.ResourceData.Id())
if err != nil {
return err
}

cognitiveAccountId := raipolicies.NewAccountID(id.SubscriptionId, id.ResourceGroupName, id.AccountName)

resp, err := client.Get(ctx, *id)
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return metadata.MarkAsGone(id)
}
return fmt.Errorf("retrieving %s: %+v", id, err)
}

state := AccountRaiPolicyResourceModel{
Name: id.RaiPolicyName,
AccountId: cognitiveAccountId.ID(),
}

if model := resp.Model; model != nil {
state.Tags = pointer.From(model.Tags)

if props := model.Properties; props != nil {
state.BasePolicyName = pointer.From(props.BasePolicyName)
state.ContentFilter = flattenRaiPolicyContentFilters(props.ContentFilters)
state.Mode = string(pointer.From(props.Mode))
}
}

return metadata.Encode(&state)
},
}
}

func (r CognitiveAccountRaiPolicyResource) Update() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Cognitive.RaiPoliciesClient

id, err := raipolicies.ParseRaiPolicyID(metadata.ResourceData.Id())
if err != nil {
return err
}

var model AccountRaiPolicyResourceModel
if err := metadata.Decode(&model); err != nil {
return err
}

existing, err := client.Get(ctx, *id)
if err != nil {
return fmt.Errorf("retrieving %s: %+v", *id, err)
}

if existing.Model == nil {
return fmt.Errorf("retrieving %s: `model` was nil", id)
}

if existing.Model.Properties == nil {
return fmt.Errorf("retrieving %s: `properties` was nil", id)
}

cognitiveAccountId := raipolicies.NewAccountID(id.SubscriptionId, id.ResourceGroupName, id.AccountName)

locks.ByID(cognitiveAccountId.ID())
defer locks.UnlockByID(cognitiveAccountId.ID())

payload := existing.Model

if metadata.ResourceData.HasChange("content_filter") {
payload.Properties.ContentFilters = expandRaiPolicyContentFilters(model.ContentFilter)
}

if metadata.ResourceData.HasChange("mode") {
payload.Properties.Mode = pointer.To(raipolicies.RaiPolicyMode(model.Mode))
}

if metadata.ResourceData.HasChange("tags") {
payload.Tags = pointer.To(model.Tags)
}

if _, err := client.CreateOrUpdate(ctx, *id, *payload); err != nil {
return fmt.Errorf("updating %s: %+v", id, err)
}

return nil
},
}
}

func (r CognitiveAccountRaiPolicyResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Cognitive.RaiPoliciesClient

id, err := raipolicies.ParseRaiPolicyID(metadata.ResourceData.Id())
if err != nil {
return err
}

cognitiveAccountId := raipolicies.NewAccountID(id.SubscriptionId, id.ResourceGroupName, id.AccountName)

locks.ByID(cognitiveAccountId.ID())
defer locks.UnlockByID(cognitiveAccountId.ID())

if err := client.DeleteThenPoll(ctx, *id); err != nil {
return fmt.Errorf("deleting %s: %+v", id, err)
}

return nil
},
}
}

func (r CognitiveAccountRaiPolicyResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return raipolicies.ValidateRaiPolicyID
}

func expandRaiPolicyContentFilters(filters []AccountRaiPolicyContentFilter) *[]raipolicies.RaiPolicyContentFilter {
if filters == nil {
return nil
}

contentFilters := make([]raipolicies.RaiPolicyContentFilter, 0, len(filters))
for _, filter := range filters {
contentFilters = append(contentFilters, raipolicies.RaiPolicyContentFilter{
Name: pointer.To(filter.Name),
Enabled: pointer.To(filter.FilterEnabled),
Blocking: pointer.To(filter.BlockEnabled),
SeverityThreshold: pointer.To(raipolicies.ContentLevel(filter.SeverityThreshold)),
Source: pointer.To(raipolicies.RaiPolicyContentSource(filter.Source)),
})
}
return &contentFilters
}

func flattenRaiPolicyContentFilters(filters *[]raipolicies.RaiPolicyContentFilter) []AccountRaiPolicyContentFilter {
contentFilters := make([]AccountRaiPolicyContentFilter, 0)
if filters == nil {
return contentFilters
}

for _, filter := range *filters {
contentFilters = append(contentFilters, AccountRaiPolicyContentFilter{
Name: pointer.From(filter.Name),
FilterEnabled: pointer.From(filter.Enabled),
BlockEnabled: pointer.From(filter.Blocking),
SeverityThreshold: string(pointer.From(filter.SeverityThreshold)),
Source: string(pointer.From(filter.Source)),
})
}
return contentFilters
}
Loading
Loading