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

Add token signing certificate resource #968

Merged
merged 3 commits into from
Jan 18, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions docs/resources/service_principal_token_signing_certificate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
subcategory: "Service Principals"
---

# Resource: azuread_service_principal_token_signing_certificate

Manages a token signing certificate associated with a service principal within Azure Active Directory.

## API Permissions

The following API permissions are required in order to use this resource.

When authenticated with a service principal, this resource requires one of the following application roles: `Application.ReadWrite.All` or `Directory.ReadWrite.All`

When authenticated with a user principal, this resource requires one of the following directory roles: `Application Administrator` or `Global Administrator`

## Example Usage

*Using default settings*

```terraform
resource "azuread_application" "example" {
display_name = "example"
}

resource "azuread_service_principal" "example" {
application_id = azuread_application.example.application_id
}

resource "azuread_service_principal_token_signing_certificate" "example" {
service_principal_id = azuread_service_principal.example.id
}
```

*Using custom settings*

```terraform
resource "azuread_application" "example" {
display_name = "example"
}

resource "azuread_service_principal" "example" {
application_id = azuread_application.example.application_id
}

resource "azuread_service_principal_token_signing_certificate" "example" {
service_principal_id = azuread_service_principal.example.id
display_name = "CN=example.com"
end_date = "2023-05-01T01:02:03Z"
}
```

## Argument Reference

The following arguments are supported:

* `display_name` - (Optional) Specifies a friendly name for the certificate.
Must start with `CN=`. Changing this field forces a new resource to be created.

~> If not specified, it will default to `CN=Microsoft Azure Federated SSO Certificate`.

* `end_date` - (Optional) The end date until which the token signing certificate is valid, formatted as an RFC3339 date string (e.g. `2018-01-01T01:02:03Z`). Changing this field forces a new resource to be created.

* `service_principal_id` - (Required) The object ID of the service principal for which this certificate should be created. Changing this field forces a new resource to be created.

## Attributes Reference

In addition to all arguments above, the following attributes are exported:

* `key_id` - A UUID used to uniquely identify the verify certificate.

* `thumbprint` - A SHA-1 generated thumbprint of the token signing certificate, which can be used to set the preferred signing certificate for a service principal.

* `start_date` - The start date from which the certificate is valid, formatted as an RFC3339 date string (e.g. `2018-01-01T01:02:03Z`).

* `value` - The certificate data, which is pem encoded but does not include the
manicminer marked this conversation as resolved.
Show resolved Hide resolved
header `-----BEGIN CERTIFICATE-----\n` or the footer `\n-----END CERTIFICATE-----`.

## Import

Token signing certificates can be imported using the object ID of the associated service principal and the key ID of the verify certificate credential, e.g.

```shell
terraform import azuread_service_principal_token_signing_certificate.test 00000000-0000-0000-0000-000000000000/tokenSigningCertificate/11111111-1111-1111-1111-111111111111
manicminer marked this conversation as resolved.
Show resolved Hide resolved
```

-> This ID format is unique to Terraform and is composed of the service principal's object ID, the string "tokenSigningCertificate" and the verify certificate's key ID in the format `{ServicePrincipalObjectId}/tokenSigningCertificate/{CertificateKeyId}`.
33 changes: 33 additions & 0 deletions internal/helpers/credentials.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package helpers

import (
"bytes"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/pem"
Expand Down Expand Up @@ -40,6 +43,18 @@ func GetKeyCredential(keyCredentials *[]msgraph.KeyCredential, id string) (crede
return
}

func GetVerifyKeyCredentialFromCustomKeyId(keyCredentials *[]msgraph.KeyCredential, id string) (credential *msgraph.KeyCredential) {
if keyCredentials != nil {
for _, cred := range *keyCredentials {
if cred.KeyId != nil && strings.EqualFold(*cred.CustomKeyIdentifier, id) && strings.EqualFold(cred.Usage, msgraph.KeyCredentialUsageVerify) {
credential = &cred
break
}
}
}
return
}

func GetPasswordCredential(passwordCredentials *[]msgraph.PasswordCredential, id string) (credential *msgraph.PasswordCredential) {
if passwordCredentials != nil {
for _, cred := range *passwordCredentials {
Expand All @@ -52,6 +67,24 @@ func GetPasswordCredential(passwordCredentials *[]msgraph.PasswordCredential, id
return
}

func GetTokenSigningCertificateThumbprint(certByte []byte) (string, error) {
block, _ := pem.Decode(certByte)
if block == nil {
return "", fmt.Errorf("Failed to decode certificate block")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return "", fmt.Errorf("failed to parse certificate block data: %+v", err)
}
thumbprint := sha1.Sum(cert.Raw)

var buf bytes.Buffer
for _, f := range thumbprint {
fmt.Fprintf(&buf, "%02X", f)
}
return buf.String(), nil
}

func KeyCredentialForResource(d *schema.ResourceData) (*msgraph.KeyCredential, error) {
keyType := d.Get("type").(string)
value := d.Get("value").(string)
Expand Down
13 changes: 13 additions & 0 deletions internal/services/serviceprincipals/parse/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ func (id CredentialId) String() string {
return id.ObjectId + "/" + id.KeyType + "/" + id.KeyId
}

func SigningCertificateID(idString string) (*CredentialId, error) {
id, err := ObjectSubResourceID(idString, "tokenSigningCertificate")
if err != nil {
return nil, fmt.Errorf("unable to parse signing certificate ID: %v", err)
}

return &CredentialId{
ObjectId: id.objectId,
KeyType: id.Type,
KeyId: id.subId,
}, nil
}

func CertificateID(idString string) (*CredentialId, error) {
id, err := ObjectSubResourceID(idString, "certificate")
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions internal/services/serviceprincipals/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func (r Registration) SupportedResources() map[string]*schema.Resource {
"azuread_service_principal_claims_mapping_policy_assignment": servicePrincipalClaimsMappingPolicyAssignmentResource(),
"azuread_service_principal_delegated_permission_grant": servicePrincipalDelegatedPermissionGrantResource(),
"azuread_service_principal_password": servicePrincipalPasswordResource(),
"azuread_service_principal_token_signing_certificate": servicePrincipalTokenSigningCertificateResource(),
"azuread_synchronization_job": synchronizationJobResource(),
"azuread_synchronization_secret": synchronizationSecretResource(),
}
Expand Down
Loading