diff --git a/go.mod b/go.mod index ba4f68e99d..e19cbce92c 100644 --- a/go.mod +++ b/go.mod @@ -77,3 +77,5 @@ require ( ) go 1.17 + +replace github.com/manicminer/hamilton => github.com/o11n/hamilton v0.40.2-0.20220217143703-8395e584f1c4 diff --git a/go.sum b/go.sum index c2ca6a4d5f..06994aeb62 100644 --- a/go.sum +++ b/go.sum @@ -299,8 +299,6 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/manicminer/hamilton v0.41.1 h1:b9XVMIo2tnHBtl7sFTmake2BddbqriW2zdPKWmrxZsc= -github.com/manicminer/hamilton v0.41.1/go.mod h1:IOYn2Dc9SUiZ7Ryw6c8Ay795vPPMnrCZe3MktS447dc= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -338,6 +336,8 @@ github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758= github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs= +github.com/o11n/hamilton v0.40.2-0.20220217143703-8395e584f1c4 h1:ai0wzt2ne+aHdBZ6ibAKODjMf6FTwgbvpxf2sp4kW3c= +github.com/o11n/hamilton v0.40.2-0.20220217143703-8395e584f1c4/go.mod h1:IOYn2Dc9SUiZ7Ryw6c8Ay795vPPMnrCZe3MktS447dc= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= diff --git a/internal/services/serviceprincipals/service_principal_resource.go b/internal/services/serviceprincipals/service_principal_resource.go index ed3f8f9808..00dfe11321 100644 --- a/internal/services/serviceprincipals/service_principal_resource.go +++ b/internal/services/serviceprincipals/service_principal_resource.go @@ -317,6 +317,19 @@ func servicePrincipalResource() *schema.Resource { }, }, + "token_signing_certificate_name": { + Description: "", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "preferred_token_signing_key_thumbprint": { + Description: "", + Type: schema.TypeString, + Computed: true, + }, + "sign_in_audience": { Description: "The Microsoft account types that are supported for the associated application", Type: schema.TypeString, @@ -502,6 +515,19 @@ func servicePrincipalResourceCreate(ctx context.Context, d *schema.ResourceData, } } + if v, ok := d.GetOk("token_signing_certificate_name"); ok { + key, _, err := client.AddTokenSigningCertificate(ctx, d.Id(), msgraph.KeyCredential{ + DisplayName: utils.String(v.(string)), + }) + if err != nil { + return tf.ErrorDiagF(err, "Could not add token signing certificate to service principal with object ID: %q", d.Id()) + } + + if _, err = client.SetPreferredTokenSigningKeyThumbprint(ctx, d.Id(), *key.Thumbprint); err != nil { + return tf.ErrorDiagF(err, "Could not set preferred token signing key thumbprint for service principal with object ID: %q", d.Id()) + } + } + return servicePrincipalResourceRead(ctx, d, meta) } @@ -618,6 +644,7 @@ func servicePrincipalResourceRead(ctx context.Context, d *schema.ResourceData, m tf.Set(d, "oauth2_permission_scope_ids", helpers.ApplicationFlattenOAuth2PermissionScopeIDs(servicePrincipal.PublishedPermissionScopes)) tf.Set(d, "oauth2_permission_scopes", helpers.ApplicationFlattenOAuth2PermissionScopes(servicePrincipal.PublishedPermissionScopes)) tf.Set(d, "object_id", servicePrincipal.ID) + tf.Set(d, "preferred_token_signing_key_thumbprint", servicePrincipal.PreferredTokenSigningKeyThumbprint) tf.Set(d, "preferred_single_sign_on_mode", servicePrincipal.PreferredSingleSignOnMode) tf.Set(d, "redirect_uris", tf.FlattenStringSlicePtr(servicePrincipal.ReplyUrls)) tf.Set(d, "saml_metadata_url", servicePrincipal.SamlMetadataUrl) diff --git a/internal/services/serviceprincipals/service_principal_resource_test.go b/internal/services/serviceprincipals/service_principal_resource_test.go index c99f0921ac..7663cb2696 100644 --- a/internal/services/serviceprincipals/service_principal_resource_test.go +++ b/internal/services/serviceprincipals/service_principal_resource_test.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "os" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -57,6 +58,7 @@ func TestAccServicePrincipal_complete(t *testing.T) { check.That(data.ResourceName).Key("redirect_uris.#").HasValue("2"), check.That(data.ResourceName).Key("sign_in_audience").HasValue("AzureADMyOrg"), check.That(data.ResourceName).Key("type").HasValue("Application"), + check.That(data.ResourceName).Key("preferred_token_signing_key_thumbprint").MatchesRegex(regexp.MustCompile("^[A-Z0-9]{40}$")), ), }, data.ImportStep("use_existing"), @@ -415,13 +417,14 @@ func (r ServicePrincipalResource) complete(data acceptance.TestData) string { resource "azuread_service_principal" "test" { application_id = azuread_application.test.application_id - account_enabled = false - alternative_names = ["foo", "bar"] - app_role_assignment_required = true - description = "An internal app for testing" - login_url = "https://test-%[2]d.internal/login" - notes = "Just testing something" - preferred_single_sign_on_mode = "saml" + account_enabled = false + alternative_names = ["foo", "bar"] + app_role_assignment_required = true + description = "An internal app for testing" + login_url = "https://test-%[2]d.internal/login" + notes = "Just testing something" + preferred_single_sign_on_mode = "saml" + token_signing_certificate_name = "testcert" notification_email_addresses = [ "alerts.internal@hashitown.net", diff --git a/vendor/github.com/manicminer/hamilton/msgraph/models.go b/vendor/github.com/manicminer/hamilton/msgraph/models.go index 95d83333e9..57a5cf866c 100644 --- a/vendor/github.com/manicminer/hamilton/msgraph/models.go +++ b/vendor/github.com/manicminer/hamilton/msgraph/models.go @@ -1044,6 +1044,7 @@ type KeyCredential struct { EndDateTime *time.Time `json:"endDateTime,omitempty"` KeyId *string `json:"keyId,omitempty"` StartDateTime *time.Time `json:"startDateTime,omitempty"` + Thumbprint *string `json:"thumbprint,omitempty"` Type KeyCredentialType `json:"type"` Usage KeyCredentialUsage `json:"usage"` Key *string `json:"key,omitempty"` @@ -1274,6 +1275,7 @@ type ServicePrincipal struct { PasswordCredentials *[]PasswordCredential `json:"passwordCredentials,omitempty"` PasswordSingleSignOnSettings *PasswordSingleSignOnSettings `json:"passwordSingleSignOnSettings,omitempty"` PreferredSingleSignOnMode *PreferredSingleSignOnMode `json:"preferredSingleSignOnMode,omitempty"` + PreferredTokenSigningKeyThumbprint *string `json:"preferredTokenSigningKeyThumbprint,omitempty"` PreferredTokenSigningKeyEndDateTime *time.Time `json:"preferredTokenSigningKeyEndDateTime,omitempty"` PublishedPermissionScopes *[]PermissionScope `json:"publishedPermissionScopes,omitempty"` ReplyUrls *[]string `json:"replyUrls,omitempty"` diff --git a/vendor/github.com/manicminer/hamilton/msgraph/serviceprincipals.go b/vendor/github.com/manicminer/hamilton/msgraph/serviceprincipals.go index 45fc367e64..d90abfc19b 100644 --- a/vendor/github.com/manicminer/hamilton/msgraph/serviceprincipals.go +++ b/vendor/github.com/manicminer/hamilton/msgraph/serviceprincipals.go @@ -449,6 +449,75 @@ func (c *ServicePrincipalsClient) RemovePassword(ctx context.Context, servicePri return status, nil } +// AddTokenSigningCertificate appends a new self signed certificate (keys and password) to a Service Principal. +func (c *ServicePrincipalsClient) AddTokenSigningCertificate(ctx context.Context, servicePrincipalId string, keyCredential KeyCredential) (*KeyCredential, int, error) { + var status int + + body, err := json.Marshal(struct { + KeyCredential KeyCredential `json:"keyCredential"` + }{ + KeyCredential: keyCredential, + }) + if err != nil { + return nil, status, fmt.Errorf("json.Marshal(): %v", err) + } + + resp, status, _, err := c.BaseClient.Post(ctx, PostHttpRequestInput{ + Body: body, + ConsistencyFailureFunc: RetryOn404ConsistencyFailureFunc, + ValidStatusCodes: []int{http.StatusOK, http.StatusCreated}, + Uri: Uri{ + Entity: fmt.Sprintf("/servicePrincipals/%s/addTokenSigningCertificate", servicePrincipalId), + HasTenantId: true, + }, + }) + if err != nil { + return nil, status, fmt.Errorf("ServicePrincipalsClient.BaseClient.Post(): %v", err) + } + + defer resp.Body.Close() + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, status, fmt.Errorf("io.ReadAll(): %v", err) + } + + var newKeyCredential KeyCredential + if err := json.Unmarshal(respBody, &newKeyCredential); err != nil { + return nil, status, fmt.Errorf("json.Unmarshal(): %v", err) + } + + return &newKeyCredential, status, nil +} + +// SetPreferredTokenSigningKeyThumbprint sets the field preferredTokenSigningKeyThumbprint for a Service Principal. +func (c *ServicePrincipalsClient) SetPreferredTokenSigningKeyThumbprint(ctx context.Context, servicePrincipalId string, thumbprint string) (int, error) { + var status int + + body, err := json.Marshal(struct { + Thumbprint string `json:"preferredTokenSigningKeyThumbprint"` + }{ + Thumbprint: thumbprint, + }) + if err != nil { + return status, fmt.Errorf("json.Marshal(): %v", err) + } + + _, status, _, err = c.BaseClient.Patch(ctx, PatchHttpRequestInput{ + Body: body, + ConsistencyFailureFunc: RetryOn404ConsistencyFailureFunc, + ValidStatusCodes: []int{http.StatusNoContent}, + Uri: Uri{ + Entity: fmt.Sprintf("/servicePrincipals/%s", servicePrincipalId), + HasTenantId: true, + }, + }) + if err != nil { + return status, fmt.Errorf("ServicePrincipalsClient.BaseClient.Patch(): %v", err) + } + + return status, nil +} + // ListOwnedObjects retrieves the owned objects of the specified Service Principal. // id is the object ID of the service principal. func (c *ServicePrincipalsClient) ListOwnedObjects(ctx context.Context, id string) (*[]string, int, error) { diff --git a/vendor/modules.txt b/vendor/modules.txt index 3748d69d79..90e61a73d8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -229,7 +229,7 @@ github.com/klauspost/compress/fse github.com/klauspost/compress/huff0 github.com/klauspost/compress/zstd github.com/klauspost/compress/zstd/internal/xxhash -# github.com/manicminer/hamilton v0.41.1 +# github.com/manicminer/hamilton v0.41.1 => github.com/o11n/hamilton v0.40.2-0.20220217143703-8395e584f1c4 ## explicit; go 1.16 github.com/manicminer/hamilton/auth github.com/manicminer/hamilton/environments @@ -507,3 +507,4 @@ google.golang.org/protobuf/types/known/durationpb google.golang.org/protobuf/types/known/emptypb google.golang.org/protobuf/types/known/timestamppb google.golang.org/protobuf/types/pluginpb +# github.com/manicminer/hamilton => github.com/o11n/hamilton v0.40.2-0.20220217143703-8395e584f1c4