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: add password property #1389

Merged
merged 12 commits into from
Jun 25, 2024
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
Loading