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

azuread_application: support the features block and the tags property #630

Merged
merged 6 commits into from
Oct 15, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
11 changes: 11 additions & 0 deletions docs/data-sources/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ The following attributes are exported:
* `disabled_by_microsoft` - Whether Microsoft has disabled the registered application. If the application is disabled, this will be a string indicating the status/reason, e.g. `DisabledDueToViolationOfServicesAgreement`
* `display_name` - The display name for the application.
* `fallback_public_client_enabled` - The fallback application type as public client, such as an installed application running on a mobile device.
* `features` - A `features` block as described below.
katbyte marked this conversation as resolved.
Show resolved Hide resolved
* `group_membership_claims` - The `groups` claim issued in a user or OAuth 2.0 access token that the app expects.
* `identifier_uris` - A list of user-defined URI(s) that uniquely identify a Web application within it's Azure AD tenant, or within a verified custom domain if the application is multi-tenant.
* `logo_url` - CDN URL to the application's logo.
Expand All @@ -64,6 +65,7 @@ The following attributes are exported:
* `sign_in_audience` - The Microsoft account types that are supported for the current application. One of `AzureADMyOrg`, `AzureADMultipleOrgs`, `AzureADandPersonalMicrosoftAccount` or `PersonalMicrosoftAccount`.
* `single_page_application` - A `single_page_application` block as documented below.
* `support_url` - URL of the application's support page.
* `tags` - A list of tags applied to the application.
* `terms_of_service_url` - URL of the application's terms of service statement.
* `web` - A `web` block as documented below.

Expand Down Expand Up @@ -102,6 +104,15 @@ The following attributes are exported:

---

`features` block exports the following:

* `custom_single_sign_on_app` - Whether this application represents a custom SAML application for linked service principals.
katbyte marked this conversation as resolved.
Show resolved Hide resolved
* `enterprise_application` - Whether this application represents an Enterprise Application for linked service principals.
* `gallery_application` - Whether this application represents a gallery application for linked service principals.
katbyte marked this conversation as resolved.
Show resolved Hide resolved
* `visible_to_users` - Whether this app is visible to users in My Apps and Office 365 Launcher.
katbyte marked this conversation as resolved.
Show resolved Hide resolved

---

`optional_claims` block exports the following:

* `access_token` - One or more `access_token` blocks as documented below.
Expand Down
22 changes: 22 additions & 0 deletions docs/resources/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ resource "azuread_application" "example" {
value = "User"
}

feature_tags {
enterprise = true
gallery = true
}

