NetworkProfile.psm1

### --- PUBLIC FUNCTIONS --- ###
#Region - Get-NetworkProfile.ps1
Function Get-NetworkProfile {
    <#
    .SYNOPSIS
    Function to return network profiles from registry as Powershell objects
    .DESCRIPTION
    Query the Local Machine registry hive for current network profiles and return them as Powershell objects. Requires administrative privilege to read the hive.
    .PARAMETER ProfileName
    Return a specific network profile by name. Uses regex for matching.
    .NOTES
    Version: 1.0
    Author: C. Bodett
    Creation Date: 7/16/2024
    Purpose/Change: initial script development
    #>

    #Requires -RunAsAdministrator
    [CmdletBinding(DefaultParameterSetName="none")]
    Param (
        [Parameter(Mandatory=$false,Position=0,ParameterSetName="ProfileName")]
        [Alias("Name")]
        [Regex]$ProfileName,
        [Parameter(Mandatory=$false,Position=1,ParameterSetName="GUID")]
        [Guid]$GUID
    )

    $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles"

    $RegProfiles = switch ($PSCmdlet.ParameterSetName) {
        "ProfileName" {
            try {
                Get-ChildItem $RegPath -ErrorAction Stop | Where-Object {
                    $_.GetValue("ProfileName") -match $ProfileName.ToString()
                }
            } catch {
                throw $Error[0]
            }
        }
        "GUID" {
            try {
                $ProfilePath = Join-Path -Path $RegPath -ChildPath "{$($GUID.ToString())}"
                if (Test-Path $ProfilePath) {
                   Get-Item -Path $ProfilePath 
                } else {
                    Write-Warning "No network profile found matching GUID: {$($GUID.ToString())}"
                }
            } catch {
                throw $Error[0]
            }
        }
        "none" {
            try {
                Get-ChildItem $RegPath -ErrorAction Stop
            } catch {
                throw $Error[0]
            }
        }
    }

    Foreach ($Profile in $RegProfiles) {
        $Managed = Get-ItemPropertyValue -Path $(Join-Path -Path $RegPath -ChildPath $Profile.PSChildName) -Name "Managed" | Foreach-Object {
            if ($_ -eq 1) {
                "Yes"
            } else {
                "No"
            }
        }
        $Category = Get-ItemPropertyValue -Path $(Join-Path -Path $RegPath -ChildPath $Profile.PSChildName) -Name "Category" | Foreach-Object {
            switch ($_) {
                0 {"Public"}
                1 {"Private"}
                2 {"Domain"}
            }
        }
        $DateCreated = Convert-NetworkProfileTimestamp -RegTimeStamp $Profile.GetValue("DateCreated")
        $NameTypeValue = Get-ItemPropertyValue -Path $(Join-Path -Path $RegPath -ChildPath $Profile.PSChildName) -Name "NameType" | Foreach-Object {
            switch ($_) {
                6  {"Wired Network"}
                23  {"VPN"}
                53 {"VPN"}
                71  {"Wireless Network"}
                243  {"Mobile Broadband"}
            }
        }
        $DateLastConnected = Convert-NetworkProfileTimestamp -RegTimeStamp $Profile.GetValue("DateLastConnected")
        [PSCustomObject]@{
            PSTypeName          = "NetworkProfile"
            ProfileName         = $Profile.GetValue("ProfileName")
            Description         = $Profile.GetValue("Description")
            Managed             = $Managed
            Category            = $Category
            DateCreated         = $DateCreated
            NameType            = $NameTypeValue
            DateLastConnected   = $DateLastConnected
            GUID                = [GUID]$Profile.PSChildName
        }
    }
}
#Region - Remove-NetworkProfile.ps1
Function Remove-NetworkProfile {
    <#
    .SYNOPSIS
    Function to change the name, description or category of existing network profiles in registry
    .DESCRIPTION
    Function takes the unique ProfileName, GUID, or pipeline input of an existing network profile from registry and allows for updating the description, name and/or category of that profile.
    .PARAMETER ProfileName
    ProfileName to target for updating. Accepts pipeline input.
    .PARAMETER GUID
    Specify the GUID without braces for the network profile. Accepts pipeline input from Get-NetworkProfile
    .NOTES
    Version: 1.0
    Author: C. Bodett
    Creation Date: 7/16/2024
    Purpose/Change: initial script development
    #>

    #Requires -RunAsAdministrator
    [CmdletBinding(DefaultParameterSetName="ProfileName",SupportsShouldProcess,ConfirmImpact="High")]
    Param (
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ParameterSetName="ProfileName")]
        [Alias("Name")]
        [Regex]$ProfileName,
        [Parameter(Mandatory=$false,Position=1,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ParameterSetName="GUID")]
        [guid]$GUID
    )

    begin {
        $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles"
    }

    process {
        switch ($PSBoundParameters.Keys) {
            "GUID" {
                $ProfilePath = Join-Path -Path $RegPath -ChildPath "{$($GUID.ToString())}"
                if (Test-Path $ProfilePath) {
                    $NetworkProfile = Get-NetworkProfile -GUID $GUID
                } else {
                    Write-Warning "No network profile with GUID $GUID found"
                }
            }
            "ProfileName" {
                $NetworkProfile = Get-NetworkProfile -ProfileName $ProfileName
                $ProfilePath = Join-Path -Path $RegPath -ChildPath "{$($NetworkProfile.GUID)}"
                if ($NetworkProfile.count -gt 1) {
                    Write-Warning "Multiple network profiles match the name $ProfileName . Please use the -GUID parameter to specify a single network profile or find a more specific name."
                    $NetworkProfile = $null
                } elseif ($null -eq $NetworkProfile) {
                    Write-Warning "No network profile with name $ProfileName found"
                }                
            }
        }  
        
        if ($NetworkProfile) {
            Write-Verbose "Removing network profile: $($NetworkProfile.ProfileName) / $($NetworkProfile.GUID.ToString())"
            if ($PSCmdlet.ShouldProcess($ProfilePath,'Remove-Item')) {
                try {
                    Remove-Item -Path $ProfilePath
                } catch {
                    $Error[0]
                }
            }
        }
    }

    end {}
}
#Region - Set-NetworkProfile.ps1
Function Set-NetworkProfile {
    <#
    .SYNOPSIS
    Function to change the name, description or category of existing network profiles in registry
    .DESCRIPTION
    Function takes the unique ProfileName, GUID, or pipeline input of an existing network profile from registry and allows for updating the description, name and/or category of that profile.
    .PARAMETER ProfileName
    ProfileName to target for updating. Accepts pipeline input.
    .PARAMETER GUID
    Specify the GUID without braces for the network profile. Accepts pipeline input from Get-NetworkProfile
    .PARAMETER NewProfileName
    Specify the new profile name to change the network profile to
    .PARAMETER Description
    Change the description text for the network profile
    .PARAMETER Category
    Change the category for the network profile. Options are Private, Public or Domain
    .NOTES
    Version: 1.0
    Author: C. Bodett
    Creation Date: 7/16/2024
    Purpose/Change: initial script development
    #>

    #Requires -RunAsAdministrator
    [CmdletBinding(DefaultParameterSetName="ProfileName")]
    Param (
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ParameterSetName="ProfileName")]
        [Alias("Name")]
        [Regex]$ProfileName,
        [Parameter(Mandatory=$false,Position=1,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ParameterSetName="GUID")]
        [guid]$GUID,
        [Parameter(ParameterSetName="ProfileName")]
        [Parameter(ParameterSetName="GUID")]
        [String]$NewProfileName,
        [Parameter(ParameterSetName="ProfileName")]
        [Parameter(ParameterSetName="GUID")]
        [String]$Description,
        [Parameter(ParameterSetName="ProfileName")]
        [Parameter(ParameterSetName="GUID")]
        [ValidateSet("Private","Public","Domain")]
        [String]$Category
    )

    begin {
        $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles"
    }

    process {
        switch ($PSBoundParameters.Keys) {
            "GUID" {
                $ProfilePath = Join-Path -Path $RegPath -ChildPath "{$($GUID.ToString())}"
                if (Test-Path $ProfilePath) {
                    $NetworkProfile = Get-NetworkProfile -GUID $GUID
                } else {
                    Write-Warning "No network profile with GUID $GUID found"
                }
            }
            "ProfileName" {
                $NetworkProfile = Get-NetworkProfile -ProfileName $ProfileName
                $ProfilePath = Join-Path -Path $RegPath -ChildPath "{$($NetworkProfile.GUID)}"
                if ($NetworkProfile.count -gt 1) {
                    Write-Warning "Multiple network profiles match the name $ProfileName . Please use the -GUID parameter to specify a single network profile or find a more specific name."
                    $NetworkProfile = $null
                } elseif ($null -eq $NetworkProfile) {
                    Write-Warning "No network profile with name $ProfileName found"
                }                
            }
        }  
        
        if ($NetworkProfile) {
            Write-Verbose "Updating properties on ProfileName: $($NetworkProfile.ProfileName)"
            $ValuesToSet = switch ($PSBoundParameters.Keys) {
                "NewProfileName" {
                    [PSCustomObject]@{
                        Name = "ProfileName"
                        Value = $NewProfileName
                    }
                }
                "Description" {
                    [PSCustomObject]@{
                        Name = "Description"
                        Value = $Description
                    }
                }
                "Category" {
                    [PSCustomObject]@{
                        Name = "Category"
                        Value = switch ($Category) {
                            "Public" {0}
                            "Private" {1}
                            "Domain" {2}
                        }
                    }
                }
            }

            try {
                $ValuesToSet | Set-ItemProperty -Path $ProfilePath -Name {$_.Name} -ErrorAction Stop
            } catch {
                throw $Error[0]
            }
        }
    }

    end {}
}
### --- PRIVATE FUNCTIONS --- ###
#Region - Convert-NetworkProfileTimestamp.ps1
Function Convert-NetworkProfileTimestamp {
    Param (
        [Byte[]]$RegTimeStamp
    )

    $First = 1
    $Second = 0
    $Values = while ($First -lt 15) {
        if ($First -eq 5) {
            #the bytes in the 4th and 5th are the day of the week which we don't need
        } else {
            [uint32]$('0x{0:x}{1:x}' -f $RegTimeStamp[$First], $RegTimeStamp[$Second])
        }
        $First+=2
        $Second+=2
    }
    New-Object Datetime ($Values)
}