Skip to content

Commit 8eb9172

Browse files
wuxu92teowa
authored andcommitted
azurerm_ai_foundry_project: add support for primary_user_assigned_identity (hashicorp#29197)
* add primary_user_assigned_identity to ai foundry project * primary user identity should be in identity block * update acc test format * code/doc clean * terrafmt
1 parent 2dc84a2 commit 8eb9172

File tree

3 files changed

+152
-10
lines changed

3 files changed

+152
-10
lines changed

internal/services/machinelearning/ai_foundry_project_resource.go

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/hashicorp/go-azure-helpers/lang/pointer"
1414
"github.com/hashicorp/go-azure-helpers/lang/response"
15+
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
1516
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
1617
"github.com/hashicorp/go-azure-helpers/resourcemanager/identity"
1718
"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
@@ -26,15 +27,16 @@ import (
2627
type AIFoundryProject struct{}
2728

2829
type AIFoundryProjectModel struct {
29-
Name string `tfschema:"name"`
30-
Location string `tfschema:"location"`
31-
AIServicesHubId string `tfschema:"ai_services_hub_id"`
32-
Identity []identity.ModelSystemAssignedUserAssigned `tfschema:"identity"`
33-
HighBusinessImpactEnabled bool `tfschema:"high_business_impact_enabled"`
34-
Description string `tfschema:"description"`
35-
FriendlyName string `tfschema:"friendly_name"`
36-
ProjectId string `tfschema:"project_id"`
37-
Tags map[string]interface{} `tfschema:"tags"`
30+
Name string `tfschema:"name"`
31+
Location string `tfschema:"location"`
32+
AIServicesHubId string `tfschema:"ai_services_hub_id"`
33+
Identity []identity.ModelSystemAssignedUserAssigned `tfschema:"identity"`
34+
HighBusinessImpactEnabled bool `tfschema:"high_business_impact_enabled"`
35+
Description string `tfschema:"description"`
36+
PrimaryUserAssignedIdentity string `tfschema:"primary_user_assigned_identity"`
37+
FriendlyName string `tfschema:"friendly_name"`
38+
ProjectId string `tfschema:"project_id"`
39+
Tags map[string]interface{} `tfschema:"tags"`
3840
}
3941

4042
func (r AIFoundryProject) ModelObject() interface{} {
@@ -105,6 +107,13 @@ func (r AIFoundryProject) Arguments() map[string]*pluginsdk.Schema {
105107
ForceNew: true,
106108
},
107109

110+
"primary_user_assigned_identity": {
111+
Type: pluginsdk.TypeString,
112+
Optional: true,
113+
ValidateFunc: commonids.ValidateUserAssignedIdentityID,
114+
RequiredWith: []string{"identity"},
115+
},
116+
108117
"description": {
109118
Type: pluginsdk.TypeString,
110119
Optional: true,
@@ -177,6 +186,14 @@ func (r AIFoundryProject) Create() sdk.ResourceFunc {
177186
payload.Identity = expandedIdentity
178187
}
179188

189+
if model.PrimaryUserAssignedIdentity != "" {
190+
userAssignedId, err := commonids.ParseUserAssignedIdentityID(model.PrimaryUserAssignedIdentity)
191+
if err != nil {
192+
return err
193+
}
194+
payload.Properties.PrimaryUserAssignedIdentity = pointer.To(userAssignedId.ID())
195+
}
196+
180197
if model.Description != "" {
181198
payload.Properties.Description = pointer.To(model.Description)
182199
}
@@ -242,6 +259,14 @@ func (r AIFoundryProject) Update() sdk.ResourceFunc {
242259
payload.Properties.Description = pointer.To(state.Description)
243260
}
244261

262+
if metadata.ResourceData.HasChange("primary_user_assigned_identity") {
263+
userAssignedId, err := commonids.ParseUserAssignedIdentityID(state.PrimaryUserAssignedIdentity)
264+
if err != nil {
265+
return err
266+
}
267+
payload.Properties.PrimaryUserAssignedIdentity = pointer.To(userAssignedId.ID())
268+
}
269+
245270
if metadata.ResourceData.HasChange("friendly_name") {
246271
payload.Properties.FriendlyName = pointer.To(state.FriendlyName)
247272
}
@@ -313,6 +338,7 @@ func (r AIFoundryProject) Read() sdk.ResourceFunc {
313338
hub.FriendlyName = pointer.From(props.FriendlyName)
314339
hub.HighBusinessImpactEnabled = pointer.From(props.HbiWorkspace)
315340
hub.ProjectId = pointer.From(props.WorkspaceId)
341+
hub.PrimaryUserAssignedIdentity = pointer.From(props.PrimaryUserAssignedIdentity)
316342
}
317343
}
318344

internal/services/machinelearning/ai_foundry_project_resource_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,28 @@ func TestAccAIFoundryProject_basic(t *testing.T) {
3333
})
3434
}
3535

36+
func TestAccAIFoundryProject_userIdentity(t *testing.T) {
37+
data := acceptance.BuildTestData(t, "azurerm_ai_foundry_project", "test")
38+
r := AIFoundryProject{}
39+
40+
data.ResourceTest(t, r, []acceptance.TestStep{
41+
{
42+
Config: r.userIdentity(data),
43+
Check: acceptance.ComposeTestCheckFunc(
44+
check.That(data.ResourceName).ExistsInAzure(r),
45+
),
46+
},
47+
data.ImportStep(),
48+
{
49+
Config: r.userIdentityUpdate(data),
50+
Check: acceptance.ComposeTestCheckFunc(
51+
check.That(data.ResourceName).ExistsInAzure(r),
52+
),
53+
},
54+
data.ImportStep(),
55+
})
56+
}
57+
3658
func TestAccAIFoundryProject_requiresImport(t *testing.T) {
3759
data := acceptance.BuildTestData(t, "azurerm_ai_foundry_project", "test")
3860
r := AIFoundryProject{}
@@ -115,6 +137,98 @@ resource "azurerm_ai_foundry_project" "test" {
115137
`, AIFoundry{}.basic(data), data.RandomInteger)
116138
}
117139

140+
func (r AIFoundryProject) userIdentityTemplate(data acceptance.TestData) string {
141+
return fmt.Sprintf(`
142+
%s
143+
144+
resource "azurerm_user_assigned_identity" "test" {
145+
location = azurerm_resource_group.test.location
146+
name = "acctestuai-%[2]d"
147+
resource_group_name = azurerm_resource_group.test.name
148+
}
149+
150+
resource "azurerm_role_assignment" "test" {
151+
scope = azurerm_resource_group.test.id
152+
role_definition_name = "Reader"
153+
principal_id = azurerm_user_assigned_identity.test.principal_id
154+
}
155+
156+
resource "azurerm_key_vault_access_policy" "test2" {
157+
key_vault_id = azurerm_key_vault.test.id
158+
tenant_id = azurerm_user_assigned_identity.test.tenant_id
159+
object_id = azurerm_user_assigned_identity.test.client_id
160+
161+
key_permissions = [
162+
"Create",
163+
"Get",
164+
]
165+
}
166+
`, AIFoundry{}.basic(data), data.RandomInteger)
167+
}
168+
169+
func (r AIFoundryProject) userIdentity(data acceptance.TestData) string {
170+
return fmt.Sprintf(`
171+
%s
172+
173+
resource "azurerm_ai_foundry_project" "test" {
174+
name = "acctestaip-%[2]d"
175+
location = azurerm_ai_foundry.test.location
176+
ai_services_hub_id = azurerm_ai_foundry.test.id
177+
primary_user_assigned_identity = azurerm_user_assigned_identity.test.id
178+
179+
identity {
180+
type = "UserAssigned"
181+
identity_ids = [azurerm_user_assigned_identity.test.id]
182+
}
183+
184+
depends_on = [azurerm_key_vault_access_policy.test2, azurerm_role_assignment.test]
185+
}
186+
`, r.userIdentityTemplate(data), data.RandomInteger)
187+
}
188+
189+
func (r AIFoundryProject) userIdentityUpdate(data acceptance.TestData) string {
190+
return fmt.Sprintf(`
191+
%s
192+
193+
resource "azurerm_user_assigned_identity" "test2" {
194+
location = azurerm_resource_group.test.location
195+
name = "acctestuai2-%[2]d"
196+
resource_group_name = azurerm_resource_group.test.name
197+
}
198+
199+
resource "azurerm_role_assignment" "test2" {
200+
scope = azurerm_resource_group.test.id
201+
role_definition_name = "Reader"
202+
principal_id = azurerm_user_assigned_identity.test2.principal_id
203+
}
204+
205+
resource "azurerm_key_vault_access_policy" "test3" {
206+
key_vault_id = azurerm_key_vault.test.id
207+
tenant_id = azurerm_user_assigned_identity.test2.tenant_id
208+
object_id = azurerm_user_assigned_identity.test2.client_id
209+
210+
key_permissions = [
211+
"Create",
212+
"Get",
213+
]
214+
}
215+
216+
resource "azurerm_ai_foundry_project" "test" {
217+
name = "acctestaip-%[2]d"
218+
location = azurerm_ai_foundry.test.location
219+
ai_services_hub_id = azurerm_ai_foundry.test.id
220+
primary_user_assigned_identity = azurerm_user_assigned_identity.test2.id
221+
222+
identity {
223+
type = "UserAssigned"
224+
identity_ids = [azurerm_user_assigned_identity.test.id, azurerm_user_assigned_identity.test2.id]
225+
}
226+
227+
depends_on = [azurerm_key_vault_access_policy.test2, azurerm_role_assignment.test2]
228+
}
229+
`, r.userIdentityTemplate(data), data.RandomInteger)
230+
}
231+
118232
func (r AIFoundryProject) complete(data acceptance.TestData) string {
119233
return fmt.Sprintf(`
120234
%s

website/docs/r/ai_foundry_project.html.markdown

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ resource "azurerm_key_vault" "example" {
3030
purge_protection_enabled = true
3131
}
3232
33-
resource "azurerm_key_vault_access_policy" "test" {
33+
resource "azurerm_key_vault_access_policy" "example" {
3434
key_vault_id = azurerm_key_vault.example.id
3535
tenant_id = data.azurerm_client_config.current.tenant_id
3636
object_id = data.azurerm_client_config.current.object_id
@@ -92,6 +92,8 @@ The following arguments are supported:
9292

9393
* `description` - (Optional) The description of this AI Foundry Project.
9494

95+
* `primary_user_assigned_identity` - (Optional) The user assigned identity ID that represents the AI Foundry Hub identity. This must be set when enabling encryption with a user assigned identity.
96+
9597
* `friendly_name` - (Optional) The display name of this AI Foundry Project.
9698

9799
* `high_business_impact_enabled` - (Optional) Whether High Business Impact (HBI) should be enabled or not. Enabling this setting will reduce diagnostic data collected by the service. Changing this forces a new AI Foundry Project to be created. Defaults to `false`.

0 commit comments

Comments
 (0)