Public/Auth/Connect-DataverseEnvironment.ps1
|
function Connect-DataverseEnvironment { <# .SYNOPSIS Connects to a Dataverse environment for plugin deployment operations. .DESCRIPTION Establishes an authenticated connection to a Dataverse environment using the authentication hierarchy: 1. Explicit parameters (ConnectionString or SPN credentials) 2. Environment variables (from .env file) 3. Interactive device code flow (if -Interactive specified) .PARAMETER ConnectionString Full connection string for Dataverse (legacy support - extracts URL and uses SPN auth). .PARAMETER EnvironmentUrl The Dataverse environment URL (e.g., https://myorg.crm.dynamics.com). .PARAMETER ClientId Azure AD application (client) ID for service principal authentication. .PARAMETER ClientSecret Client secret for service principal authentication. .PARAMETER TenantId Azure AD tenant ID. .PARAMETER EnvFile Path to .env file with credentials. .PARAMETER Interactive Use interactive device code flow for authentication. .EXAMPLE Connect-DataverseEnvironment -EnvironmentUrl "https://myorg.crm.dynamics.com" -Interactive Connects using device code flow (opens browser for authentication). .EXAMPLE Connect-DataverseEnvironment -ClientId $id -ClientSecret $secret -TenantId $tenant -EnvironmentUrl $url Connects using service principal credentials. .OUTPUTS DataverseConnection object with access token and environment information. #> [CmdletBinding()] param( [Parameter()] [string]$ConnectionString, [Parameter()] [string]$EnvironmentUrl, [Parameter()] [string]$ClientId, [Parameter()] [string]$ClientSecret, [Parameter()] [string]$TenantId, [Parameter()] [string]$EnvFile, [Parameter()] [switch]$Interactive ) # Load environment file if specified or by default if (-not [string]::IsNullOrWhiteSpace($EnvFile)) { Import-EnvFile -Path $EnvFile | Out-Null } elseif ([string]::IsNullOrWhiteSpace($ConnectionString) -and [string]::IsNullOrWhiteSpace($ClientId)) { Import-EnvFile | Out-Null } # Determine authentication method and parameters $authMethod = $null $finalUrl = $null $finalClientId = $null $finalClientSecret = $null $finalTenantId = $null # Priority 1: Explicit connection string (parse it for credentials) if (-not [string]::IsNullOrWhiteSpace($ConnectionString)) { # Use case-insensitive hashtable for connection string keys $parsed = [System.Collections.Generic.Dictionary[string,string]]::new([System.StringComparer]::OrdinalIgnoreCase) $ConnectionString -split ";" | ForEach-Object { $parts = $_ -split "=", 2 if ($parts.Count -eq 2) { $parsed[$parts[0].Trim()] = $parts[1].Trim() } } $finalUrl = $parsed["Url"] if ($parsed["AuthType"] -eq "ClientSecret") { $finalClientId = $parsed["ClientId"] $finalClientSecret = $parsed["ClientSecret"] # Try to extract tenant from URL or use common $finalTenantId = if ($TenantId) { $TenantId } else { "organizations" } $authMethod = "Connection String (Service Principal)" } elseif ($parsed["AuthType"] -eq "OAuth") { # OAuth connection string - use interactive $Interactive = $true $authMethod = "Connection String (Interactive)" } else { throw "Unsupported AuthType in connection string. Supported: ClientSecret, OAuth" } } # Priority 2: Explicit SPN parameters elseif (-not [string]::IsNullOrWhiteSpace($ClientId) -and -not [string]::IsNullOrWhiteSpace($ClientSecret) -and -not [string]::IsNullOrWhiteSpace($TenantId)) { $finalUrl = if (-not [string]::IsNullOrWhiteSpace($EnvironmentUrl)) { $EnvironmentUrl } else { Get-EnvVar "DATAVERSE_URL" } if ([string]::IsNullOrWhiteSpace($finalUrl)) { throw "EnvironmentUrl required when using service principal parameters" } $finalClientId = $ClientId $finalClientSecret = $ClientSecret $finalTenantId = $TenantId $authMethod = "Service Principal (Parameters)" } # Priority 3: Environment variables for SPN else { $envUrl = Get-EnvVar "DATAVERSE_URL" $envClientId = Get-EnvVar "SP_APPLICATION_ID" $envClientSecret = Get-EnvVar "SP_CLIENT_SECRET" $envTenantId = Get-EnvVar "SP_TENANT_ID" $finalUrl = if (-not [string]::IsNullOrWhiteSpace($EnvironmentUrl)) { $EnvironmentUrl } else { $envUrl } if (-not [string]::IsNullOrWhiteSpace($envClientId) -and -not [string]::IsNullOrWhiteSpace($envClientSecret) -and -not [string]::IsNullOrWhiteSpace($finalUrl)) { $finalClientId = $envClientId $finalClientSecret = $envClientSecret $finalTenantId = if ($envTenantId) { $envTenantId } else { "organizations" } $authMethod = "Service Principal (Environment)" } # Priority 4: Interactive device code flow elseif ($Interactive) { if ([string]::IsNullOrWhiteSpace($finalUrl)) { throw "EnvironmentUrl required for interactive authentication" } $authMethod = "Interactive (Device Code)" } else { Write-LogError "No authentication method available." Write-Log "Options:" Write-Log " 1. Provide -ConnectionString parameter" Write-Log " 2. Provide -ClientId, -ClientSecret, -TenantId, -EnvironmentUrl parameters" Write-Log " 3. Create .env.dev file with SP_APPLICATION_ID, SP_CLIENT_SECRET, SP_TENANT_ID, DATAVERSE_URL" Write-Log " 4. Use -Interactive flag for device code authentication" throw "No authentication credentials available" } } # Log connection attempt Write-Log "Auth method: $authMethod" Write-Log "Environment: $finalUrl" # Connect using appropriate method try { if ($authMethod -eq "Interactive (Device Code)" -or $authMethod -eq "Connection String (Interactive)") { Write-Log "Starting interactive authentication..." $tenantForInteractive = if ($finalTenantId) { $finalTenantId } else { "organizations" } $connection = New-DataverseConnectionInteractive -EnvironmentUrl $finalUrl -TenantId $tenantForInteractive } else { Write-Log "Connecting to Dataverse..." $connection = New-DataverseConnection -EnvironmentUrl $finalUrl -TenantId $finalTenantId -ClientId $finalClientId -ClientSecret $finalClientSecret } if (-not $connection -or -not $connection.IsReady) { throw "Connection failed - IsReady is false" } Write-LogSuccess "Connected to: $($connection.ConnectedOrgFriendlyName)" return $connection } catch { Write-LogError "Connection failed: $($_.Exception.Message)" throw } } |