Skip to content

Feature scaffolding - added funcs for env var handling, doc updates and resource pretty print implementation #19

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

Merged
merged 7 commits into from
Jul 24, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!-- BEGIN_TF_DOCS -->
## Requirements

No requirements.

## Providers

| Name | Version |
|------|---------|
| <a name="provider_microsoft365"></a> [microsoft365](#provider\_microsoft365) | n/a |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [microsoft365_graph_beta_device_and_app_management_assignment_filter.example](https://registry.terraform.io/providers/hashicorp/microsoft365/latest/docs/resources/graph_beta_device_and_app_management_assignment_filter) | resource |

## Inputs

No inputs.

## Outputs

No outputs.
<!-- END_TF_DOCS -->
41 changes: 41 additions & 0 deletions examples/microsoft365_provider/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!-- BEGIN_TF_DOCS -->
## Requirements

No requirements.

## Providers

No providers.

## Modules

No modules.

## Resources

No resources.

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_auth_method"></a> [auth\_method](#input\_auth\_method) | The authentication method to use for the Entra ID application to authenticate the provider. Options: 'device\_code', 'client\_secret', 'client\_certificate', 'interactive\_browser', 'username\_password'. Can also be set using the `M365_AUTH_METHOD` environment variable. | `string` | `"client_secret"` | no |
| <a name="input_client_certificate_base64"></a> [client\_certificate\_base64](#input\_client\_certificate\_base64) | Base64 encoded PKCS#12 certificate bundle. For use when authenticating as a Service Principal using a Client Certificate. Can also be set using the `M365_CLIENT_CERTIFICATE_BASE64` environment variable. | `string` | n/a | yes |
| <a name="input_client_certificate_file_path"></a> [client\_certificate\_file\_path](#input\_client\_certificate\_file\_path) | The path to the Client Certificate associated with the Service Principal for use when authenticating as a Service Principal using a Client Certificate. Can also be set using the `M365_CLIENT_CERTIFICATE_FILE_PATH` environment variable. | `string` | n/a | yes |
| <a name="input_client_certificate_password"></a> [client\_certificate\_password](#input\_client\_certificate\_password) | The password associated with the Client Certificate. For use when authenticating as a Service Principal using a Client Certificate. Can also be set using the `M365_CLIENT_CERTIFICATE_PASSWORD` environment variable. | `string` | n/a | yes |
| <a name="input_client_id"></a> [client\_id](#input\_client\_id) | The client ID for the Entra ID application. This ID is generated when you register an application in the Entra ID (Azure AD) and can be found under App registrations > YourApp > Overview. Can also be set using the `M365_CLIENT_ID` environment variable. | `string` | n/a | yes |
| <a name="input_client_secret"></a> [client\_secret](#input\_client\_secret) | The client secret for the Entra ID application. This secret is generated in the Entra ID (Azure AD) and is required for authentication flows such as client credentials and on-behalf-of flows. It can be found under App registrations > YourApp > Certificates & secrets. Required for client credentials and on-behalf-of flows. Can also be set using the `M365_CLIENT_SECRET` environment variable. | `string` | n/a | yes |
| <a name="input_cloud"></a> [cloud](#input\_cloud) | The cloud to use for authentication and Graph / Graph Beta API requests. Default is `public`. Valid values are `public`, `gcc`, `gcchigh`, `china`, `dod`, `ex`, `rx`. Can also be set using the `M365_CLOUD` environment variable. | `string` | `"public"` | no |
| <a name="input_enable_chaos"></a> [enable\_chaos](#input\_enable\_chaos) | Enable the chaos handler for testing purposes. When enabled, the chaos handler can simulate specific failure scenarios and random errors in API responses to help test the robustness and resilience of the terraform provider against intermittent issues. This is particularly useful for testing how the provider handles various error conditions and ensures it can recover gracefully. Use with caution in production environments. Can also be set using the `M365_ENABLE_CHAOS` environment variable. | `bool` | `false` | no |
| <a name="input_password"></a> [password](#input\_password) | The password for username/password authentication. Can also be set using the `M365_PASSWORD` environment variable. | `string` | n/a | yes |
| <a name="input_proxy_url"></a> [proxy\_url](#input\_proxy\_url) | Specifies the URL of the HTTP proxy server. This URL should be in a valid URL format (e.g., 'http://proxy.example.com:8080'). When 'use\_proxy' is enabled, this URL is used to configure the HTTP client to route requests through the proxy. Ensure the proxy server is reachable and correctly configured to handle the network traffic. Can also be set using the `M365_PROXY_URL` environment variable. | `string` | n/a | yes |
| <a name="input_redirect_url"></a> [redirect\_url](#input\_redirect\_url) | The redirect URL for interactive browser authentication. Can also be set using the `M365_REDIRECT_URL` environment variable. | `string` | n/a | yes |
| <a name="input_telemetry_optout"></a> [telemetry\_optout](#input\_telemetry\_optout) | Flag to indicate whether to opt out of telemetry. Default is `false`. Can also be set using the `M365_TELEMETRY_OPTOUT` environment variable. | `bool` | `false` | no |
| <a name="input_tenant_id"></a> [tenant\_id](#input\_tenant\_id) | The M365 tenant ID for the Entra ID application. This ID uniquely identifies your Entra ID (EID) instance. It can be found in the Azure portal under Entra ID > Properties. Can also be set using the `M365_TENANT_ID` environment variable. | `string` | n/a | yes |
| <a name="input_use_proxy"></a> [use\_proxy](#input\_use\_proxy) | Enables the use of an HTTP proxy for network requests. When set to true, the provider will route all HTTP requests through the specified proxy server. This can be useful for environments that require proxy access for internet connectivity or for monitoring and logging HTTP traffic. Can also be set using the `M365_USE_PROXY` environment variable. | `bool` | `false` | no |
| <a name="input_username"></a> [username](#input\_username) | The username for username/password authentication. Can also be set using the `M365_USERNAME` environment variable. | `string` | n/a | yes |

## Outputs

No outputs.
<!-- END_TF_DOCS -->
55 changes: 55 additions & 0 deletions internal/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package client

import (
msgraphbetasdk "github.com/microsoftgraph/msgraph-beta-sdk-go"
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
)

// GraphClients encapsulates both the stable and beta GraphServiceClients
// provided by the Microsoft Graph SDKs. These clients are used to interact
// with the Microsoft Graph API and its beta endpoints, respectively.
//
// The stable client (StableClient) is used for making API calls to the
// stable Microsoft Graph endpoints, which are generally considered
// production-ready and have a higher level of reliability and support.
// The v1.0 endpoint of Microsoft Graph provides a stable and reliable API
// that is fully supported by Microsoft, ensuring that applications built
// on this endpoint have a solid foundation and offer the best possible
// user experience.
//
// The beta client (BetaClient) is used for making API calls to the
// beta Microsoft Graph endpoints, which allow developers to test and
// experiment with new features before they are released to the general public.
// However, it is important to note that the beta endpoint is not intended
// for use in production environments. APIs and functionalities available
// in the beta endpoint are subject to change, and features might be modified
// or removed without notice, potentially causing disruptions or breaking
// changes to applications. Additionally, the beta endpoint might not have
// the same level of support, reliability, or performance as the v1.0 endpoint,
// and can be unexpectedly unavailable or have slower response times.
//
// For these reasons, developers are strongly recommended to use the v1.0
// endpoint when building production applications to ensure stability,
// reliability, and a better user experience.
//
// Fields:
//
// StableClient (*msgraphsdk.GraphServiceClient): The client for interacting
// with the stable Microsoft Graph API, providing access to well-supported
// and reliable endpoints suitable for production use.
//
// BetaClient (*msgraphbetasdk.GraphServiceClient): The client for interacting
// with the beta Microsoft Graph API, providing access to new and experimental
// features that are subject to change and should be used with caution in
// production environments.
//
// Usage:
// The GraphClients struct is intended to be instantiated and configured by
// the provider during initialization, and then passed to the resources that
// need to interact with the Microsoft Graph API. This separation of stable
// and beta clients allows resources to choose the appropriate client based
// on the API features they require.
type GraphClients struct {
StableClient *msgraphsdk.GraphServiceClient
BetaClient *msgraphbetasdk.GraphServiceClient
}
32 changes: 27 additions & 5 deletions internal/helpers/variables.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,45 @@
package helpers

import "os"
import (
"context"
"os"

"github.com/hashicorp/terraform-plugin-log/tflog"
)

// GetEnvOrDefault fetches an environment variable value or returns a default if not set.
func GetEnvOrDefault(value, envKey string) string {
if value == "" {
func GetEnvOrDefault(ctx context.Context, value, envKey string) string {
if value == "" || value == "null" {
if envValue, exists := os.LookupEnv(envKey); exists {
tflog.Debug(ctx, "Using environment variable value", map[string]interface{}{
"envKey": envKey,
"value": envValue,
})
return envValue
}
}
tflog.Debug(ctx, "Using provided value", map[string]interface{}{
"envKey": envKey,
"value": value,
})
return value
}

// GetEnvOrDefaultBool fetches an environment variable value or returns a default if not set.
func GetEnvOrDefaultBool(value bool, envKey string) bool {
func GetEnvOrDefaultBool(ctx context.Context, value bool, envKey string) bool {
if !value {
if envValue, exists := os.LookupEnv(envKey); exists {
return envValue == "true"
result := envValue == "true"
tflog.Debug(ctx, "Using environment variable value", map[string]interface{}{
"envKey": envKey,
"value": result,
})
return result
}
}
tflog.Debug(ctx, "Using provided value", map[string]interface{}{
"envKey": envKey,
"value": value,
})
return value
}
73 changes: 44 additions & 29 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"regexp"

"github.com/deploymenttheory/terraform-provider-microsoft365/internal/client"
"github.com/deploymenttheory/terraform-provider-microsoft365/internal/helpers"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
Expand All @@ -22,12 +23,7 @@ var _ provider.Provider = &M365Provider{}
// M365Provider defines the provider implementation.
type M365Provider struct {
version string
clients *GraphClients
}

type GraphClients struct {
StableClient *msgraphsdk.GraphServiceClient
BetaClient *msgraphbetasdk.GraphServiceClient
clients *client.GraphClients
}

// M365ProviderModel describes the provider data model.
Expand Down Expand Up @@ -85,9 +81,9 @@ func (p *M365Provider) Schema(ctx context.Context, req provider.SchemaRequest, r
"This ID uniquely identifies your Entra ID (EID) instance. " +
"It can be found in the Azure portal under Entra ID > Properties. " +
"Can also be set using the `M365_TENANT_ID` environment variable.",
Validators: []validator.String{
validateGUID(),
},
// Validators: []validator.String{
// validateGUID(),
// },
},
"client_id": schema.StringAttribute{
Optional: true,
Expand All @@ -96,9 +92,9 @@ func (p *M365Provider) Schema(ctx context.Context, req provider.SchemaRequest, r
"This ID is generated when you register an application in the Entra ID (Azure AD) " +
"and can be found under App registrations > YourApp > Overview. " +
"Can also be set using the `M365_CLIENT_ID` environment variable.",
Validators: []validator.String{
validateGUID(),
},
// Validators: []validator.String{
// validateGUID(),
// },
},
"client_secret": schema.StringAttribute{
Optional: true,
Expand Down Expand Up @@ -228,21 +224,40 @@ func (p *M365Provider) Configure(ctx context.Context, req provider.ConfigureRequ
return
}

cloud := helpers.GetEnvOrDefault(data.Cloud.ValueString(), "M365_CLOUD")
authMethod := helpers.GetEnvOrDefault(data.AuthMethod.ValueString(), "M365_AUTH_METHOD")
tenantID := helpers.GetEnvOrDefault(data.TenantID.ValueString(), "M365_TENANT_ID")
clientID := helpers.GetEnvOrDefault(data.ClientID.ValueString(), "M365_CLIENT_ID")
clientSecret := helpers.GetEnvOrDefault(data.ClientSecret.ValueString(), "M365_CLIENT_SECRET")
clientCertificateBase64 := helpers.GetEnvOrDefault(data.ClientCertificateBase64.ValueString(), "M365_CLIENT_CERTIFICATE_BASE64")
clientCertificateFilePath := helpers.GetEnvOrDefault(data.ClientCertificateFilePath.ValueString(), "M365_CLIENT_CERTIFICATE_FILE_PATH")
clientCertificatePassword := helpers.GetEnvOrDefault(data.ClientCertificatePassword.ValueString(), "M365_CLIENT_CERTIFICATE_PASSWORD")
username := helpers.GetEnvOrDefault(data.Username.ValueString(), "M365_USERNAME")
password := helpers.GetEnvOrDefault(data.Password.ValueString(), "M365_PASSWORD")
redirectURL := helpers.GetEnvOrDefault(data.RedirectURL.ValueString(), "M365_REDIRECT_URL")
useProxy := helpers.GetEnvOrDefaultBool(data.UseProxy.ValueBool(), "M365_USE_PROXY")
proxyURL := helpers.GetEnvOrDefault(data.ProxyURL.ValueString(), "M365_PROXY_URL")
enableChaos := helpers.GetEnvOrDefaultBool(data.EnableChaos.ValueBool(), "M365_ENABLE_CHAOS")
telemetryOptout := helpers.GetEnvOrDefaultBool(data.TelemetryOptout.ValueBool(), "M365_TELEMETRY_OPTOUT")
cloud := helpers.GetEnvOrDefault(ctx, data.Cloud.ValueString(), "M365_CLOUD")
authMethod := helpers.GetEnvOrDefault(ctx, data.AuthMethod.ValueString(), "M365_AUTH_METHOD")
tenantID := helpers.GetEnvOrDefault(ctx, data.TenantID.ValueString(), "M365_TENANT_ID")
clientID := helpers.GetEnvOrDefault(ctx, data.ClientID.ValueString(), "M365_CLIENT_ID")
clientSecret := helpers.GetEnvOrDefault(ctx, data.ClientSecret.ValueString(), "M365_CLIENT_SECRET")
clientCertificateBase64 := helpers.GetEnvOrDefault(ctx, data.ClientCertificateBase64.ValueString(), "M365_CLIENT_CERTIFICATE_BASE64")
clientCertificateFilePath := helpers.GetEnvOrDefault(ctx, data.ClientCertificateFilePath.ValueString(), "M365_CLIENT_CERTIFICATE_FILE_PATH")
clientCertificatePassword := helpers.GetEnvOrDefault(ctx, data.ClientCertificatePassword.ValueString(), "M365_CLIENT_CERTIFICATE_PASSWORD")
username := helpers.GetEnvOrDefault(ctx, data.Username.ValueString(), "M365_USERNAME")
password := helpers.GetEnvOrDefault(ctx, data.Password.ValueString(), "M365_PASSWORD")
redirectURL := helpers.GetEnvOrDefault(ctx, data.RedirectURL.ValueString(), "M365_REDIRECT_URL")
useProxy := helpers.GetEnvOrDefaultBool(ctx, data.UseProxy.ValueBool(), "M365_USE_PROXY")
proxyURL := helpers.GetEnvOrDefault(ctx, data.ProxyURL.ValueString(), "M365_PROXY_URL")
enableChaos := helpers.GetEnvOrDefaultBool(ctx, data.EnableChaos.ValueBool(), "M365_ENABLE_CHAOS")
telemetryOptout := helpers.GetEnvOrDefaultBool(ctx, data.TelemetryOptout.ValueBool(), "M365_TELEMETRY_OPTOUT")

// Logging to verify environment variables are being used
tflog.Debug(ctx, "Configuration values", map[string]interface{}{
"cloud": cloud,
"authMethod": authMethod,
"tenantID": tenantID,
"clientID": clientID,
"clientSecret": clientSecret,
"clientCertificateBase64": clientCertificateBase64,
"clientCertificateFilePath": clientCertificateFilePath,
"clientCertificatePassword": clientCertificatePassword,
"username": username,
"password": password,
"redirectURL": redirectURL,
"useProxy": useProxy,
"proxyURL": proxyURL,
"enableChaos": enableChaos,
"telemetryOptout": telemetryOptout,
})

ctx = tflog.SetField(ctx, "cloud", cloud)
ctx = tflog.SetField(ctx, "auth_method", authMethod)
Expand Down Expand Up @@ -345,7 +360,7 @@ func (p *M365Provider) Configure(ctx context.Context, req provider.ConfigureRequ
stableAdapter.SetBaseUrl(graphServiceRoot)
betaAdapter.SetBaseUrl(graphBetaServiceRoot)

clients := &GraphClients{
clients := &client.GraphClients{
StableClient: msgraphsdk.NewGraphServiceClient(stableAdapter),
BetaClient: msgraphbetasdk.NewGraphServiceClient(betaAdapter),
}
Expand All @@ -361,7 +376,7 @@ func New(version string) func() provider.Provider {
return func() provider.Provider {
return &M365Provider{
version: version,
clients: &GraphClients{},
clients: &client.GraphClients{},
}
}
}

This file was deleted.

Loading
Loading