Skip to content

Commit

Permalink
Merge pull request #1 from hashicorp/main
Browse files Browse the repository at this point in the history
Fork Sync: Update from parent repository
  • Loading branch information
mbialon authored Sep 14, 2023
2 parents 2fb54e1 + efbb8f3 commit e7367ab
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 24 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 2.42.0 (September 15, 2023)

IMPROVEMENTS:

* provider: support for the `client_id_file_path` and `client_secret_file_path` provider properties ([#1189](https://github.com/hashicorp/terraform-provider-azuread/issues/1189))
* `data.azuread_group` - support for looking up a group with the `mail_nickname` property ([#1173](https://github.com/hashicorp/terraform-provider-azuread/issues/1173))

BUG FIXES:

* `azuread_conditional_access_policy` - allow specifying `terms_of_use` in place of `built_in_controls` in the `grant_controls` block ([#1168](https://github.com/hashicorp/terraform-provider-azuread/issues/1168))

## 2.41.0 (July 27, 2023)

FEATURES:
Expand Down
3 changes: 2 additions & 1 deletion docs/data-sources/group.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ data "azuread_group" "example" {
The following arguments are supported:

* `display_name` - (Optional) The display name for the group.
* `mail_nickname` - (Optional) The mail alias for the group, unique in the organisation.
* `mail_enabled` - (Optional) Whether the group is mail-enabled.
* `object_id` - (Optional) Specifies the object ID of the group.
* `security_enabled` - (Optional) Whether the group is a security group.

~> One of `display_name` or `object_id` must be specified.
~> One of `display_name`, `object_id` or `mail_nickname` must be specified.

## Attributes Reference

Expand Down
9 changes: 4 additions & 5 deletions docs/resources/access_package_assignment_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The following API permissions are required in order to use this resource.

When authenticated with a service principal, this resource requires the following application role: `EntitlementManagement.ReadWrite.All`.

When authenticated with a user principal, this resource requires `Global Administrator` directory role, or one of the `Catalog Owner` and `Access Package Manager` role in Idneity Governance.
When authenticated with a user principal, this resource requires `Global Administrator` directory role, or one of the `Catalog Owner` and `Access Package Manager` role in Identity Governance.

## Example Usage

Expand All @@ -33,8 +33,8 @@ resource "azuread_access_package" "example" {
description = "Access Package"
}
resource "azuread_access_package_assignment_policy" "test" {
access_package_id = azuread_access_package.test.id
resource "azuread_access_package_assignment_policy" "example" {
access_package_id = azuread_access_package.example.id
display_name = "assignment-policy"
description = "My assignment policy"
duration_in_days = 90
Expand All @@ -50,7 +50,7 @@ resource "azuread_access_package_assignment_policy" "test" {
approval_timeout_in_days = 14
primary_approver {
object_id = azuread_group.test.object_id
object_id = azuread_group.example.object_id
subject_type = "groupMembers"
}
}
Expand All @@ -72,7 +72,6 @@ resource "azuread_access_package_assignment_policy" "test" {
}
```


## Argument Reference

- `access_package_id` (Required) The ID of the access package that will contain the policy.
Expand Down
4 changes: 3 additions & 1 deletion docs/resources/conditional_access_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,13 @@ The following arguments are supported:

`grant_controls` block supports the following:

* `built_in_controls` - (Required) List of built-in controls required by the policy. Possible values are: `block`, `mfa`, `approvedApplication`, `compliantApplication`, `compliantDevice`, `domainJoinedDevice`, `passwordChange` or `unknownFutureValue`.
* `built_in_controls` - (Optional) List of built-in controls required by the policy. Possible values are: `block`, `mfa`, `approvedApplication`, `compliantApplication`, `compliantDevice`, `domainJoinedDevice`, `passwordChange` or `unknownFutureValue`.
* `custom_authentication_factors` - (Optional) List of custom controls IDs required by the policy.
* `operator` - (Required) Defines the relationship of the grant controls. Possible values are: `AND`, `OR`.
* `terms_of_use` - (Optional) List of terms of use IDs required by the policy.

-> At least one of `built_in_controls` or `terms_of_use` must be specified.

---

`session_controls` block supports the following:
Expand Down
74 changes: 71 additions & 3 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"log"
"os"
"strings"

"github.com/hashicorp/go-azure-sdk/sdk/auth"
"github.com/hashicorp/go-azure-sdk/sdk/environments"
Expand Down Expand Up @@ -81,6 +82,13 @@ func AzureADProvider() *schema.Provider {
Description: "The Client ID which should be used for service principal authentication",
},

"client_id_file_path": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_ID_FILE_PATH", ""),
Description: "The path to a file containing the Client ID which should be used for service principal authentication",
},

"tenant_id": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -132,6 +140,13 @@ func AzureADProvider() *schema.Provider {
Description: "The application password to use when authenticating as a Service Principal using a Client Secret",
},

"client_secret_file_path": {
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_SECRET_FILE_PATH", ""),
Description: "The path to a file containing the application password to use when authenticating as a Service Principal using a Client Secret",
},

// OIDC specific fields
"use_oidc": {
Type: schema.TypeBool,
Expand Down Expand Up @@ -228,9 +243,18 @@ func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc {
}
}

clientSecret, err := getClientSecret(d)
if err != nil {
return nil, diag.FromErr(err)
}

clientId, err := getClientId(d)
if err != nil {
return nil, diag.FromErr(err)
}

var (
env *environments.Environment
err error

envName = d.Get("environment").(string)
metadataHost = d.Get("metadata_host").(string)
Expand Down Expand Up @@ -258,11 +282,11 @@ func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc {
authConfig := &auth.Credentials{
Environment: *env,
TenantID: d.Get("tenant_id").(string),
ClientID: d.Get("client_id").(string),
ClientID: *clientId,
ClientCertificateData: certData,
ClientCertificatePassword: d.Get("client_certificate_password").(string),
ClientCertificatePath: d.Get("client_certificate_path").(string),
ClientSecret: d.Get("client_secret").(string),
ClientSecret: *clientSecret,
OIDCAssertionToken: idToken,
GitHubOIDCTokenRequestURL: d.Get("oidc_request_url").(string),
GitHubOIDCTokenRequestToken: d.Get("oidc_request_token").(string),
Expand Down Expand Up @@ -339,3 +363,47 @@ func oidcToken(d *schema.ResourceData) (string, error) {

return idToken, nil
}

func getClientId(d *schema.ResourceData) (*string, error) {
clientId := strings.TrimSpace(d.Get("client_id").(string))

if path := d.Get("client_id_file_path").(string); path != "" {
fileClientIdRaw, err := os.ReadFile(path)

if err != nil {
return nil, fmt.Errorf("reading Client ID from file %q: %v", path, err)
}

fileClientId := strings.TrimSpace(string(fileClientIdRaw))

if clientId != "" && clientId != fileClientId {
return nil, fmt.Errorf("mismatch between supplied Client ID and supplied Client ID file contents - please either remove one or ensure they match")
}

clientId = fileClientId
}

return &clientId, nil
}

func getClientSecret(d *schema.ResourceData) (*string, error) {
clientSecret := strings.TrimSpace(d.Get("client_secret").(string))

if path := d.Get("client_secret_file_path").(string); path != "" {
fileSecretRaw, err := os.ReadFile(path)

if err != nil {
return nil, fmt.Errorf("reading Client Secret from file %q: %v", path, err)
}

fileSecret := strings.TrimSpace(string(fileSecretRaw))

if clientSecret != "" && clientSecret != fileSecret {
return nil, fmt.Errorf("mismatch between supplied Client Secret and supplied Client Secret file contents - please either remove one or ensure they match")
}

clientSecret = fileSecret
}

return &clientSecret, nil
}
91 changes: 89 additions & 2 deletions internal/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,25 @@ func TestAccProvider_clientCertificateInlineAuth(t *testing.T) {
}

func TestAccProvider_clientSecretAuth(t *testing.T) {
t.Run("fromEnvironment", testAccProvider_clientSecretAuthFromEnvironment)
t.Run("fromFiles", testAccProvider_clientSecretAuthFromFiles)
}

func testAccProvider_clientSecretAuthFromEnvironment(t *testing.T) {
if os.Getenv("TF_ACC") == "" {
t.Skip("TF_ACC not set")
}
if os.Getenv("ARM_CLIENT_ID") == "" {
t.Skip("ARM_CLIENT_ID not set")
}
if os.Getenv("ARM_CLIENT_SECRET") == "" {
t.Skip("ARM_CLIENT_SECRET not set")
}

// Ensure we are running using the expected env-vars
// t.SetEnv does automatic cleanup / resets the values after the test
t.Setenv("ARM_CLIENT_ID_FILE_PATH", "")
t.Setenv("ARM_CLIENT_SECRET_FILE_PATH", "")

provider := AzureADProvider()
ctx := context.Background()
Expand All @@ -172,13 +188,84 @@ func TestAccProvider_clientSecretAuth(t *testing.T) {
t.Fatalf("configuring environment %q: %v", envName, err)
}

clientId, err := getClientId(d)
if err != nil {
return nil, diag.FromErr(err)
}

clientSecret, err := getClientSecret(d)
if err != nil {
return nil, diag.FromErr(err)
}

authConfig := &auth.Credentials{
Environment: *env,
TenantID: d.Get("tenant_id").(string),
ClientID: d.Get("client_id").(string),
ClientID: *clientId,

EnableAuthenticatingUsingClientSecret: true,
ClientSecret: *clientSecret,
}

return buildClient(ctx, provider, authConfig, "")
}

d := provider.Configure(ctx, terraform.NewResourceConfigRaw(nil))
if d != nil && d.HasError() {
t.Fatalf("err: %+v", d)
}

if errs := testCheckProvider(provider); len(errs) > 0 {
for _, err := range errs {
t.Error(err)
}
}
}

func testAccProvider_clientSecretAuthFromFiles(t *testing.T) {
if os.Getenv("TF_ACC") == "" {
t.Skip("TF_ACC not set")
}
if os.Getenv("ARM_CLIENT_ID_FILE_PATH") == "" {
t.Skip("ARM_CLIENT_ID_FILE_PATH not set")
}
if os.Getenv("ARM_CLIENT_SECRET_FILE_PATH") == "" {
t.Skip("ARM_CLIENT_SECRET_FILE_PATH not set")
}

// Ensure we are running using the expected env-vars
// t.SetEnv does automatic cleanup / resets the values after the test
t.Setenv("ARM_CLIENT_ID", "")
t.Setenv("ARM_CLIENT_SECRET", "")

provider := AzureADProvider()
ctx := context.Background()

// Support only client secret authentication
provider.ConfigureContextFunc = func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
envName := d.Get("environment").(string)
env, err := environments.FromName(envName)
if err != nil {
t.Fatalf("configuring environment %q: %v", envName, err)
}

clientId, err := getClientId(d)
if err != nil {
return nil, diag.FromErr(err)
}

clientSecret, err := getClientSecret(d)
if err != nil {
return nil, diag.FromErr(err)
}

authConfig := &auth.Credentials{
Environment: *env,
TenantID: d.Get("tenant_id").(string),
ClientID: *clientId,

EnableAuthenticatingUsingClientSecret: true,
ClientSecret: d.Get("client_secret").(string),
ClientSecret: *clientSecret,
}

return buildClient(ctx, provider, authConfig, "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,9 @@ func conditionalAccessPolicyResource() *schema.Resource {
},

"built_in_controls": {
Type: schema.TypeList,
Required: true,
Type: schema.TypeList,
Optional: true,
AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.terms_of_use"},
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{
Expand All @@ -414,8 +415,9 @@ func conditionalAccessPolicyResource() *schema.Resource {
},

"terms_of_use": {
Type: schema.TypeList,
Optional: true,
Type: schema.TypeList,
Optional: true,
AtLeastOneOf: []string{"grant_controls.0.built_in_controls", "grant_controls.0.terms_of_use"},
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateDiagFunc: validate.NoEmptyStrings,
Expand Down
Loading

0 comments on commit e7367ab

Please sign in to comment.