PowerShell: Kill a Windows Service Forcefully

# killService.ps1
$serviceName='vmms'

function killService($serviceName='Spooler',$restart=$false){    
    $processId=Get-WmiObject -Class Win32_Service -Filter "Name LIKE '$serviceName'"|Select-Object -ExpandProperty ProcessId
    if($processId.count -eq 1 -and $processId -ne 0){
        try{
            $taskkill=taskkill /f /pid $processid
            write-host $taskkill
            if($restart){
                start-service $serviceName
                $result=get-service $serviceName
                if($result.Status -eq 'Running'){
                    $processId=Get-WmiObject -Class Win32_Service -Filter "Name LIKE '$serviceName'"|Select-Object -ExpandProperty ProcessId
                    write-host "$serviceName has successfully restarted with pid $processId" -foregroundcolor GREEN
                    return $true
                }else{
                    write-host "$serviceName has NOT successfully restarted" -foregroundcolor RED
                    return $false
                }
            }else{
                $result=get-service $serviceName
                if($result.Status -eq 'Stopped'){
                    $processId=Get-WmiObject -Class Win32_Service -Filter "Name LIKE '$serviceName'"|Select-Object -ExpandProperty ProcessId
                    write-host "$serviceName has successfully stopped" -foregroundcolor GREEN
                    return $true
                }else{
                    write-host "$serviceName has NOT successfully stopped" -foregroundcolor RED
                    return $false
                }
            }            
        }catch{
            write-warning $_
            return $false
        }
    }else{
        try{
            start-service $serviceName
        }catch{
            write-warning $_
            # write-warning "$serviceName is not found on $env:computername"
            return $null
        }
        
    }
}

killService $serviceName

How to Install PowerShell Module on An Offlined Computer

# Download the desired module onto a 'jump host' (an intermediary computer that has access to the Internet)
PS C:\WINDOWS\system32> Save-Module -Name SqlServer -Path C:\Temp
PS C:\WINDOWS\system32> ls C:\temp
    Directory: C:\temp
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         1/30/2023   5:04 PM                SqlServer

# Add files into remote computer from the jump host
robocopy c:\temp\sqlserver \\SERVERNAME\C$\Program Files\WindowsPowerShell\Modules /E /NP

