diff --git a/docs/guides/microsoft-graph.md b/docs/guides/microsoft-graph.md index 04c3c2d006..d198e7d226 100644 --- a/docs/guides/microsoft-graph.md +++ b/docs/guides/microsoft-graph.md @@ -232,14 +232,16 @@ The deprecated field `description` has been replaced by the `display_name` field The `azuread_service_principal_password` resource no longer supports importing. -The `display_name`, `start_date` and `end_date` fields are no longer respected by the API and have been made read-only. Accordingly the `end_date_relative` field has been removed. - The `key_id` field has become read-only as Azure Active Directory no longer allows user-specified key IDs for passwords. The `value` field has become read-only as Azure Active Directory no longer accepts user-supplied password values. Passwords are instead auto-generated by Azure and exported with the `value` attribute by the resource. -> **Removal of `key_id` and `value`** Note that whilst these properties must be removed from any existing `azuread_service_principal_password` resource configurations, doing so will not remove the password value from your service principal. +-> The following also applies when the Microsoft Graph beta is enabled in version 1.5 through 1.6.0, and in versions 2.0.0 through 2.13.0 of the provider, but not in version 2.14.0 or later. + +The `display_name`, `start_date` and `end_date` fields are no longer respected by the API and have been made read-only. Accordingly the `end_date_relative` field has been removed. + ### Resource: `azuread_user` The deprecated field `immutable_id` has been replaced by the `onpremises_immutable_id` field and has been removed. diff --git a/docs/resources/service_principal_password.md b/docs/resources/service_principal_password.md index 875efc44fa..3a307cd0bc 100644 --- a/docs/resources/service_principal_password.md +++ b/docs/resources/service_principal_password.md @@ -60,17 +60,18 @@ resource "azuread_service_principal_password" "example" { The following arguments are supported: +* `display_name` - (Optional) A display name for the password. +* `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. +* `end_date_relative` - (Optional) A relative duration for which the password is valid until, for example `240h` (10 days) or `2400h30m`. Changing this field forces a new resource to be created. * `rotate_when_changed` - (Optional) A map of arbitrary key/value pairs that will force recreation of the password when they change, enabling password rotation based on external conditions such as a rotating timestamp. Changing this forces a new resource to be created. * `service_principal_id` - (Required) The object ID of the service principal for which this password should be created. 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: -* `display_name` - The display name for the password. -* `end_date` - The end date until which the password is valid, formatted as an RFC3339 date string (e.g. `2018-01-01T01:02:03Z`). * `key_id` - A UUID used to uniquely identify this password credential. -* `start_date` - The start date from which the password is valid, formatted as an RFC3339 date string (e.g. `2018-01-01T01:02:03Z`). * `value` - The password for this service principal, which is generated by Azure Active Directory. ## Import diff --git a/internal/services/serviceprincipals/service_principal_password_resource.go b/internal/services/serviceprincipals/service_principal_password_resource.go index 52a368cfd7..aa26b57ca6 100644 --- a/internal/services/serviceprincipals/service_principal_password_resource.go +++ b/internal/services/serviceprincipals/service_principal_password_resource.go @@ -9,6 +9,8 @@ import ( "strings" "time" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -54,6 +56,42 @@ func servicePrincipalPasswordResource() *schema.Resource { ValidateDiagFunc: validate.UUID, }, + "display_name": { + Description: "A display name for the password", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "start_date": { + Description: "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", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.IsRFC3339Time, + }, + + "end_date": { + Description: "The end date until which the password is valid, formatted as an RFC3339 date string (e.g. `2018-01-01T01:02:03Z`)", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"end_date_relative"}, + ValidateFunc: validation.IsRFC3339Time, + }, + + "end_date_relative": { + Description: "A relative duration for which the password is valid until, for example `240h` (10 days) or `2400h30m`. Changing this field forces a new resource to be created", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"end_date"}, + ValidateDiagFunc: validate.NoEmptyStrings, + }, + "rotate_when_changed": { Description: "Arbitrary map of values that, when changed, will trigger rotation of the password", Type: schema.TypeMap, @@ -70,24 +108,6 @@ func servicePrincipalPasswordResource() *schema.Resource { Computed: true, }, - "display_name": { - Description: "The display name for the password", - Type: schema.TypeString, - Computed: true, - }, - - "start_date": { - Description: "The start date from which the password is valid, formatted as an RFC3339 date string (e.g. `2018-01-01T01:02:03Z`)", - Type: schema.TypeString, - Computed: true, - }, - - "end_date": { - Description: "The end date until which the password is valid, formatted as an RFC3339 date string (e.g. `2018-01-01T01:02:03Z`)", - Type: schema.TypeString, - Computed: true, - }, - "value": { Description: "The password for this service principal, which is generated by Azure Active Directory", Type: schema.TypeString, diff --git a/internal/services/serviceprincipals/service_principal_password_resource_test.go b/internal/services/serviceprincipals/service_principal_password_resource_test.go index 1c3d5873d7..8f4ef6a549 100644 --- a/internal/services/serviceprincipals/service_principal_password_resource_test.go +++ b/internal/services/serviceprincipals/service_principal_password_resource_test.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "testing" + "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -37,6 +38,45 @@ func TestAccServicePrincipalPassword_basic(t *testing.T) { }) } +func TestAccServicePrincipalPassword_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azuread_service_principal_password", "test") + startDate := time.Now().AddDate(0, 0, 7).UTC().Format(time.RFC3339) + endDate := time.Now().AddDate(0, 5, 27).UTC().Format(time.RFC3339) + r := ServicePrincipalPasswordResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.complete(data, startDate, endDate), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("key_id").Exists(), + check.That(data.ResourceName).Key("start_date").Exists(), + check.That(data.ResourceName).Key("end_date").Exists(), + check.That(data.ResourceName).Key("value").Exists(), + ), + }, + }) +} + +func TestAccServicePrincipalPassword_relativeEndDate(t *testing.T) { + data := acceptance.BuildTestData(t, "azuread_service_principal_password", "test") + r := ServicePrincipalPasswordResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.relativeEndDate(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("end_date").Exists(), + check.That(data.ResourceName).Key("end_date_relative").HasValue("8760h"), + check.That(data.ResourceName).Key("key_id").Exists(), + check.That(data.ResourceName).Key("start_date").Exists(), + check.That(data.ResourceName).Key("value").Exists(), + ), + }, + }) +} + func (r ServicePrincipalPasswordResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { client := clients.ServicePrincipals.ServicePrincipalsClient client.BaseClient.DisableRetries = true @@ -65,7 +105,7 @@ func (r ServicePrincipalPasswordResource) Exists(ctx context.Context, clients *c return nil, fmt.Errorf("Password Credential %q was not found for Service Principal %q", id.KeyId, id.ObjectId) } -func (r ServicePrincipalPasswordResource) basic(data acceptance.TestData) string { +func (r ServicePrincipalPasswordResource) template(data acceptance.TestData) string { return fmt.Sprintf(` resource "azuread_application" "test" { display_name = "acctestServicePrincipal-%[1]d" @@ -74,9 +114,40 @@ resource "azuread_application" "test" { resource "azuread_service_principal" "test" { application_id = azuread_application.test.application_id } +`, data.RandomInteger) +} + +func (r ServicePrincipalPasswordResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s resource "azuread_service_principal_password" "test" { service_principal_id = azuread_service_principal.test.object_id } -`, data.RandomInteger) +`, r.template(data)) +} + +func (r ServicePrincipalPasswordResource) complete(data acceptance.TestData, startDate, endDate string) string { + return fmt.Sprintf(` +%[1]s + +resource "azuread_service_principal_password" "test" { + service_principal_id = azuread_service_principal.test.object_id + display_name = "terraform-%[2]s" + start_date = "%[3]s" + end_date = "%[4]s" +} +`, r.template(data), data.RandomString, startDate, endDate) +} + +func (r ServicePrincipalPasswordResource) relativeEndDate(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azuread_service_principal_password" "test" { + service_principal_id = azuread_service_principal.test.object_id + display_name = "terraform-%[2]s" + end_date_relative = "8760h" +} +`, r.template(data), data.RandomString) }