Skip to content

Commit

Permalink
azuread_application: linting, add documentation example for `password…
Browse files Browse the repository at this point in the history
…` block
  • Loading branch information
manicminer committed Jun 25, 2024
1 parent 5f841c6 commit 53dd076
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 31 deletions.
64 changes: 49 additions & 15 deletions docs/resources/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,33 @@ resource "azuread_application" "example" {
}
```

*Create application and generate a password*

```terraform
data "azuread_client_config" "current" {}
resource "time_rotating" "example" {
rotation_days = 180
}
resource "azuread_application" "example" {
display_name = "example"
owners = [data.azuread_client_config.current.object_id]
password {
display_name = "MySecret-1"
start_date = time_rotating.example.id
end_date = timeadd(time_rotating.example.id, "4320h")
}
}
output "example_password" {
sensitive = true
value = tolist(azuread_application.example.password).0.value
}
```

*Create application from a gallery template*

```terraform
Expand All @@ -157,8 +184,8 @@ resource "azuread_application" "example" {
}
resource "azuread_service_principal" "example" {
application_id = azuread_application.example.application_id
use_existing = true
client_id = azuread_application.example.client_id
use_existing = true
}
```

Expand All @@ -184,11 +211,13 @@ The following arguments are supported:
* `oauth2_post_response_required` - (Optional) Specifies whether, as part of OAuth 2.0 token requests, Azure AD allows POST requests, as opposed to GET requests. Defaults to `false`, which specifies that only GET requests are allowed.
* `optional_claims` - (Optional) An `optional_claims` block as documented below.
* `owners` - (Optional) A set of object IDs of principals that will be granted ownership of the application. Supported object types are users or service principals. By default, no owners are assigned.
* `password` - (Optional) Single `password` block das decoumented below. The password is generated as part of the application and can be used instantaneous. By default, no password is generated.


-> **Ownership of Applications** It's recommended to always specify one or more application owners, including the principal being used to execute Terraform, such as in the example above.

* `password` - (Optional) A single `password` block as documented below. The password is generated during creation. By default, no password is generated.

-> **Creating a Password** The `password` block supports a single password for the application, and is provided so that a password can be generated when a new application is created. This helps to make new applications available for authentication more quickly. To add additional passwords to an application, see the [azuread_application_password](application_password.html) resource.

* `prevent_duplicate_names` - (Optional) If `true`, will return an error if an existing application is found with the same name. Defaults to `false`.
* `privacy_statement_url` - (Optional) URL of the application's privacy statement.
* `public_client` - (Optional) A `public_client` block as documented below, which configures non-web app or non-web API application settings, for example mobile or other public clients such as an installed application running on a desktop device.
Expand Down Expand Up @@ -286,6 +315,14 @@ The following arguments are supported:

---

`password` block supports the following:

* `display_name` - (Required) A display name for the password. Changing this field forces a new resource to be created.
* `end_date` - (Optional) The end date until which the password 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.
* `start_date` - (Optional) The start date from which the password is valid, formatted as an RFC3339 date string (e.g. `2018-01-01T01:02:03Z`). If this isn't specified, the current date is used. Changing this field forces a new resource to be created.

---

`public_client` block supports the following:

* `redirect_uris` - (Optional) A set of URLs where user tokens are sent for sign-in, or the redirect URIs where OAuth 2.0 authorization codes and access tokens are sent. Must be a valid `https` or `ms-appx-web` URL.
Expand Down Expand Up @@ -330,16 +367,6 @@ The following arguments are supported:

---

`password` block supports the following:

-> **Tip: Generating a `password` for the application inline** To inline generation of a password is usable instantaneously after the application is created. There should be no delay to use created resource instead of using the `azuread_application_password`

* `display_name` - (Required) A display name for the password. Changing this field forces a new resource to be created.
* `end_date` - (Optional) The end date until which the password 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.
* `start_date` - (Optional) The start date from which the password is valid, formatted as an RFC3339 date string (e.g. `2018-01-01T01:02:03Z`). If this isn't specified, the current date is used. Changing this field forces a new resource to be created.

---

## Attributes Reference

In addition to all arguments above, the following attributes are exported:
Expand All @@ -351,9 +378,16 @@ In addition to all arguments above, the following attributes are exported:
* `logo_url` - CDN URL to the application's logo, as uploaded with the `logo_image` property.
* `oauth2_permission_scope_ids` - A mapping of OAuth2.0 permission scope values to scope IDs, intended to be useful when referencing permission scopes in other resources in your configuration.
* `object_id` - The application's object ID.
* `password` - A `password` block as documented below. Note that this block is a set rather than a list, and you will need to convert or iterate it to address its attributes (see the usage example above).
* `publisher_domain` - The verified publisher domain for the application.
* `publisher_domain` - The verified publisher domain for the application.
* `password` - The password `value` and `key_id` of the application password that is created inline.

---

`password` block exports the following:

* `key_id` - (Required) The unique key ID for the generated password.
* `value` - (Required) The generated password for the application.


## Import
Expand Down
16 changes: 7 additions & 9 deletions internal/helpers/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,22 +185,22 @@ func KeyCredentialForResource(d *pluginsdk.ResourceData) (*msgraph.KeyCredential
return &credential, nil
}

func PasswordCredential(d map[string]interface{}) (*msgraph.PasswordCredential, error) {
func PasswordCredential(in map[string]interface{}) (*msgraph.PasswordCredential, error) {
credential := msgraph.PasswordCredential{}

if v, ok := d["display_name"]; ok {
if v, ok := in["display_name"]; ok {
credential.DisplayName = pointer.To(v.(string))
}

if v, ok := d["start_date"]; ok && v.(string) != "" {
if v, ok := in["start_date"]; ok && v.(string) != "" {
startDate, err := time.Parse(time.RFC3339, v.(string))
if err != nil {
return nil, CredentialError{str: fmt.Sprintf("Unable to parse the provided start date %q: %+v", v, err), attr: "start_date"}
}
credential.StartDateTime = &startDate
}

if v, ok := d["end_date"]; ok && v.(string) != "" {
if v, ok := in["end_date"]; ok && v.(string) != "" {
var err error
expiry, err := time.Parse(time.RFC3339, v.(string))
if err != nil {
Expand All @@ -210,20 +210,19 @@ func PasswordCredential(d map[string]interface{}) (*msgraph.PasswordCredential,
credential.EndDateTime = &expiry
}

if v, ok := d["key_id"]; ok && v.(string) != "" {
if v, ok := in["key_id"]; ok && v.(string) != "" {
credential.KeyId = pointer.To(v.(string))
}

if v, ok := d["value"]; ok && v.(string) != "" {
if v, ok := in["value"]; ok && v.(string) != "" {
credential.SecretText = pointer.To(v.(string))
}

return &credential, nil
}

func PasswordCredentialForResource(d *pluginsdk.ResourceData) (*msgraph.PasswordCredential, error) {

data := make(map[string]interface{}, 0)
data := make(map[string]interface{})

// display_name, start_date and end_date support intentionally remains for if/when the API supports user-specified values for these
if v, ok := d.GetOk("display_name"); ok {
Expand All @@ -234,7 +233,6 @@ func PasswordCredentialForResource(d *pluginsdk.ResourceData) (*msgraph.Password
data["start_date"] = v
}

// var endDate *time.Time
if v, ok := d.GetOk("end_date"); ok && v.(string) != "" {
data["end_date"] = v
} else if v, ok := d.GetOk("end_date_relative"); ok && v.(string) != "" {
Expand Down
11 changes: 5 additions & 6 deletions internal/services/applications/application_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,10 +410,12 @@ func applicationResource() *pluginsdk.Resource {
},
},

//lintignore:S018 // We are intentionally using TypeSet here to effect a replace-style representation in the diff for this block
"password": {
Description: "App password definition",
Type: pluginsdk.TypeSet,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
Expand Down Expand Up @@ -1062,7 +1064,7 @@ func applicationResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, m
Web: expandApplicationWeb(d.Get("web").([]interface{})),
}

// Create application passwords, the first is created within the application request, rest is added later.
// Generate an application password, if specified
if v, ok := d.GetOk("password"); ok {
password := v.(*pluginsdk.Set).List()
if len(password) > 1 {
Expand Down Expand Up @@ -1139,12 +1141,9 @@ func applicationResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, m
id := parse.NewApplicationID(*app.ID())
d.SetId(id.ID())

// set the pw credentials to state
// Save the password key ID and generated value to state
if app.PasswordCredentials != nil {
password := d.Get("password").(*pluginsdk.Set).List()

// Save the password key ID and generated value to state
if len(password) == 1 {
if password := d.Get("password").(*pluginsdk.Set).List(); len(password) == 1 {
pw := password[0].(map[string]interface{})
if credentials := flattenApplicationPasswordCredentials(app.PasswordCredentials); len(credentials) == 1 {
pw["key_id"] = credentials[0]["key_id"]
Expand Down
1 change: 0 additions & 1 deletion internal/services/applications/applications.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,6 @@ func expandApplicationPasswordCredentials(input []interface{}) (*[]msgraph.Passw
}

return &result, nil

}

func expandApplicationAppRoles(input []interface{}) *[]msgraph.AppRole {
Expand Down

0 comments on commit 53dd076

Please sign in to comment.