Skip to content

Commit

Permalink
azuread_application: add password property (#1389)
Browse files Browse the repository at this point in the history
* azuread_application: add password

* Update password resource

* Update to single password definition

* Update

* Update code

* Remove comments

* Redo mapping

* Add feedback

* Add feedback

* Add documentation

* azuread_application: linting, add documentation example for `password` block

* azuread_application: TestAccApplication_basic test fix

---------

Co-authored-by: Tom Bamford <tom@bamford.io>
  • Loading branch information
HappyTobi and manicminer authored Jun 25, 2024
1 parent 2fc4d08 commit bfaeb83
Show file tree
Hide file tree
Showing 5 changed files with 436 additions and 24 deletions.
55 changes: 53 additions & 2 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 Down Expand Up @@ -187,6 +214,10 @@ The following arguments are supported:

-> **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 @@ -284,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 @@ -326,6 +365,8 @@ The following arguments are supported:
* `access_token_issuance_enabled` - (Optional) Whether this web application can request an access token using OAuth 2.0 implicit flow.
* `id_token_issuance_enabled` - (Optional) Whether this web application can request an ID token using OAuth 2.0 implicit flow.

---

## Attributes Reference

In addition to all arguments above, the following attributes are exported:
Expand All @@ -337,8 +378,18 @@ 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` block exports the following:

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


## Import

Applications can be imported using the object ID of the application, in the following format.
Expand Down
54 changes: 33 additions & 21 deletions internal/helpers/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,47 +185,59 @@ func KeyCredentialForResource(d *pluginsdk.ResourceData) (*msgraph.KeyCredential
return &credential, nil
}

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

// 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 {
if v, ok := in["display_name"]; ok {
credential.DisplayName = pointer.To(v.(string))
}

if v, ok := d.GetOk("start_date"); ok {
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
}

var endDate *time.Time
if v, ok := d.GetOk("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 {
return nil, CredentialError{str: fmt.Sprintf("Unable to parse the provided end date %q: %+v", v, err), attr: "end_date"}
}
endDate = &expiry
} else if v, ok := d.GetOk("end_date_relative"); ok && v.(string) != "" {
d, err := time.ParseDuration(v.(string))
if err != nil {
return nil, CredentialError{str: fmt.Sprintf("Unable to parse `end_date_relative` (%q) as a duration", v), attr: "end_date_relative"}
}

if credential.StartDateTime == nil {
expiry := time.Now().Add(d)
endDate = &expiry
} else {
expiry := credential.StartDateTime.Add(d)
endDate = &expiry
}
credential.EndDateTime = &expiry
}
if endDate != nil {
credential.EndDateTime = endDate

if v, ok := in["key_id"]; ok && v.(string) != "" {
credential.KeyId = pointer.To(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{})

// 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 {
data["display_name"] = v
}

if v, ok := d.GetOk("start_date"); ok {
data["start_date"] = v
}

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) != "" {
data["end_date_relative"] = v
}

return PasswordCredential(data)
}
Loading

0 comments on commit bfaeb83

Please sign in to comment.