PS C:\Windows\system32> Enter-PSSession SERVERNAME
[SERVERNAME]: PS C:\Users\kimconnect\Documents> Get-Module -Name SQLServer -ListAvailable
    Directory: C:\Program Files\WindowsPowerShell\Modules
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     21.1.18256 SqlServer                           {Add-RoleMember, Add-SqlAvailabilityDatabase, Add-SqlAvail...

# Check SqlServer module's path
[SERVERNAME]: PS C:\Users\kimconnect\Documents> (Get-Module -ListAvailable SQLServer).path
C:\Program Files\WindowsPowerShell\Modules\SqlServer\21.1.18256\SqlServer.psd1

# Import sqlserver powershell module into current session
[SERVERNAME]: PS C:\Users\kimconnect\Documents> Import-Module -Name SqlServer

[SERVERNAME]: PS C:\Users\kimconnect\Documents> get-module

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Manifest   3.1.0.0    Microsoft.PowerShell.Management     {Add-Computer, Add-Content, Checkpoint-Computer, Clear-Con...
Manifest   3.1.0.0    Microsoft.PowerShell.Utility        {Add-Member, Add-Type, Clear-Variable, Compare-Object...}
Script     21.1.18256 SqlServer                           {Add-RoleMember, Add-SqlAvailabilityDatabase, Add-SqlAvail...

PowerShell: Uninstall Program Using Its Name

# uninstallProgramUsingId.ps1

$appName='Virtual Machine Manager Agent'
$uninstallStringRegPaths='HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall','HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$uninstallStrings=Get-ChildItem -Path $uninstallStringRegPaths
$uninstallString=($uninstallStrings|Get-ItemProperty|Where-Object {$_.DisplayName -match $appName}).UninstallString
if($uninstallString.count -eq 1){
    $appCode=[regex]::match($uninstallString,'\{(.*)\}').Value
    $uninstallCommand="& msiexec.exe /x $appCode /quiet /norestart"
    write-host "Invoking uninstall Command: $uninstallCommand"
    Invoke-Expression $uninstallCommand
    $appStillExists=Get-WmiObject -Class Win32_Product -Filter "Name='$appName'"
    if($appStillExists){
        write-warning "Uninstall has been unsuccessful at removing $appName"
        return $false
    }else{
        return $true
    }
}else{
    write-warning "Please check this/these uninstall string(s):`r`n$uninstallString"
    return $false
}

PowerShell: Get Connected Port by Process Name

# getProcessConnectionPort.ps1

$computerlist=@'
SQL1
SQL2
'@

$computernames=@($computerList -split "`n")|%{$_.Trim()}
$processname='sqlservr'
$state='Established'

$results=@()
foreach ($computername in $computernames){
    $result=invoke-command -computername $computername{
        param($processName,$state='Established')
        try{
            $processid=(get-process $processName).Id            
            $ports=Get-NetTcpConnection -OwningProcess $processid -State $state|Group-Object -Property LocalPort
            write-host "$env:computername`: $processName PID is found as $processid running on port(s) $($ports.Name)"
            return $ports
        }catch{
            write-warning $_
            return $false
        }        
    } -Args $processName,$state
    $results+=$result
}
$results

PowerShell: Quickly Test Connectivity from a List of Sources toward a Destination on a Specific Port

# testRemotePort.ps1

$connectFrom=@'
windows1
windows2
'@
$connectTo=@'
\\servername\sharename
'@
$testPort=445

$sources=@($connectFrom -split "`n")|%{$_.Trim()}
$destinations=@($connectTo -split "`n")|%{$_.Trim()}
$results=@()
foreach($source in $sources){
    foreach($destination in $destinations){
        $result=invoke-command -computername $source {
            param($destination,$testPort)
            write-host "$env:computername to $destination`:$testPort => " -nonewline
            $result=try{if(test-netconnection $destination -port $testPort -informationLevel Quiet){'Open'}else{'Closed'}}catch{write-warning $_;'Unknown'}
            write-host "$result"
            return $result
        } -Args $destination,$testPort
        $results+=[pscustomobject]@{
            source=$source
            destination=$destination
            port=$testPort
            status=$result
        }
    }
}

How To Recover SQLSERVER Service from Being Unable to Start

# startSqlService.ps1

# special provision to deal with SQL Server not starting up due to various reasons
$sqlServerService='mssqlserver'
$sqlWaitSeconds=120
$desiredState='Running'
$sqlWaitTimespan=[timespan]::fromseconds($sqlWaitSeconds)
if($sqlServerService -in $originalStoppedServices.Name){            	
    try{
    $sqlService=get-service $sqlServerService -EA Ignore
    if($sqlService.Status -ne $desiredState -and $null -ne $sqlService){
        $stopped=try{$sqlService.waitforstatus('Stopped',$sqlWaitTimespan);$true}catch{$false}
        if($stopped){
            $attempt=net start $sqlServerService /T902 2>&1
            $running=(get-service $sqlServerService).Status -eq 'Running'
            $running=if(!$running){try{$sqlService.waitforstatus('Running',$sqlWaitTimespan);$true}catch{$false}}else{$running}
            if($attempt -match 'started' -and $running){
                write-host "$sqlServerService has been started with trace flag /T902" -foregroundcolor Green
            }else{
                write-host "$sqlServerService has NOT been started with trace flag /T902" -foregroundcolor Red
            }
        }else{
            write-warning "$sqlServerService has current state is $((get-service $sqlServerService).Status)"
        }                 
    }                
    }catch{
    write-warning $_                               
    }            
}

Resume All Guest VMs in a Clustered Hyper-V Environment

# resumeAllVms.ps1
# Significance: this script is useful in a Clustered Hyper-V environment
# where storage was down for over 60 seconds, causing Hyper-V to put all guest VM's into 'paused-critical' statuses.
# During such event, the cluster would be unreachable as well. Hence, knowing node names prior to pasting this script
# would be necessary.

$computerlist=@'
hyperVServer1
hyperVServer2
hyperVServer100
'@
$computernames=@($computerlist -split "`n")|%{$_.Trim()}
foreach ($computername in $computernames){
  invoke-command $computername {
    write-host "Connected to $env:computername..."
    #Get list of all VMs on this host
    #$vmNames=(get-vm).Name
    #write-host "$env:computername`:`r`n$($vmNames|out-string)"
    
    # Unpausing all VMs
    $pausedVms=get-vm | where state -eq 'paused'
    foreach($vm in $pausedVMs){        
        write-host $vm.Name -nonewline
      try{        
        $vm|resume-vm -ea stop
        write-host ": resumed" -foregroundcolor Green
      }catch{
        write-warning $_
        continue
      }    
    }   
  }
}

PowerShell: Command to Retrieve Windows Applications List

The following the the PoSH equivalent of appwiz.cpl in Windows:

PS> invoke-command TestWindows {Get-Package -Provider Programs -IncludeWindowsInstaller|select-object Name,Version|sort -property Name}|select -property * -ExcludeProperty runspaceid,pscomputername

Name                                                               Version
----                                                               -------
7-Zip 18.06 (x64 edition)                                          18.06.00.0
DRAC Command Line Tools                                            9.2.0
Notepad++ (64-bit x64)                                             7.5.9
NSClient++ (x64)                                                   0.5.2039
PuTTY release 0.71 (64-bit)                                        0.71.0.0

How to Search for Installed Application by Name in PowerShell

One can search using the known Vendor or application Name as illustrated below:

[testwindows]: PS C:\Users\user1\Documents> Get-WmiObject -Class Win32_Product | ?{$_.Name -like '*exporter*'}


IdentifyingNumber : {EDD0CDE3-4519-4C1A-9FB4-C8C067615698}
Name              : windows_exporter
Vendor            : prometheus-community
Version           : 0.20.0
Caption           : windows_exporter

[btestwindows]: PS C:\Users\user1\Documents> Get-WmiObject -Class Win32_Product | ?{$_.Vendor -like '*prometheus*'}


IdentifyingNumber : {EDD0CDE3-4519-4C1A-9FB4-C8C067615698}
Name              : windows_exporter
Vendor            : prometheus-community
Version           : 0.20.0
Caption           : windows_exporter

PowerShell: Create Registry Keys within Windows Localhost

# createRegKey.ps1

$regKeys=@(
	@{
        hive='REGISTRY::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome';
        name='ChromeCleanupEnabled';
        value=0
    }
	@{
        hive='REGISTRY::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome';
        name='ChromeCleanupReportingEnabled';
        value=0
        }
)
 
function createRegKey{
    param(
        $regHive,
        $keyName,
        $value
    )
    try{
        $keyValue=(Get-ItemProperty -Path $regHive -Name $keyName -ErrorAction Stop).$keyName
        if($keyValue -ne $value){
            New-ItemProperty -Path $regHive -Name $keyName -Value $value -Type String -Force
        }else{
            write-host "$regHive $keyName already has a value of $value"
        }
    }catch [System.Management.Automation.ItemNotFoundException],[System.Management.Automation.PSArgumentException] {
        New-Item -Path $regHive -Force
        New-ItemProperty -Path $regHive -Name $keyName -Value $value -Force
        write-host "$regHive $keyName value has been set to $value"
    }catch {
        write-warning $_
        New-ItemProperty -Path $regHive -Name $keyName -Value $value -Type String -Force
        write-host "$regHive $keyName value has been set to $value"
    }
}

$regKeys|%{createRegKey $_.hive $_.name $_.value}

Windows: How to Map Network Drive for All Users of a Local Machine

# Mapping file share using legacy commands (more effective than newer PowerShell equivalents in certain scenarios)
$textContent=@'
net use m: /delete
net use m: \\FILESERVER\SHARENAME
'@
Set-Content "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\mountShares.bat" $textContent

How to Set PowerShell Window and Prompt Title or Label

Have you ever wondered about changing the boring PS C:\Windows\system32> whenever a new window is opened in PowerShell? You’re in luck by finding this quick note. Here’s how you’d do it:

# Set Window title to match current machine name
$host.UI.RawUI.WindowTitle="PS $env:computername $env:username"

# Set Window prompt to match current machine name
function prompt {"PS $env:computername $env:username> "}

# Set color of label
function prompt{
Write-Host ("PS $env:computername $env:username> ") -nonewline -foregroundcolor Yellow
return " "
}

Use DISM To Install Windows Features

# dismInstallFeatures.ps1

$featuresToInstall = ('FileAndStorage-Services','Storage-Services','Web-Server','Web-WebServer','Web-Common-Http','Web-Default-Doc','Web-Dir-Browsing','Web-Http-Errors',
'Web-Static-Content','Web-Http-Redirect','Web-Health','Web-Http-Logging','Web-Log-Libraries','Web-ODBC-Logging','Web-Request-Monitor',
'Web-Http-Tracing','Web-Performance','Web-Stat-Compression','Web-Dyn-Compression','Web-Security','Web-Filtering','Web-Basic-Auth',
'Web-Client-Auth','Web-Digest-Auth','Web-Cert-Auth','Web-IP-Security','Web-Url-Auth','Web-Windows-Auth','Web-App-Dev','Web-Net-Ext',
'Web-Net-Ext45','Web-ASP','Web-Asp-Net','Web-Asp-Net45','Web-CGI','Web-ISAPI-Ext','Web-ISAPI-Filter','Web-Includes','Web-WebSockets',
'Web-Ftp-Server','Web-Ftp-Service','Web-Mgmt-Tools','Web-Mgmt-Console','Web-Mgmt-Compat','Web-Metabase','Web-Lgcy-Mgmt-Console','Web-Lgcy-Scripting',
'Web-WMI','Web-Scripting-Tools','Web-Mgmt-Service','NET-Framework-Features','NET-Framework-Core','NET-Framework-45-Features','NET-Framework-45-Core',
'NET-Framework-45-ASPNET','NET-WCF-Services45','NET-WCF-HTTP-Activation45','NET-WCF-TCP-PortSharing45','RSAT','RSAT-Feature-Tools','RSAT-SMTP',
'RSAT-SNMP','FS-SMB1','SMTP-Server','SNMP-Service','User-Interfaces-Infra','Server-Gui-Mgmt-Infra','Server-Gui-Shell','PowerShellRoot','PowerShell',
'PowerShell-V2','PowerShell-ISE','WAS','WAS-Process-Model','WAS-Config-APIs','WoW64-Support')

# Remove any feature that is not available to prevent dism failures. MSMQ-Container for example is only available 
# on Windows 7 but not on Windows Server 2008 R2. 
$availableFeatures = dism /online /Get-Features
$featuresToRemove = @()
$featuresToInstall | % { if (-not ($availableFeatures | Select-String ('{0}$' -f $_) -Quiet)) { $featuresToRemove += $_} }
$featuresToInstall = Compare-Object -ReferenceObject $featuresToInstall -DifferenceObject $featuresToRemove | select -ExpandProperty InputObject

$dismParameter = @('/online', '/Enable-Feature', ($featuresToInstall | % { '/FeatureName:{0}' -f $_ }), '/NoRestart', '/all')
$output = dism @dismParameter

# throw an error if dism wasn't successful
if ($global:LastExitCode -ne 0){
    throw 'Error while installing Windows Features. {0}' -f ($output | Select-String '\.log$')
}
# dismCheckWindowsFeature.ps1

$featuresToCheck = ('RDS-RD-Server')
$availableFeatures = dism /online /Get-Features
$featuresToRemove = @()
$featuresToInstall | % { if (-not ($availableFeatures | Select-String ('{0}$' -f $_) -Quiet)) { $featuresToRemove += $_} }
$featuresToInstall = Compare-Object -ReferenceObject $featuresToInstall -DifferenceObject $featuresToRemove | select -ExpandProperty InputObject

$dismParameter = @('/online', '/Get-Features', ($featuresToCheck | % { '/FeatureName:{0}' -f $_ }), '/NoRestart', '/all')

$output = dism @dismParameter

# throw an error if dism wasn't successful
if ($global:LastExitCode -ne 0)
{
    throw 'Error while installing Windows Features. {0}' -f ($output | Select-String '\.log$')
}

dism /online /get-Features 'RDS-RD-Server'

PowerShell: Get Available RAM Slots

# getRamSlotsAvailable.ps1

$computername=$env:computername

function getRamSlotsAvailable{
    param($computername=$env:computername)
    write-host "Computer name: $computerName"
    $slots = Get-WmiObject -Class "win32_PhysicalMemoryArray" -namespace "root\CIMV2" -computerName $computerName
    $ramModules = Get-WmiObject -Class "win32_PhysicalMemory" -namespace "root\CIMV2" -computerName $computerName
    $ramSum=0
    $ramModules.Capacity|%{$ramSum+=$_}
    $slotsSum=0
    $slots.MemoryDevices|%{$slotsSum+=$_}
    write-host "Total slots: $slotsSum"
    write-host "Total RAM: $([math]::round($ramSum/1GB,2)) GB"
    Foreach ($module In $ramModules) {
        write-host "Memory Installed: $($module.DeviceLocator)"
        write-host "Memory Size: $($module.Capacity/1GB) GB"
    }
    return $slotsSum
}

getRamSlotsAvailable $computername

# Sample output
# PS C:\Windows\system32> getRamSlotsAvailable $computername
# Computer name: TEST-SERVER
# Total slots: 24
# Total RAM: 256 GB
# Memory Installed: DIMM_A1
# Memory Size: 16 GB
# Memory Installed: DIMM_A2
# Memory Size: 16 GB
# Memory Installed: DIMM_A3
# Memory Size: 16 GB
# Memory Installed: DIMM_A4
# Memory Size: 16 GB
# Memory Installed: DIMM_A5
# Memory Size: 16 GB
# Memory Installed: DIMM_A6
# Memory Size: 16 GB
# Memory Installed: DIMM_A7
# Memory Size: 16 GB
# Memory Installed: DIMM_A8
# Memory Size: 16 GB
# Memory Installed: DIMM_B1
# Memory Size: 16 GB
# Memory Installed: DIMM_B2
# Memory Size: 16 GB
# Memory Installed: DIMM_B3
# Memory Size: 16 GB
# Memory Installed: DIMM_B4
# Memory Size: 16 GB
# Memory Installed: DIMM_B5
# Memory Size: 16 GB
# Memory Installed: DIMM_B6
# Memory Size: 16 GB
# Memory Installed: DIMM_B7
# Memory Size: 16 GB
# Memory Installed: DIMM_B8
# Memory Size: 16 GB

How To Add or Remove a Path in Windows Environmental Paths

There are 2 functions to Add and Remove, at your convenience:

# addEnvironmentalPath.ps1

$pathToAdd='C:\Scripts'
$pathToRemove='C:\Scripts'

function addEnvironmentalPath($pathToAdd){
    $registryEnvironment='Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment'
    $pathToAdd=if($pathToAdd -match '\\$'){$pathToAdd -replace '.$'}else{$pathToAdd}
    try{
        $originalPaths=(Get-ItemProperty -Path $registryEnvironment -Name PATH).path
        $pathsArray=$originalPaths -split ';'|?{$_.trim() -ne ''}|%{if($_ -match '\\$'){$_ -replace '.$'}else{$_}}|Sort-Object -Unique
        if($pathToAdd -in $pathsArray){
            write-host "$pathToAdd is already included in the environmental paths: '$originalPaths'"
            return $true
        }else{
            $newPathsArray=$pathsArray+$pathToAdd
            $newPaths=$newPathsArray -join ';'
            Set-ItemProperty -Path $registryEnvironment -Name PATH -Value $newPaths
            $newRegistryEnvironment=(Get-ItemProperty -Path $registryEnvironment -Name PATH).Path
            write-host "Environmental paths have been changed:`r`nFrom: $originalPaths`r`nTo: $newPaths"
            return $true
        }
    }catch{
        write-warning $_
        return $false
    }
}

function removeEnvironmentalPath($pathToRemove){
    $registryEnvironment='Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment'
    $pathToRemove=if($pathToRemove -match '\\$'){$pathToRemove -replace '.$'}else{$pathToRemove}
    try{        
        $originalPaths=(Get-ItemProperty -Path $registryEnvironment -Name PATH).path
        $pathsArray=$originalPaths -split ';'|?{$_.trim() -ne ''}|%{if($_ -match '\\$'){$_ -replace '.$'}else{$_}}|Sort-Object -Unique
        if($pathToRemove -notin $pathsArray){
            write-host "$pathToRemove does not exist in the environmental paths: '$originalPaths'"
            return $true
        }else{
            $itemIndex=[array]::indexof($pathsArray,$pathToRemove)
            [System.Collections.ArrayList]$newPathsArrayList=$pathsArray
            $newPathsArrayList.RemoveAt($itemIndex)
            $newPaths=$newPathsArrayList -join ';'
            Set-ItemProperty -Path $registryEnvironment -Name PATH -Value $newPaths
            $newRegistryEnvironment=(Get-ItemProperty -Path $registryEnvironment -Name PATH).Path
            write-host "Environmental paths have been changed:`r`nFrom: $originalPaths`r`nTo: $newPaths"
            return $true            
        }
    }catch{
        write-warning $_
        return $false
    }
}

addEnvironmentalPath $pathToAdd
removeEnvironmentalPath $pathToRemove

PowerShell: Find Windows RDS Roles and Their Licensing Servers

# Get TS Licensing Servers (Enterprise or AD Registered)
$termLicenseServers=Get-ADGroupMember -Identity "Terminal Server License Servers"
$termLicenseServers|Select-Object -Property @{label='computername';expression={[regex]::match($_.DistinguishedName,'([\w\d-]+)(?=,OU=)').Value}}

# Get TS Connection Points with Licensing Role Installed (may or may not be registered on the domain)
$termServers=Get-ADObject -Filter {objectClass -eq 'serviceConnectionPoint' -and Name -eq 'TermServLicensing'}
$termServers|Select-Object -Property @{label='computername';expression={[regex]::match($_.DistinguishedName,'([\w\d-]+)(?=,OU=)').Value}}

# Check localhost for TS Licensing Servers
$tsSettings=gwmi -namespace "Root/CIMV2/TerminalServices" Win32_TerminalServiceSetting 
$tsSettings.GetSpecifiedLicenseServerList().SpecifiedLSList

# Find RDS Servers and their Licensing Configs
$servers=Get-ADComputer -Filter {OperatingSystem -Like '*Windows Server*' -and Enabled -eq $true}
$results=@()
$noResults=@()
$initCount=0
$serversCount=$servers.Count
write-host "There are $serversCount servers to check..."
foreach($server in $servers.DNSHostName){
	$initCount++
	write-host "$initCount of $serversCount`: $server"
	$rdsRole=try{Get-WindowsFeature RDS-RD-Server -ComputerName $server -EA Ignore}catch{$null}	
	if($rdsRole){
		$rdsRoleInstalled=$rdsRole.InstallState -eq 'Installed'
		if($rdsRoleInstalled){
		$rdsLicenseServer=try{		
			$tsSettings=gwmi -namespace "Root/CIMV2/TerminalServices" Win32_TerminalServiceSetting -computername $server
			$tsSettings.GetSpecifiedLicenseServerList().SpecifiedLSList
		}catch{
			'not configured'
		}
		$result=@{
			computername=$server
			rdsLicenseServer=$rdsLicenseServer
			licensingType=$tsSettings.LicensingName
		}
		write-host "$server has RDS-RD-Server role installed with licensing server(s) $rdsLicenseServer"
		$results+=$result
		}else{	
			write-host "$server doesn't have RDS-RD-Server role installed"
		}
	}else{
		write-warning "$env:computername cannot connect to $server"
		$noResults+=@{
			computername=$server
			connectionProblem=$True			
		}
	}
	#pause
}

$results|%{write-host "$($_.computername)`: $($_.rdsLicenseServer) | $($_.LicensingType)"}
$noResults|%{write-host "$($_.computername)"}
Issue:
Log Name: System
Source: Microsoft-Windows-DNS-Client
Date: 8/1/2018 2:11:47 AM
Event ID: 8019
Task Category: (1028)
Level: Warning
Keywords:
User: NETWORK SERVICE
Computer:
Description:
The system failed to register host (A or AAAA) resource records (RRs) for network adapter
with settings:

Adapter Name : {2F85D40D-4BEB}
Host Name : testwindows
Primary Domain Suffix : intranet.kimconnect.com
DNS server list : x.x.x.x
Sent update to server : <?>
IP Address(es) : y.y.y.y

The reason the system could not register these RRs was because of a security related problem. The cause of this could be (a) your computer does not have permissions to register and update the specific DNS domain name set for this adapter, or (b) there might have been a problem negotiating valid credentials with the DNS server during the processing of the update request.

You can manually retry DNS registration of the network adapter and its settings by typing 'ipconfig /registerdns' at the command prompt. If problems still persist, contact your DNS server or network systems administrator. See event details for specific error code information.
Event Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name="Microsoft-Windows-DNS-Client" Guid="{1C95126E}" />
<EventID>8019</EventID>
<Version>0</Version>
<Level>3</Level>
<Task>1028</Task>
<Opcode>0</Opcode>
<Keywords>0x4000000000000000</Keywords>
<TimeCreated SystemTime="2018-08-01T06:11:47.524646900Z" />
<EventRecordID>285426</EventRecordID>
<Correlation />
<Execution ProcessID="484" ThreadID="2104" />
<Channel>System</Channel>
<Computer>testwindows.intranet.kimconnect.com</Computer>
<Security UserID="S-1-5-20" />
</System>
<EventData>
<Data Name="AdapterName">{2F85D40D}</Data>
<Data Name="HostName">dbmon01l-ops-23</Data>
<Data Name="AdapterSuffixName">intranet.kimconnect.com</Data>
<Data Name="DnsServerList"> x.x.x.x</Data>
<Data Name="Sent UpdateServer">&lt;?&gt;</Data>
<Data Name="Ipaddress">y.y.y.y</Data>
<Data Name="ErrorCode">9017</Data>
</EventData>
</Event>
Resolution:

List available NIC’s to pick the correct one

[testwindows]: PS C:\Users\brucelee\Documents> Get-NetAdapter

Name InterfaceDescription ifIndex Status MacAddress LinkSpeed
---- -------------------- ------- ------ ---------- ---------
Ethernet Microsoft Hyper-V Network Adapter 2 Up AA-6C-B8-00-00-00 20 Gbps
Ethernet 2 Cisco AnyConnect Secure Mobility Cli... 6 Not Present 00-05-00-00-00-00 0 bps

Remove the GUI check mark next to ‘Register this connection’s addresses with DNS’
Advanced TCP/IP Settings

[testwindows]: PS C:\Users\brucelee\Documents> Get-NetAdapter Ethernet|Set-DNSClient -RegisterThisConnectionsAddress $F
alse

Verify the result:

[testwindows]: PS C:\Users\brucelee\Documents> Get-NetAdapter Ethernet | Get-DNSClient

InterfaceAlias Interface ConnectionSpecificSuffix ConnectionSpecificSuffix RegisterThisConn UseSuffixWhen
Index SearchList ectionsAddress Registering
-------------- --------- ------------------------ ------------------------ ---------------- -------------
Ethernet 2 {} False True

How to Install Windows Dot Net 3.5 & 4.5

Run these commands to check dot net 3.5:

PS C:\Windows\system32> import-module servermanager
PS C:\Windows\system32> get-windowsfeature web-asp-net

Display Name Name Install State
------------ ---- -------------
[ ] ASP.NET 3.5 Web-Asp-Net Available

Install ASP.NET 3.5:

PS C:\Windows\system32> install-windowsfeature web-asp-net 
Success Restart Needed Exit Code Feature Result
------- -------------- --------- --------------
True No Success {ASP.NET 4.8, Application Development, ASP...

Run this command to check dot net 4.5:

PS C:\Windows\system32> get-windowsfeature Net-Framework-45-Core

Display Name Name Install State
------------ ---- -------------
[X] .NET Framework 4.8 NET-Framework-45-Core Installed

If necessary, install DotNet 4.5:

add-windowsfeature Net-Framework-45-Core

PowerShell: Install App Using MSI on Remote Computers Via WinRM

# installMSiRemoteComputers.ps1
# version 0.0.1

$computernames='REMOTEPC001','REMOTEPC002'
$thisMsiFile='C:\Temp\something.msi'
$appName='testapp'
$desiredVersion='1.0'
$maxWaitSeconds=120

function installMsiOnRemoteComputers($computernames,$msiFile,$appName,$desiredVersion,$maxWaitSeconds){
    function installMsiOnRemoteComputer{
        param(
            $computernames=$env:computername,
            $msiFile,
            $destinationLocalTempFolder='C:\Temp',
            $testFileName='testfile.txt'
        )
        
        function translateLocalPathToSmbPath($computername,$localPath,$testFileName){
            $adminDriveLetter=[regex]::match($localPath,'^([\w\W])\:').captures.groups[1].value
            $partialPath=[regex]::match($localPath,'^([\w\W])\:(.*)').captures.groups[2].value
            $testPath=join-path "\\$computername\$adminDriveLetter`$" "$partialPath"
            if(!(test-path $testPath)){
                try{
                    New-Item -Path $testPath -ItemType "directory" -force
                }catch{
                    write-warning "Unable to create $testPath"
                    return $false
                }
            }
            try{
                $null=New-Item -Path $testPath -Name $testFileName -ItemType "file" -force
                Remove-Item "$testPath\$testFileName" -force
                return $testPath
            }catch{
                write-warning "Unable to read or write to $testPath"
                return $false
            }        
        }
    
        $results=[hashtable]@{}
        $msiLocalFilePath=join-path $destinationLocalTempFolder $(split-path $msiFile -leaf)
        foreach ($computername in $computernames){
            $translatedDestination=translateLocalPathToSmbPath $computername $destinationLocalTempFolder $testFileName
            if($translatedDestination){
                copy-item $msiFile $translatedDestination
            }else{
                write-warning "Unable to copy $msiFile to $translatedDestination"
                $results+=[hashtable]@{$computername=$false}
                continue
            }        
            $psSession=new-psSession $computername
            if($psSession.State -eq 'Opened'){
                $result=invoke-command -session $pssession -scriptblock{
                    param($filePath)
                    $file=gi $filePath
                    $DataStamp = get-date -Format yyyyMMddTHHmmss
                    $logFile ="C:\" + '{0}-{1}.log' -f $file.name,$DataStamp
                    $MSIArguments = @(
                        "/i"
                        ('"{0}"' -f $file.fullname)
                        "/qn"
                        "/norestart"
                        "/L*v"
                        $logFile
                    )
                    try{
                        [diagnostics.process]::start("msiexec.exe", $MSIArguments).WaitForExit()
                        write-host "MSIEXEC has been called for file $filePath on $env:computername"
                        return $true
                    }catch{
                        write-warning $_
                        return $false
                    }             
                } -Args $msiLocalFilePath
    
                # Note Error:
                # Resolved by not using -wait switch and calling [diagnostics.process] instead of Start-Process "msiexec.exe"
                # Although, this error would still being thrown with the [diagnostics.process] result of success
                # Processing data for a remote command failed with the following error message: The I/O operation has been aborted
                # because of either a thread exit or an application request. For more information, see the about_Remote_Troubleshooting
                # Help topic.
                #     + CategoryInfo          : OperationStopped: (:String) [], PSRemotingTransportException
                #     + FullyQualifiedErrorId : JobFailure
                #     + PSComputerName        : 
    
                $results+=[hashtable]@{$computername=$result}
                remove-psSession $psSession
            }else{
                write-warning "Unable to connect to $computername"
                $results+=[hashtable]@{$computername="Unable to connect to $computername"}
            }
        }
        return $results
    }
    $results=[hashtable]@{}
    foreach($computername in $computernames){
        try{
            installMsiOnRemoteComputer $computername $msiFile
            write-host "Now waiting up to $maxWaitSeconds seconds before checking on the install result"
            $timer=[System.Diagnostics.Stopwatch]::StartNew()
            do{                        
                start-sleep -seconds 5
                $appVersionPassed=invoke-command -computername $computername {
                    param($appName,$desiredVersion)
                    $matchedApp=Get-WmiObject -Class Win32_Product -Filter "Name='$appName'"
                    if($matchedApp.Version -eq $desiredVersion){
                        return $true
                    }else{
                        return $false
                    }
                } -Args $appName,$desiredVersion
                if($appVersionPassed){
                    write-host "$appName $desiredVersion installed on $computername successfully"
                    $results+=[hashtable]@{$computername='$appName $desiredVersion installed'}                            
                }
                $exitCondition=$timer.elapsed.totalseconds -ge $maxWaitSeconds
            }until($appVersionPassed -or $exitCondition)
            if(!$appVersionPassed){                        
                    write-host "$appName $desiredVersion has NOT been installed successfully on $computername"
                    $results+=[hashtable]@{$computername="$appName $desiredVersion NOT installed"}
            }
            $timer.stop()
        }catch{
            write-warning $_
            $results+=[hashtable]@{$computername="$_"}
            exit 1   
        }
    }
    return $results
}

installMsiOnRemoteComputers $computernames $thisMsiFile $appName $desiredVersion $maxWaitSeconds

PowerShell: Add Local Group Members Onto a Server List

Sometimes, the GUI method of accomplishing tasks is too arduous and error prone. Thus, these quick cmdlets would get the job done in matter of seconds, assuming that WinRM and firewall allows the jump host to reach its targets.

# addLocalGroupMemberOnServerList.ps1

$computernames=@(
	'testserver1',
    'testserver2'
)

$localAdmins=@(
    'domain\backupadmin'
)

$remoteDesktopUsers=@(
    'domain\testuser'
)

invoke-command -computername $computernames -scriptblock{
		param($localAdmins,$remoteDesktopUsers)
		$localAdmins=$localAdmins.ToArray() # converting ArrayList datatype to Array [of objects]
		$remoteDesktopUsers=$remoteDesktopUsers.ToArray()
		try{
			$remoteDesktopUsers|%{add-localgroupmember -group 'Remote Desktop Users' -Member $_}
			$localAdmins|%{add-localgroupmember -group 'Administrators' -Member $_}
			return $true
		}catch{
			write-warning $_
			return $false
		}
	} -Args (,$localAdmins),(,$remoteDesktopUsers)

# Use legacy cmdlets to preemt this error
# Failed to compare two elements in the array.
#     + CategoryInfo          : NotSpecified: (:) [Get-LocalGroupMember], InvalidOperationException
#     + FullyQualifiedErrorId : An unspecified error occurred.,Microsoft.PowerShell.Commands.GetLocalGroupMemberCommand
#     + PSComputerName        : testserver.kimconnect.com
invoke-command -computername $computernames {net localgroup 'Administrators'; net localgroup 'Remote Desktop Users'}