TenantToolkit.psm1

function Connect-Graph {
  [CmdletBinding(DefaultParameterSetName = 'Interactive')]
  param(
    [Parameter(Mandatory, ParameterSetName = 'App')]
    [Parameter(Mandatory, ParameterSetName = 'Certificate')]
    [string]$ClientId,
    [Parameter(Mandatory, ParameterSetName = 'App')]
    [Parameter(Mandatory, ParameterSetName = 'Certificate')]
    [string]$TenantId,
    [Parameter(Mandatory, ParameterSetName = 'App')]
    [securestring]$ClientSecret,
    [Parameter(Mandatory, ParameterSetName = 'Certificate')]
    [string]$CertificateThumbprint,
    [Parameter(ParameterSetName = 'Interactive')]
    [switch]$Interactive,
    [Parameter()]
    [string[]]$Scopes = @("User.ReadWrite.All", "Group.ReadWrite.All", "Directory.ReadWrite.All"),
    # Passthrough parameters for Connect-MgGraph
    [Parameter()][string]$AccessToken,
    [Parameter()][switch]$Break,
    [Parameter()][X509Certificate2]$Certificate,
    [Parameter()][string]$CertificateSubjectName,
    [Parameter()][PSCredential]$ClientSecretCredential,
    [Parameter()][double]$ClientTimeout,
    [Parameter()][ContextScope]$ContextScope,
    [Parameter()][string]$Environment,
    [Parameter()][string]$EnvironmentVariable,
    [Parameter()][PSCredential]$Identity,
    [Parameter()][switch]$NoWelcome,
    [Parameter()][switch]$SendCertificateChain,
    [Parameter()][switch]$UseDeviceCode
  )
  begin {
     # Reference unused parameters to satisfy PSScriptAnalyzer
     $null = $Interactive; $null = $AccessToken; $null = $Break; $null = $Certificate
     $null = $CertificateSubjectName; $null = $ClientSecretCredential; $null = $ClientTimeout
     $null = $ContextScope; $null = $Environment; $null = $EnvironmentVariable
     $null = $Identity; $null = $NoWelcome; $null = $SendCertificateChain; $null = $UseDeviceCode
  }
  process {
    try {
      switch ($PSCmdlet.ParameterSetName) {
        'Interactive' {
          Write-PSFMessage -Level Verbose -Message "Initiating interactive authentication..."
          Connect-MgGraph -Scopes $Scopes -ErrorAction Stop
          Write-PSFMessage -Level Important -Tag 'Graph.Authentication' -Message "Connected interactively to Microsoft Graph. Scopes: $($Scopes -join ', ')"
        }
        'App' {
          Write-PSFMessage -Level Verbose -Message "Connecting using application credentials..."
          $clientSecretCredential = New-Object System.Management.Automation.PSCredential($ClientId, $ClientSecret)
          Connect-MgGraph -ClientId $ClientId -TenantId $TenantId -ClientSecretCredential $clientSecretCredential -ErrorAction Stop
          Write-PSFMessage -Level Important -Tag 'Graph.Authentication' -Message "Connected using App credentials. Tenant: $TenantId, ClientId: $ClientId"
        }
        'Certificate' {
          Write-PSFMessage -Level Verbose -Message "Connecting using certificate authentication..."
          Connect-MgGraph -ClientId $ClientId -TenantId $TenantId -CertificateThumbprint $CertificateThumbprint -ErrorAction Stop
          Write-PSFMessage -Level Important -Tag 'Graph.Authentication' -Message "Connected using Certificate. Tenant: $TenantId, Thumbprint: $CertificateThumbprint"
        }
      }
    } catch {
      Write-PSFMessage -Level Critical -Message "Failed to connect to Microsoft Graph: $_"
      throw $_
      }
  }
  end {
    Write-PSFMessage -Level Info -Message "Connect-Graph exectued successfully."
  }
}