optional_claims {
access_token {
name = "myclaim"
Expand Down Expand Up @@ -162,6 +167,10 @@ The following arguments are supported:
* `device_only_auth_enabled` - (Optional) Specifies whether this application supports device authentication without a user. Defaults to `false`.
* `display_name` - (Required) The display name for the application.
* `fallback_public_client_enabled` - (Optional) Specifies whether the application is a public client. Appropriate for apps using token grant flows that don't use a redirect URI. Defaults to `false`.
* `feature_tags` - (Optional) A `feature_tags` block as described below. Cannot be used together with the `tags` property.

-> **Features and Tags** Features are configured for an application using tags, and are provided as a shortcut to set the corresponding magic tag value for each feature. You cannot configure `feature_tags` and `tags` for an application at the same time, so if you need to assign additional custom tags it's recommended to use the `tags` property instead. Tag values also propagate to any linked service principals.

* `group_membership_claims` - (Optional) Configures the `groups` claim issued in a user or OAuth 2.0 access token that the app expects. Possible values are `None`, `SecurityGroup`, `DirectoryRole`, `ApplicationGroup` or `All`.
* `identifier_uris` - (Optional) A set of user-defined URI(s) that uniquely identify an application within its Azure AD tenant, or within a verified custom domain if the application is multi-tenant.
* `logo_image` - (Optional) A logo image to upload for the application, as a raw base64-encoded string. The image should be in gif, jpeg or png format. Note that once an image has been uploaded, it is not possible to remove it without replacing it with another image.
Expand All @@ -182,6 +191,10 @@ The following arguments are supported:

* `single_page_application` - (Optional) A `single_page_application` block as documented below, which configures single-page application (SPA) related settings for this application.
* `support_url` - (Optional) URL of the application's support page.
* `tags` - (Optional) A set of tags to apply to the application. Cannot be used together with the `feature_tags` block.

-> **Tags and Features** Azure Active Directory uses special tag values to configure the behavior of applications. These can be specified using either the `tags` property or with the `feature_tags` block. If you need to set any custom tag values not supported by the `feature_tags` block, it's recommended to use the `tags` property. Tag values also propagate to any linked service principals.

* `template_id` - (Optional) Unique ID for a templated application in the Azure AD App Gallery, from which to create the application. Changing this forces a new resource to be created.
* `terms_of_service_url` - (Optional) URL of the application's terms of service statement.
* `web` - (Optional) A `web` block as documented below, which configures web related settings for this application.
Expand Down Expand Up @@ -235,6 +248,15 @@ The following arguments are supported:

---

`feature_tags` block supports the following:

* `custom_single_sign_on` - (Optional) Whether this application represents a custom SAML application for linked service principals. Enabling this will assign the `WindowsAzureActiveDirectoryCustomSingleSignOnApplication` tag. Defaults to `false`.
* `enterprise` - (Optional) Whether this application represents an Enterprise Application for linked service principals. Enabling this will assign the `WindowsAzureActiveDirectoryIntegratedApp` tag. Defaults to `false`.
* `gallery` - (Optional) Whether this application represents a gallery application for linked service principals. Enabling this will assign the `WindowsAzureActiveDirectoryGalleryApplicationNonPrimaryV1` tag. Defaults to `false`.
* `hide` - (Optional) Whether this app is invisible to users in My Apps and Office 365 Launcher. Enabling this will assign the `HideApp` tag. Defaults to `false`.

---

`optional_claims` block supports the following:

* `access_token` - (Optional) One or more `access_token` blocks as documented below.
Expand Down
26 changes: 16 additions & 10 deletions docs/resources/service_principal.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ resource "azuread_service_principal" "example" {
app_role_assignment_required = false
owners = [data.azuread_client_config.current.object_id]

features {
enterprise_application = true
gallery_application = true
feature_tags {
enterprise = true
gallery = true
}
}
```
Expand Down Expand Up @@ -95,7 +95,10 @@ The following arguments are supported:
* `app_role_assignment_required` - (Optional) Whether this service principal requires an app role assignment to a user or group before Azure AD will issue a user or access token to the application. Defaults to `false`.
* `application_id` - (Required) The application ID (client ID) of the application for which to create a service principal.
* `description` - (Optional) A description of the service principal provided for internal end-users.
* `features` - (Optional) A `features` block as described below. Cannot be used together with the `tags` property.
* `feature_tags` - (Optional) A `feature_tags` block as described below. Cannot be used together with the `tags` property.

-> **Features and Tags** Features are configured for a service principal using tags, and are provided as a shortcut to set the corresponding magic tag value for each feature. You cannot configure `feature_tags` and `tags` for a service principal at the same time, so if you need to assign additional custom tags it's recommended to use the `tags` property instead. Any tags configured for the linked application will propagate to this service principal.

* `login_url` - (Optional) The URL where the service provider redirects the user to Azure AD to authenticate. Azure AD uses the URL to launch the application from Microsoft 365 or the Azure AD My Apps. When blank, Azure AD performs IdP-initiated sign-on for applications configured with SAML-based single sign-on.
* `notes` - (Optional) A free text field to capture information about the service principal, typically used for operational purposes.
* `notification_email_addresses` - (Optional) A set of email addresses where Azure AD sends a notification when the active certificate is near the expiration date. This is only for the certificates used to sign the SAML token issued for Azure AD Gallery applications.
Expand All @@ -105,19 +108,22 @@ The following arguments are supported:

* `preferred_single_sign_on_mode` - (Optional) The single sign-on mode configured for this application. Azure AD uses the preferred single sign-on mode to launch the application from Microsoft 365 or the Azure AD My Apps. Supported values are `oidc`, `password`, `saml` or `notSupported`. Omit this property or specify a blank string to unset.
* `saml_single_sign_on` - (Optional) A `saml_single_sign_on` block as documented below.
* `tags` - (Optional) A set of tags to apply to the service principal. Cannot be used together with the `features` block.
* `tags` - (Optional) A set of tags to apply to the service principal. Cannot be used together with the `feature_tags` block.

-> **Tags and Features** Azure Active Directory uses special tag values to configure the behavior of service principals. These can be specified using either the `tags` property or with the `feature_tags` block. If you need to set any custom tag values not supported by the `feature_tags` block, it's recommended to use the `tags` property. Tag values set for the linked application will also propagate to this service principal.

* `use_existing` - (Optional) When true, any existing service principal linked to the same application will be automatically imported. When false, an import error will be raised for any pre-existing service principal.

-> **Caveats of `use_existing`** Enabling this behaviour is useful for managing existing service principals that may already be installed in your tenant for Microsoft-published APIs, as it allows you to make changes where permitted, and then also reference them in your Terraform configuration. However, the behaviour of delete operations is also affected - when `use_existing` is `true`, Terraform will still attempt to delete the service principal on destroy, although it will not raise an error if the deletion fails (as it often the case for first-party Microsoft applications).

---

`features` block supports the following:
`feature_tags` block supports the following:

* `custom_single_sign_on_app` - (Optional) Whether this service principal represents a custom SAML application. Defaults to `false`.
* `enterprise_application` - (Optional) Whether this service principal represents an Enterprise Application. Defaults to `false`.
* `gallery_application` - (Optional) Whether this service principal represents a gallery application. Defaults to `false`.
* `visible_to_users` - (Optional) Whether this app is visible to users in My Apps and Office 365 Launcher. Defaults to `true`.
* `custom_single_sign_on` - (Optional) Whether this service principal represents a custom SAML application. Enabling this will assign the `WindowsAzureActiveDirectoryCustomSingleSignOnApplication` tag. Defaults to `false`.
* `enterprise` - (Optional) Whether this service principal represents an Enterprise Application. Enabling this will assign the `WindowsAzureActiveDirectoryIntegratedApp` tag. Defaults to `false`.
* `gallery` - (Optional) Whether this service principal represents a gallery application. Enabling this will assign the `WindowsAzureActiveDirectoryGalleryApplicationNonPrimaryV1` tag. Defaults to `false`.
* `hide` - (Optional) Whether this app is invisible to users in My Apps and Office 365 Launcher. Enabling this will assign the `HideApp` tag. Defaults to `false`.

---

Expand Down
99 changes: 99 additions & 0 deletions internal/helpers/applications.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,47 @@
package helpers

import (
"strings"

"github.com/manicminer/hamilton/msgraph"
)

func ApplicationExpandFeatures(in []interface{}) []string {
out := make([]string, 0)

if len(in) == 0 || in[0] == nil {
return out
}

features := in[0].(map[string]interface{})

if v, ok := features["custom_single_sign_on"]; ok && v.(bool) {
out = append(out, "WindowsAzureActiveDirectoryCustomSingleSignOnApplication")
} else if v, ok := features["custom_single_sign_on_app"]; ok && v.(bool) {
out = append(out, "WindowsAzureActiveDirectoryCustomSingleSignOnApplication")
}

if v, ok := features["enterprise"]; ok && v.(bool) {
out = append(out, "WindowsAzureActiveDirectoryIntegratedApp")
} else if v, ok := features["enterprise_application"]; ok && v.(bool) { // TODO: remove in v3.0
out = append(out, "WindowsAzureActiveDirectoryIntegratedApp")
}

if v, ok := features["gallery"]; ok && v.(bool) {
out = append(out, "WindowsAzureActiveDirectoryGalleryApplicationNonPrimaryV1")
} else if v, ok := features["gallery_application"]; ok && v.(bool) { // TODO: remove in v3.0
out = append(out, "WindowsAzureActiveDirectoryGalleryApplicationNonPrimaryV1")
}

if v, ok := features["hide"]; ok && v.(bool) {
out = append(out, "HideApp")
} else if v, ok := features["visible_to_users"]; ok && !v.(bool) { // TODO: remove in v3.0
out = append(out, "HideApp")
}

return out
}

func ApplicationFlattenAppRoleIDs(in *[]msgraph.AppRole) map[string]string {
result := make(map[string]string)
if in != nil {
Expand Down Expand Up @@ -61,6 +99,67 @@ func ApplicationFlattenAppRoles(in *[]msgraph.AppRole) (result []map[string]inte
return //nolint:nakedret
}

func ApplicationFlattenFeatures(tags *[]string, deprecated bool) []interface{} {
// TODO: remove this in v3.0
if deprecated {
result := map[string]bool{
"custom_single_sign_on_app": false,
"enterprise_application": false,
"gallery_application": false,
"visible_to_users": true,
}

if tags == nil || len(*tags) == 0 {
return []interface{}{result}
}

for _, tag := range *tags {
if strings.EqualFold(tag, "WindowsAzureActiveDirectoryCustomSingleSignOnApplication") {
result["custom_single_sign_on_app"] = true
}
if strings.EqualFold(tag, "WindowsAzureActiveDirectoryIntegratedApp") {
result["enterprise_application"] = true
}
if strings.EqualFold(tag, "WindowsAzureActiveDirectoryGalleryApplicationNonPrimaryV1") {
result["gallery_application"] = true
}
if strings.EqualFold(tag, "HideApp") {
result["visible_to_users"] = false
}
}

return []interface{}{result}
}

result := map[string]bool{
"custom_single_sign_on": false,
"enterprise": false,
"gallery": false,
"hide": false,
}

if tags == nil || len(*tags) == 0 {
return []interface{}{result}
}

for _, tag := range *tags {
if strings.EqualFold(tag, "WindowsAzureActiveDirectoryCustomSingleSignOnApplication") {
result["custom_single_sign_on"] = true
}
if strings.EqualFold(tag, "WindowsAzureActiveDirectoryIntegratedApp") {
result["enterprise"] = true
}
if strings.EqualFold(tag, "WindowsAzureActiveDirectoryGalleryApplicationNonPrimaryV1") {
result["gallery"] = true
}
if strings.EqualFold(tag, "HideApp") {
result["hide"] = true
}
}

return []interface{}{result}
}

func ApplicationFlattenOAuth2PermissionScopeIDs(in *[]msgraph.PermissionScope) map[string]string {
result := make(map[string]string)
if in != nil {
Expand Down
46 changes: 46 additions & 0 deletions internal/services/applications/application_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/manicminer/hamilton/odata"

"github.com/hashicorp/terraform-provider-azuread/internal/clients"
"github.com/hashicorp/terraform-provider-azuread/internal/helpers"
"github.com/hashicorp/terraform-provider-azuread/internal/tf"
"github.com/hashicorp/terraform-provider-azuread/internal/validate"
)
Expand Down Expand Up @@ -214,6 +215,40 @@ func applicationDataSource() *schema.Resource {
Computed: true,
},

"feature_tags": {
Description: "Block of features configured for this application using tags",
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"custom_single_sign_on": {
Description: "Whether this application principal represents a custom SAML application for linked service principals",
Type: schema.TypeBool,
Optional: true,
},

"enterprise": {
Description: "Whether this application represents an Enterprise Application for linked service principals",
Type: schema.TypeBool,
Optional: true,
},

"gallery": {
Description: "Whether this application represents a gallery application for linked service principals",
Type: schema.TypeBool,
Optional: true,
},

"hide": {
Description: "Whether this app is invisible to users in My Apps and Office 365 Launcher",
Type: schema.TypeBool,
Optional: true,
Default: true,
},
},
},
},

"group_membership_claims": {
Description: "The `groups` claim issued in a user or OAuth 2.0 access token that the app expects",
Type: schema.TypeList,
Expand Down Expand Up @@ -373,6 +408,15 @@ func applicationDataSource() *schema.Resource {
Computed: true,
},

"tags": {
Description: "A set of tags applied to the application",
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},

"terms_of_service_url": {
Description: "URL of the application's terms of service statement",
Type: schema.TypeString,
Expand Down Expand Up @@ -511,6 +555,7 @@ func applicationDataSourceRead(ctx context.Context, d *schema.ResourceData, meta
tf.Set(d, "disabled_by_microsoft", fmt.Sprintf("%v", app.DisabledByMicrosoftStatus))
tf.Set(d, "display_name", app.DisplayName)
tf.Set(d, "fallback_public_client_enabled", app.IsFallbackPublicClient)
tf.Set(d, "feature_tags", helpers.ApplicationFlattenFeatures(app.Tags, false))
tf.Set(d, "group_membership_claims", tf.FlattenStringSlicePtr(app.GroupMembershipClaims))
tf.Set(d, "identifier_uris", tf.FlattenStringSlicePtr(app.IdentifierUris))
tf.Set(d, "oauth2_post_response_required", app.Oauth2RequirePostResponse)
Expand All @@ -521,6 +566,7 @@ func applicationDataSourceRead(ctx context.Context, d *schema.ResourceData, meta
tf.Set(d, "required_resource_access", flattenApplicationRequiredResourceAccess(app.RequiredResourceAccess))
tf.Set(d, "sign_in_audience", app.SignInAudience)
tf.Set(d, "single_page_application", flattenApplicationSpa(app.Spa))
tf.Set(d, "tags", app.Tags)
tf.Set(d, "web", flattenApplicationWeb(app.Web))

if app.Api != nil {
Expand Down
Loading