Public/Wifi/Connect-WifiNetwork.ps1

Function Connect-WiFiNetwork {
  <#
.SYNOPSIS
  Connects one, multiple, or all WiFi adapters to a specific network (SSID).
.DESCRIPTION
  This function uses the WinRT API to connect. It supports simultaneously targeting specific adapters by their index. If a network is already known to Windows (profile exists), the password is not required.
.PARAMETER Ssid
  Mandatory The name of the WiFi network. Use the TAB key for suggestions of available nearby networks.
.PARAMETER Password
  Optional The password for the network. This is a dynamic parameter that uses SecureString (unless -AsPlainText is specified). Not required if a stored profile already exists.
.PARAMETER Index
  Optional The index of the adapter(s) (e.g., 0, 1). Leave this blank to attempt the connection on ALL available WiFi adapters simultaneously.
.PARAMETER User
  Optional username for enterprise networks (802.1X).
.PARAMETER Bssid
  Optional Force connection to a specific Access Point based on the MAC address.
.EXAMPLE
  Connect-WiFiNetwork -Ssid "MyHomeNetwork" -Index 0
  Connects the first adapter to the specified network.
.EXAMPLE
  Connect-WiFiNetwork -Ssid "Open_WiFi"
  Attempts to connect all WiFi adapters on the system to an open network.
.EXAMPLE
  Connect-WiFiNetwork -Ssid "Company_WiFi" -Password (Read-Host -AsSecureString) -Index 0, 2
  Connects adapter 0 and 2 to a secured network with manual password input.
.NOTES
  The function checks each adapter to ensure the SSID is visible before initiating the connection.
#>

  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)]$Ssid, 
    [string]$Bssid,
    [string]$User,
    [Switch]$AsPlainText,
    [Switch]$Hidden,
    [int[]]$Index,
    [Int]$TimeoutMS = 120000,
    [Windows.Devices.WiFi.WiFiReconnectionKind]$WifiReconnectionKind = [Windows.Devices.WiFi.WiFiReconnectionKind]::Automatic
  )
  DynamicParam {
    # Choice type depending on Switch
    $parameterType = if ($AsPlainText) { [string] } else { [securestring] }
    # Create the attributeCollection for all attributes like Mandatory
    $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
    $attribute = New-Object System.Management.Automation.ParameterAttribute
    $attribute.Mandatory = $false
    $attributeCollection.Add($attribute)
    # Define the 'Password' paramter at runtime
    $dynParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Password', $parameterType, $attributeCollection)
    # Add to the Dictionary wich powreshell expects to receive
    $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    $paramDictionary.Add('Password', $dynParam)
    return $paramDictionary
  }
  Process {
    # --- 1. PERMISSIE & RADIO CHECK ---
    if ((Request-LocationPermission) -ne 'Allowed') { return } 
    
    $Adapters = Get-WiFiAvailableAdapter
    if (-not $Adapters) { throw "No active WiFi-adapter found." }

    # Radio check (Simpeler geschreven)
    $Radios = Get-RadioState | Where-Object Kind -eq 'WiFi'
    if ($Radios.Count -gt 0 -and -not ($Radios | Where-Object State -eq 'On')) {
      throw "Error: All WiFi radios are OFF. Turn them on first."
    }

    # --- 2. Prepair Credentials ---
    $Pass = $PSBoundParameters['Password']
    $Creds = [Windows.Security.Credentials.PasswordCredential]::new()
    if ($null -ne $Pass) {
      if ($AsPlainText) { $Creds.Password = $Pass }
      else { $Creds.Password = ([System.Net.NetworkCredential]::new("", $Pass)).Password }
    }
    if ($User) { $Creds.UserName = $User }

    # --- 3. Getting the adapters you want to control ---
    $Targets = if ($PSBoundParameters.ContainsKey('Index')) { $Index | ForEach-Object { $Adapters[$_] } } else { $Adapters }

    # --- 4. Connection Loop ---
    $Targets | ForEach-Object {  
      $currentId = $_.NetworkAdapter.NetworkAdapterId
      $name = (Get-NetAdapter | Where-Object DeviceID -match $currentId).InterfaceDescription
      
      # Search the network specifick for this
      $Net = $_.NetworkReport.AvailableNetworks | Where-Object { if ($Bssid) { $_.Bssid -eq $Bssid } else { $_.Ssid -eq $Ssid } } | Sort-Object NetworkRssiInDecibelMilliwatts -Descending | Select-Object -First 1

      if (-not $Net -and -not $Hidden) {
        # Force Scan if not found
        $null = Search-WifiNetworksPrivate
        $Net = $_.NetworkReport.AvailableNetworks | Where-Object { if ($Bssid) { $_.Bssid -eq $Bssid } else { $_.Ssid -eq $Ssid } } | Select-Object -First 1
      }

      if (-not $Net) { Write-Warning "Adapter $($name): SSID '$($Ssid)' not found."; return }

      Write-Host "Connecting $name to $Ssid..." -ForegroundColor Cyan
      $_.Disconnect()
      $_Ssid = $null
      if ($Hidden) {
        $_Ssid = $Ssid  # Pass the SSID if it's a hidden network
      }
      $Task = $_.ConnectAsync($Net, $WifiReconnectionKind, $Creds, $_Ssid)
      $Res = AwaitWithCancel -WinRtTask $Task -ResultType ([Windows.Devices.WiFi.WiFiConnectionResult]) -TimeoutMs $TimeoutMS -Activity "Connecting $Ssid"

      # Output
      [PSCustomObject]@{
        ConnectionStatus = $Res.ConnectionStatus
        AdapterName      = $name
        Ssid             = $Ssid
        Bssid            = $Net.Bssid
      }
    }
  }
}
Register-ArgumentCompleter -CommandName 'Connect-WiFiNetwork' -ParameterName 'Index' -ScriptBlock $ScriptBlockArgumentCompleterIndex
Register-ArgumentCompleter -CommandName 'Connect-WiFiNetwork' -ParameterName 'Bssid' -ScriptBlock $ScriptBlockArgumentCompleterBssid
Register-ArgumentCompleter -CommandName 'Connect-WiFiNetwork' -ParameterName 'Ssid' -ScriptBlock $ScriptBlockArgumentCompleterSsid
Register-ArgumentCompleter -CommandName 'Connect-wifiNetwork' -ParameterName 'WifiReconnectionKind' -ScriptBlock $ScriptBlockArgumentCompleterWifiReconnectionKind