Skip to content

Commit 030d446

Browse files
authored
Added search by display_name to databricks_service_principal data source (#2963)
* Added search by `display_name` to `databricks_service_principal` data source Allow to search not only by `application_id`, but also by the exact `display_name`. This fixes #2959 * Address review comments * More review comments
1 parent 191c028 commit 030d446

File tree

3 files changed

+99
-10
lines changed

3 files changed

+99
-10
lines changed

docs/data-sources/service_principal.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ resource "databricks_group_member" "my_member_a" {
2929

3030
## Argument Reference
3131

32-
Data source allows you to pick service principals by the following attributes
32+
Data source allows you to pick service principals by one of the following attributes (only one of them):
3333

34-
- `application_id` - (Required) ID of the service principal. The service principal must exist before this resource can be retrieved.
34+
- `application_id` - (Required if `display_name` isn't used) ID of the service principal. The service principal must exist before this resource can be retrieved.
35+
- `display_name` - (Required if `application_id` isn't used) Exact display name of the service principal. The service principal must exist before this resource can be retrieved. In case if there are several service principals with the same name, an error is thrown.
3536

3637
## Attribute Reference
3738

scim/data_service_principal.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,34 @@ func DataSourceServicePrincipal() *schema.Resource {
2424
return common.DataResource(spnData{}, func(ctx context.Context, e any, c *common.DatabricksClient) error {
2525
response := e.(*spnData)
2626
spnAPI := NewServicePrincipalsAPI(ctx, c)
27-
spList, err := spnAPI.Filter(fmt.Sprintf("applicationId eq '%s'", response.ApplicationID), true)
27+
var spList []User
28+
var err error
29+
if response.ApplicationID != "" && response.DisplayName != "" {
30+
return fmt.Errorf("please specify only one of application_id or display_name")
31+
}
32+
if response.ApplicationID != "" {
33+
spList, err = spnAPI.Filter(fmt.Sprintf("applicationId eq '%s'", response.ApplicationID), true)
34+
} else if response.DisplayName != "" {
35+
spList, err = spnAPI.Filter(fmt.Sprintf("displayName eq '%s'", response.DisplayName), true)
36+
} else {
37+
return fmt.Errorf("please specify either application_id or display_name")
38+
}
2839
if err != nil {
2940
return err
3041
}
3142
if len(spList) == 0 {
32-
return fmt.Errorf("cannot find SP with ID %s", response.ApplicationID)
43+
if response.ApplicationID != "" {
44+
return fmt.Errorf("cannot find SP with ID %s", response.ApplicationID)
45+
} else {
46+
return fmt.Errorf("cannot find SP with name %s", response.DisplayName)
47+
}
48+
} else if len(spList) > 1 {
49+
return fmt.Errorf("there are more than 1 service principal with name %s", response.DisplayName)
3350
}
51+
3452
sp := spList[0]
3553
response.DisplayName = sp.DisplayName
54+
response.ApplicationID = sp.ApplicationID
3655
response.Home = fmt.Sprintf("/Users/%s", sp.ApplicationID)
3756
response.Repos = fmt.Sprintf("/Repos/%s", sp.ApplicationID)
3857
response.AclPrincipalID = fmt.Sprintf("servicePrincipals/%s", sp.ApplicationID)

scim/data_service_principal_test.go

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"testing"
55

66
"github.com/databricks/terraform-provider-databricks/qa"
7-
"github.com/stretchr/testify/require"
7+
"github.com/stretchr/testify/assert"
88
)
99

1010
func TestDataServicePrincipalReadByAppId(t *testing.T) {
@@ -52,8 +52,8 @@ func TestDataServicePrincipalReadByAppId(t *testing.T) {
5252
})
5353
}
5454

55-
func TestDataServicePrincipalReadNotFound(t *testing.T) {
56-
_, err := qa.ResourceFixture{
55+
func TestDataServicePrincipalReadByIdNotFound(t *testing.T) {
56+
qa.ResourceFixture{
5757
Fixtures: []qa.HTTPFixture{
5858
{
5959
Method: "GET",
@@ -66,8 +66,24 @@ func TestDataServicePrincipalReadNotFound(t *testing.T) {
6666
Read: true,
6767
NonWritable: true,
6868
ID: "_",
69-
}.Apply(t)
70-
require.Error(t, err)
69+
}.ExpectError(t, "cannot find SP with ID abc")
70+
}
71+
72+
func TestDataServicePrincipalReadByNameNotFound(t *testing.T) {
73+
qa.ResourceFixture{
74+
Fixtures: []qa.HTTPFixture{
75+
{
76+
Method: "GET",
77+
Resource: "/api/2.0/preview/scim/v2/ServicePrincipals?excludedAttributes=roles&filter=displayName%20eq%20%27abc%27",
78+
Response: UserList{},
79+
},
80+
},
81+
Resource: DataSourceServicePrincipal(),
82+
HCL: `display_name = "abc"`,
83+
Read: true,
84+
NonWritable: true,
85+
ID: "_",
86+
}.ExpectError(t, "cannot find SP with name abc")
7187
}
7288

7389
func TestDataServicePrincipalReadError(t *testing.T) {
@@ -85,5 +101,58 @@ func TestDataServicePrincipalReadError(t *testing.T) {
85101
NonWritable: true,
86102
ID: "_",
87103
}.Apply(t)
88-
require.Error(t, err)
104+
assert.ErrorContains(t, err, "unexpected error handling request: unexpected end of JSON input")
105+
}
106+
107+
func TestDataServicePrincipalReadByNameDuplicates(t *testing.T) {
108+
qa.ResourceFixture{
109+
Fixtures: []qa.HTTPFixture{
110+
{
111+
Method: "GET",
112+
Resource: "/api/2.0/preview/scim/v2/ServicePrincipals?excludedAttributes=roles&filter=displayName%20eq%20%27abc%27",
113+
Response: UserList{
114+
Resources: []User{
115+
{
116+
ID: "abc1",
117+
DisplayName: "abc",
118+
Active: true,
119+
ApplicationID: "abc1",
120+
},
121+
{
122+
ID: "abc2",
123+
DisplayName: "abc",
124+
Active: true,
125+
ApplicationID: "abc2",
126+
},
127+
},
128+
},
129+
},
130+
},
131+
Resource: DataSourceServicePrincipal(),
132+
HCL: `display_name = "abc"`,
133+
Read: true,
134+
NonWritable: true,
135+
ID: "abc",
136+
}.ExpectError(t, "there are more than 1 service principal with name abc")
137+
}
138+
139+
func TestDataServicePrincipalReadNoParams(t *testing.T) {
140+
qa.ResourceFixture{
141+
Resource: DataSourceServicePrincipal(),
142+
HCL: ``,
143+
Read: true,
144+
NonWritable: true,
145+
ID: "_",
146+
}.ExpectError(t, "please specify either application_id or display_name")
147+
}
148+
149+
func TestDataServicePrincipalReadBothParams(t *testing.T) {
150+
qa.ResourceFixture{
151+
Resource: DataSourceServicePrincipal(),
152+
HCL: `display_name = "abc"
153+
application_id = "abc"`,
154+
Read: true,
155+
NonWritable: true,
156+
ID: "_",
157+
}.ExpectError(t, "please specify only one of application_id or display_name")
89158
}

0 commit comments

Comments
 (0)