function Connect-Graph {
  <#
  .SYNOPSIS
      Authenticates to Microsoft Graph API.
  .DESCRIPTION
      This function supports interactive authentication, app-based authentication (using ClientId and ClientSecret), and certificate-based authentication.
  .PARAMETER ClientId
      Application (Client) ID for app-based or certificate-based authentication.
  .PARAMETER TenantId
      Tenant (Directory) ID for the Microsoft 365 environment.
  .PARAMETER ClientSecret
      Client secret for app-based authentication (SecureString).
  .PARAMETER CertificateThumbprint
      Thumbprint of the installed certificate for authentication.
  .PARAMETER Interactive
      Switch to explicitly perform interactive authentication.
  .PARAMETER Scopes
      Microsoft Graph scopes required for authentication.
  .EXAMPLE
      Connect-Graph -Interactive
      Explicitly performs interactive authentication.
  .PARAMETER AccessToken
    Token to use directly instead of prompting.
  .PARAMETER Break
      Trigger a breakpoint on startup.
  .PARAMETER Certificate
      X509 certificate object for auth.
  .PARAMETER CertificateSubjectName
      Subject name of the certificate to select.
  .PARAMETER ClientSecretCredential
      PSCredential containing client secret.
  .PARAMETER ClientTimeout
      Timeout in seconds for Graph connection.
  .PARAMETER ContextScope
      PSFramework context scope.
  .PARAMETER Environment
      Azure environment to target.
  .PARAMETER EnvironmentVariable
      Name of environment variable containing the token.
  .PARAMETER Identity
      PSCredential or account identity to use.
  .PARAMETER NoWelcome
      Suppress the welcome banner.
  .PARAMETER SendCertificateChain
      Include the entire certificate chain in auth.
  .PARAMETER UseDeviceCode
      Use device-code auth flow.
  .EXAMPLE
      $secret = ConvertTo-SecureString "my-secret" -AsPlainText -Force
      Connect-Graph -ClientId "xxxx-xxxx" -TenantId "xxxx-xxxx" -ClientSecret $secret
      Authenticate using App credentials.
  .EXAMPLE
      Connect-Graph -ClientId "xxxx-xxxx" -TenantId "xxxx-xxxx" -CertificateThumbprint "ABCD1234..."
      Authenticate using certificate authentication.
  .EXAMPLE
      Connect-Graph -Interactive
      Performs an interactive login prompt.
  .EXAMPLE
      $cred = Get-Credential
      Connect-Graph -ClientId '...' -TenantId '...' -ClientSecretCredential $cred
      Auth via app+secret.
  .NOTES
      Requires Microsoft.Graph module.
  #>

  [CmdletBinding(DefaultParameterSetName = 'Interactive')]
  param(
    [Parameter(Mandatory, ParameterSetName = 'App')]
    [Parameter(Mandatory, ParameterSetName = 'Certificate')]
    [string]$ClientId,
    [Parameter(Mandatory, ParameterSetName = 'App')]
    [Parameter(Mandatory, ParameterSetName = 'Certificate')]
    [string]$TenantId,
    [Parameter(Mandatory, ParameterSetName = 'App')]
    [securestring]$ClientSecret,
    [Parameter(Mandatory, ParameterSetName = 'Certificate')]
    [string]$CertificateThumbprint,
    [Parameter(ParameterSetName = 'Interactive')]
    [switch]$Interactive,
    [Parameter()]
    [string[]]$Scopes = @("User.ReadWrite.All", "Group.ReadWrite.All", "Directory.ReadWrite.All"),
    # Passthrough parameters for Connect-MgGraph
    [Parameter()][string]$AccessToken,
    [Parameter()][switch]$Break,
    [Parameter()][X509Certificate2]$Certificate,
    [Parameter()][string]$CertificateSubjectName,
    [Parameter()][double]$ClientTimeout,
    [Parameter()][ContextScope]$ContextScope,
    [Parameter()][string]$Environment,
    [Parameter()][string]$EnvironmentVariable,
    [Parameter()][PSCredential]$Identity,
    [Parameter()][switch]$NoWelcome,
    [Parameter()][switch]$SendCertificateChain,
    [Parameter()][switch]$UseDeviceCode
  )
    # Skip logic only when no parameters bound and context already exists
    if ((Get-MgContext) -and ($PSBoundParameters.Keys.Count -eq 0)) {
        Write-PSFMessage -Level Verbose -Message 'Already connected to Microsoft Graph.'
        return
    }
    # Delegate to internal implementation
    Connect-GraphInternal @PSBoundParameters
}
# Ensure the function is exported
Export-ModuleMember -Function Connect-Graph