NN.MrfkCommands.psm1

#Region './Private/Get-CompInf.ps1' 0
function Get-CompInf {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)][array]$allMecmComputers,
        [Parameter(Mandatory)][array]$allCpuInfo,
        [Parameter(Mandatory)][array]$allAdComputers,
        [Parameter(Mandatory)][array]$allModelInfo,
        [Parameter(Mandatory)][string]$hostname
    )
    
    process {
        $notes = New-Object -TypeName System.Collections.ArrayList
        $mecmComputer = $allMecmComputers.where({$_.Name -eq $hostname})
        $cpuInfo = $allCpuInfo.where({$_.SystemName -eq $hostname})
        $adComputer = $allAdComputers.where({$_.Name -eq $hostname})
        $modelInfo = $allModelInfo.where({$_.ResourceID -eq $mecmComputer.ResourceId})

        if ($mecmComputer.agentname) {
            $heartbeatIndex = $mecmComputer.agentname.IndexOf("Heartbeat Discovery")
            $lastHeartbeat = $mecmComputer.agenttime[$heartbeatIndex]
        }

        [PSCustomObject]@{
            Hostname          = $mecmComputer.Name
            Username          = $mecmComputer.LastLogonUserName
            MACAddress        = $mecmComputer.MACAddresses
            Model             = $modelInfo.Name
            SN                = $modelInfo.IdentifyingNumber
            GUID              = $mecmComputer.SMBIOSGUID
            CreatedAt         = $mecmComputer.CreationDate
            DeviceOSBuild     = $mecmComputer.BuildExt
            OS                = $mecmComputer.OperatingSystemNameandVersion
            "Enabled(AD)"     = $adComputer.Enabled
            LastHeartbeat     = $lastHeartbeat
            CpuName           = $cpuInfo.Name
            Notes             = $notes
        }
    }
}
#EndRegion './Private/Get-CompInf.ps1' 40
#Region './Private/Register-Autofill.ps1' 0
$Splat = @{
    "CommandName" = "New-ShippingLabel"
    "ParameterName" = "location"
    "ScriptBlock" = {
        param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)

        $AllLocationNames = (Get-SnipeLocations).name | Sort-Object
        $AllLocationNames.Where({
            $_ -like "$wordToComplete*"
        }).ForEach({
            "`"$_`""
        })
    }
}
Register-ArgumentCompleter @Splat
#EndRegion './Private/Register-Autofill.ps1' 16
#Region './Public/Disable-OutlookElements.ps1' 0
function Disable-OutlookElements {
    param (
    [Parameter(Mandatory=$true)][string]$hostname
    )
    $tnc = tnc $hostname
    if ($tnc.PingSucceeded) {
        if ((Invoke-command -computername $hostname {Get-ItemProperty 'HKLM:\Software\Microsoft\Office\Outlook\Addins\Gecko.Ephorte.Outlook.Main' -Name LoadBehavior}) -eq 0) {
            "Outlook Elements is already deactivated on $hostname"
        } else {

            $response = Invoke-command -computername $hostname {
                New-Item 'HKLM:\Software\Microsoft\Office\Outlook\Addins\Gecko.Ephorte.Outlook.Main' -Force | Out-Null
                (New-ItemProperty 'HKLM:\Software\Microsoft\Office\Outlook\Addins\Gecko.Ephorte.Outlook.Main' -Name 'LoadBehavior' -Value 0 -PropertyType dword).LoadBehavior
            }

            if ($response -eq 0) {
                "Success"
            } else {
                $response
                "Failed"
            }
        }
    } else {
        "Computer is offline"
    }
}
#EndRegion './Public/Disable-OutlookElements.ps1' 27
#Region './Public/Get-AdmCreds.ps1' 0
function Get-AdmCreds {
    param (
        [string]$admCredsPath = "$env:USERPROFILE\.creds\Windows\adm_creds.xml"
    )

    if (!(Test-Path $admCredsPath)) {
        #Create directories for adm_creds
        $admCredsDir = $admCredsPath.Substring(0, $admCredsPath.lastIndexOf('\'))
        if (!(Test-Path $admCredsDir)) {
            New-Item -ItemType Directory $admCredsDir | Out-Null
        }
        
        #Create adm_creds file
        Get-Credential -Message "Enter admin credentials" | Export-Clixml $admCredsPath
    } else {
        Import-Clixml $admCredsPath
    }
}
#EndRegion './Public/Get-AdmCreds.ps1' 19
#Region './Public/Get-Computer.ps1' 0
function Get-Computer {
    [CmdletBinding(DefaultParameterSetName="List computers")]
    param (
        [Parameter(Mandatory,ValueFromPipeline,Position=0,ParameterSetName="Get computer by hostname")][string]$hostname,
        [pscredential]$credential,
        [Parameter(ParameterSetName="List computers")][switch]$ListComputers,
        [string]$OutCsvPath
    )
    
    begin {
        $allCpuInfoQry = @"
select distinct SMS_G_System_PROCESSOR.*
from SMS_R_System
inner join SMS_G_System_PROCESSOR
on SMS_G_System_PROCESSOR.ResourceID = SMS_R_System.ResourceId
"@

        Write-Information "$(Get-Date -Format "hh:mm:ss") Creating CimSession"
        $cimsession = New-CimSession -Credential $credential -ComputerName sccm-ps.intern.mrfylke.no -ErrorAction Stop

        Write-Information "$(Get-Date -Format "hh:mm:ss") Fetching all AD computers"
        $allAdComputers = Get-ADComputer -Filter *
        Write-Information "$(Get-Date -Format "hh:mm:ss") Fetching MECM computers"
        $allMecmComputers = Get-CimInstance -Query "Select * from SMS_R_System" -Namespace root/SMS/site_PS1 -CimSession $cimsession
        Write-Information "$(Get-Date -Format "hh:mm:ss") Fetching all CPU info"
        $allCpuInfo = Get-CimInstance -Query $allCpuInfoQry -Namespace root/SMS/site_PS1 -CimSession $cimsession
        Write-Information "$(Get-Date -Format "hh:mm:ss") Fetching all model info"
        $allModelInfo = Get-CimInstance -Query "Select * from SMS_G_System_Computer_System_Product" -Namespace root/SMS/site_PS1 -CimSession $cimsession
    
        $compInfExport = New-Object -TypeName System.Collections.ArrayList
    }
    
    process {
        switch ($PsCmdlet.ParameterSetName) {
            "List computers" {
                $CompInfDef = ${function:Get-CompInf}.ToString()

                $compInfExport = $allMecmComputers | ForEach-Object -ThrottleLimit 5000 -Parallel {
                    ${function:Get-CompInf} = $using:CompInfDef
                    Write-Information "$(Get-Date -Format "hh:mm:ss") Fetching CompInf for `"$($_.Name)`""

                    $CompInfSplat = @{
                        "allMecmComputers" = $using:allMecmComputers
                        "allCpuInfo" = $using:allCpuInfo
                        "allAdComputers" = $using:allAdComputers
                        "allModelInfo" = $using:allModelInfo
                        "hostname" = $_.Name
                    }
                    Get-CompInf @CompInfSplat
                    Write-Information "$(Get-Date -Format "hh:mm:ss") Completed fetching info for computer `"$($_.Name)`""
                }
            }
            "Get computer by hostname" {
                $CompInfSplat = @{
                    "allMecmComputers" = $allMecmComputers
                    "allCpuInfo" = $allCpuInfo
                    "allAdComputers" = $allAdComputers
                    "allModelInfo" = $allModelInfo
                    "hostname" = $hostname
                }
                $currentCompInf = Get-CompInf @CompInfSplat
                $null = $compInfExport.Add($currentCompInf)
            }
        }
    }

    end {
        if ($OutCsvPath) {
            $compInfExport | Export-Csv $OutCsvPath
        } else {
            $compInfExport
        }
    }
}
#EndRegion './Public/Get-Computer.ps1' 74
#Region './Public/Get-FreeHostnames.ps1' 0
function Get-FreeHostnames {
    param (
        [Parameter(Mandatory,HelpMessage="Example inputs: `"LT-SADM-`" or `"PC-SADM-`"")][ValidateScript({
            $_ -like "*-*-"
        }, ErrorMessage="Please provide a valid input. Valid inputs are `"<device-type>-<locationname>-`" Example: `"LT-SADM-`" or `"PC-VROM-`""
        )][string]$filter,
        [pscredential]$credential,
        [int]$count
    )
    $cimsession = New-CimSession -Credential $credential -ComputerName sccm-ps.intern.mrfylke.no -ErrorAction Stop
    $allMecmComputers = Get-CimInstance -Query "Select * from SMS_R_System" -Namespace root/SMS/site_PS1 -CimSession $cimsession
    
    $pcNumArr = $allMecmComputers.where({$_.Name -like "*$filter*"}).Name.Replace("$filter","","OrdinalIgnoreCase") -replace "^0+","" | Sort-Object
    $freePcNumArr = (1..999).where({$_ -notin $pcNumArr})

    if ($count) {
        $freePcNumArr = $freePcNumArr | Select-Object -First $count
    }
    
    $freePcNumArr.ForEach({
        $num = ([string]$_).PadLeft(3,'0')
        "$filter$num"
    })
}
#todo: add $HostnamePrefix="PC","TB","LT" and $LocationName="SADM","VROM",etc.
#todo: Get all locaiton shorts from hostnames currently in MECM and validatescript
#EndRegion './Public/Get-FreeHostnames.ps1' 27
#Region './Public/Invoke-Camfix.ps1' 0
function Invoke-Camfix {
    param (
        [Parameter(Mandatory=$true)][string]$hostname
    )
    $tnc = tnc $hostname
    if ($tnc.PingSucceeded) {
        if (Invoke-command -computername $hostname -ScriptBlock {Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows Media Foundation\Platform\' -Name EnableFrameServerMode}) {
            "Camfix is already installed on $hostname"
        } else {
            $response = Invoke-command -computername $hostname -ScriptBlock {(New-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows Media Foundation\Platform\' -Name 'EnableFrameServerMode' -Value 0 -PropertyType dword).EnableFrameServerMode}
            if ($response -eq 0) {
                "Success"
            } else {
                $response
                "Failed"
            }
        }
    } else {
        "Computer is offline"
    }
}
#EndRegion './Public/Invoke-Camfix.ps1' 22
#Region './Public/Invoke-PCCheck.ps1' 0
function Invoke-PCCheck {
    param (
        [Parameter(Mandatory)][string]$displayName
    )
    $notes = New-Object -TypeName System.Collections.ArrayList

    # Check if there is a active EXO sessions
    $psSessions = Get-PSSession | Select-Object -Property State, Name
    If (!(($psSessions -like '@{State=Opened; Name=ExchangeOnlineInternalSession*').Count -gt 0)) {
        throw "Run Connect-ExchangeOnline before running this function."
    }
    
    #Get userinfo
    $adUser = Get-ADUser -Credential $(Get-AdmCreds) -Filter {DisplayName -eq $displayName} -Properties *

    if (!$adUser) {
        throw "Can't find user `"$displayName`" in AD."
    }

    $adUsername = $aduser.Name
    $homeDir = $adUser.HomeDirectory
    
    #Create home dir
    if ($homeDir) {
        [bool]$homeDirExists = Invoke-gsudo {
            if (!(Test-Path $using:homeDir)) {
                New-Item -ItemType Directory $using:homeDir | Out-Null
            }
            # Get the ACL for an existing folder
            $ExistingACL = Get-Acl -Path $using:homeDir
            # Sets Full Control permission for the given user on This Folder, Subfolders and Files
            $Permissions = "intern\$using:adUsername", 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow'
            # Create a new FileSystemAccessRule object
            $Rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $Permissions
            # Modify the existing ACL to include the new rule
            $ExistingACL.SetAccessRule($Rule)
            # Apply the modified access rule to the folder
            $ExistingACL | Set-Acl -Path $using:homeDir | Out-Null
    
            Test-Path $using:homeDir
        }
    } else {
        [bool]$homeDirExists = $false
        $notes.Add("There's no homedir path on the AD user.")
    }

    #Check if mailbox is in Exchange Online
    if (Get-InstalledModule ExchangeOnlineManagement) {
        Import-Module ExchangeOnlineManagement
    } else {
        Install-Module ExchangeOnlineManagement -Force
    }
    
    [bool]$migrated = Get-EXOMailbox $adUser.mail -ErrorAction SilentlyContinue
    
    #Reset AD password
    Set-ADAccountPassword $adUser.Name -Credential $(Get-AdmCreds) -NewPassword (Read-Host "Please enter a new password for user `"$($adUser.Name)`"" -AsSecureString) -Reset -Server "dc01.intern.mrfylke.no"

    [PSCustomObject]@{
        DisplayName = $adUser.DisplayName
        Username = $adUser.Name
        UPN = $adUser.UserPrincipalName
        ADLocation = $adUser.DistinguishedName
        HomeDirExists = $homeDirExists
        Migrated = $migrated
        Notes = $notes
    }
}
#EndRegion './Public/Invoke-PCCheck.ps1' 69
#Region './Public/New-LocalAdmin.ps1' 0
function New-LocalAdmin {
    param (
        [Parameter(Mandatory)][string]$username,
        [Parameter(Mandatory)][string]$hostname
    )
    $admUsername = "adm_$username"
    $UPN = "$admUsername@intern.mrfylke.no"

    #Create user in AD
    if (!(Get-ADUser -Credential $(Get-AdmCreds) -filter {UserPrincipalName -eq $UPN})) {
        $result = Get-ADuser -Credential $(Get-AdmCreds) $username
        $firstName = $result.GivenName
        $lastName = $result.Surname
        
        New-ADUser -Credential $(Get-AdmCreds) -name "$firstName $lastName (Admin)" -SamAccountName $admUsername -Path "OU=Lokaladmins,OU=Adminbrukere,DC=intern,DC=mrfylke,DC=no" -UserPrincipalName $UPN -Description "Lokaladmin på maskin $hostname" -GivenName "$firstName" -Surname "$lastName" -DisplayName "$firstName $lastName (Admin)" -AccountPassword ("Molde123!" | ConvertTo-SecureString -AsPlainText) -ChangePasswordAtLogon $true -Enabled $true
    } else {
        Write-Information -MessageData "UPN `"$UPN`" already exists" -InformationAction Continue
    }

    $discordHookPath = "$env:USERPROFILE\.creds\Discord\workHookUri.xml"
    if (!(Test-Path $discordHookPath)) {
        $discordHookDir = $discordHookPath.Substring(0, $discordHookPath.lastIndexOf('\'))
        if (!(Test-Path $discordHookDir)) {
            New-Item -ItemType Directory $discordHookDir | Out-Null
        }
        Read-Host -AsSecureString "Enter Discord webhook uri" | Export-Clixml $discordHookPath
    }

    $taskName = "Localadmin for $admUsername on $hostname"
    $task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue

    #Add ADUser to admin role on the given hostname
    if (Test-Connection $hostname -Count 1 -Quiet) {
        Invoke-Command -Credential $(Get-AdmCreds) -ComputerName $hostname -ScriptBlock {
            if (Get-LocalGroupMember -Group Administratorer -Member "intern\$using:admUsername" -ErrorAction SilentlyContinue) {
                "User `"$using:admUsername`" is already an administrator on `"$using:hostname`""
            } else {
                Add-LocalGroupMember -Group Administratorer -Member "intern\$using:admUsername"
            }
        }

        if ($task) {
            Send-DiscordMessage -uri (Import-Clixml $discordHookPath | ConvertFrom-SecureString -AsPlainText) -message "Task `"$taskName`" has been completed."
            $task | Unregister-ScheduledTask -Confirm:$false
        }
    } elseif (!$task) {
        $module1 = "C:\Users\danhol13\repos\mrfk-commands\Output\mrfk-commands.psm1"
        $module2 = "C:\Users\danhol13\repos\NN.Notifications\Output\NN.Notifications.psm1"
        $action = New-ScheduledTaskAction -Execute "pwsh.exe" -Argument "-WindowStyle Hidden -command `"Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force;Import-Module `"$module1`";Import-Module `"$module2`";New-LocalAdmin -username $username -hostname $hostname`""
        #Run every 5 minutes
        $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5)
        $null = Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $taskName -Description "Add `"$admUsername`" as localadmin on `"$hostname`""

        Send-DiscordMessage -uri (Import-Clixml $discordHookPath | ConvertFrom-SecureString -AsPlainText) -message "Task `"$taskName`" has been created."
    }
}
#todo: move scheduled tasks with discord implementation to their own functions
#EndRegion './Public/New-LocalAdmin.ps1' 58
#Region './Public/New-ShippingLabel.ps1' 0
function New-ShippingLabel {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)][ValidateScript({
            $_ -in (Get-SnipeLocations).name
        })][string]$location,
        [Parameter(Mandatory)][string]$displayname,
        [int]$copies = 1,
        [string]$mobile,
        [string]$PrinterNetworkPath = "\\sr-safecom-sla1\PR-STORLABEL-SSDSK"
    )
    
    begin {
        $RequiredModulesNameArray = @("NN.SnipeIT","NN.WindowsSetup")
        $RequiredModulesNameArray.ForEach({
            if (Get-InstalledModule $_ -ErrorAction SilentlyContinue) {
                Import-Module $_ -Force
            } else {
                Install-Module $_ -Force
            }
        })

        #Install RSAT
        Install-RSAT -WUServerBypass
    }
    
    process {
        #Get the selected locations address
        $locationResult = Get-SnipeLocations -name $location

        $locationName = $locationResult.address
        $address = $locationResult.address2
        $postalCode = $locationResult.zip
        $city = $locationResult.city

        if (!$mobile) {
            #Search AD for phonenumber
            [string]$mobile = (Get-ADUser -Filter {DisplayName -like $displayname} -Properties mobile).mobile
        }

        if ($location) {
            #Get current default printer
            $defaultPrinter = (Get-CimInstance -Class Win32_Printer).where({$_.Default -eq $true}).Name

            #Set printer named PR-STORLABEL-SSDSK to default printer
            if (!(Get-Printer -Name $PrinterNetworkPath -ErrorAction SilentlyContinue)) {
                (New-Object -ComObject WScript.Network).AddWindowsPrinterConnection($PrinterNetworkPath)
            }
            (New-Object -ComObject WScript.Network).SetDefaultPrinter($PrinterNetworkPath)

            #Create new Word document
            $WordObj = New-Object -ComObject Word.Application
            $null = $WordObj.Documents.Add()

            #Set page size and margins
            $WordObj.Selection.PageSetup.PageHeight = "192mm"
            $WordObj.Selection.PageSetup.PageWidth = "102mm"
            $WordObj.Selection.PageSetup.TopMargin = "12,7mm"
            $WordObj.Selection.PageSetup.BottomMargin = "12,7mm"
            $WordObj.Selection.PageSetup.LeftMargin = "12,7mm"
            $WordObj.Selection.PageSetup.RightMargin = "12,7mm"

            #Insert content
            $WordObj.Selection.Font.Bold = 1
            $WordObj.Selection.TypeText("Mottaker:
            "
)
            $WordObj.Selection.Font.Bold = 0
            $WordObj.Selection.TypeText("$displayname
            $mobile"
)
            $WordObj.Selection.TypeParagraph()
            $WordObj.Selection.Font.Bold = 1
            $WordObj.Selection.TypeText("Addresse:
            "
)
            $WordObj.Selection.Font.Bold = 0
            $WordObj.Selection.TypeText("$locationName
            $address
            $postalCode $city"
)

            (1..$copies).ForEach({
                #Send To Default Printer
                $WordObj.PrintOut()
            })

            #Change default printer back to the previous value
            (New-Object -ComObject WScript.Network).SetDefaultPrinter("$defaultPrinter")

            #Close File without saving
            $WordObj.ActiveDocument.Close(0)
            $WordObj.quit() 
        }
    }
}
#EndRegion './Public/New-ShippingLabel.ps1' 93