Automatic site setup with powershell throws several errors - powershell

first and foremost I am NOT a Powershell dev so I'm a little over my head here, if this was BASH it would have been done by now....
I'm getting several errors that I don't really understand the what or why of,
I believe that maybe $vars cannot be interpreted in the way that I am trying to use? idk...
If someone WITH powershell knowledge could lend an assist and educate this poor linux guy, I would be very grateful...
I would expect this to register and load a new (empty) site with IIS based of the input params provided.
Here's my PS1
# --------------------------------------------------------------------
# Define the variables.
# --------------------------------------------------------------------
Param (
[string]$InetSiteName = $( Read-Host "Site Name" ),
[int]$InetSitePort = $( Read-Host "Site Port" ),
[string]$InetPhysPath = $( $env:SystemDrive + "\inetpub\wwwroot" )
)
Import-Module "WebAdministration"
# --------------------------------------------------------------------
# Check for empty.
# --------------------------------------------------------------------
if(-not($InetSiteName)) { Throw "Site name cannot be empty..." }
if(-not($InetSitePort)) { Throw "Site port cannot be empty..." }
if(-not($InetPhysPath)) { Throw "Path name cannot be empty..." }
# --------------------------------------------------------------------
# Configure and register.
# --------------------------------------------------------------------
New-Item IIS:\Sites\$InetSiteName -physicalPath $InetPhysPath -bindings #{ protocol="http";bindingInformation=":"+$InetSitePort+":"+$InetSiteName }
Set-ItemProperty IIS:\Sites\$InetSiteName -name applicationPool -value BenAPI
Start-WebSite $InetSiteName
# --------------------------------------------------------------------
# Run.
# --------------------------------------------------------------------
$webclient = New-Object Net.WebClient
$webclient.DownloadString("http://localhost:$InetSitePort/");
$ie = New-Object -com InternetExplorer.Application
$ie.Visible = $true
$ie.Navigate("http://localhost:$InetSitePort/");

This is because "WebAdministration" module is not getting loaded. Run Powershell as Administrator so that "WebAdministration" module gets imported successfully.
Then Go to location where you have saved the script to run the script
PS C:\> cd 'C:\Users\jDaniel\Documents'
PS C:\Users\jDaniel\Documents> .\iis-site.ps1
There is a step to step guide Here - how to create Web-Sites, Web Applications, Virtual Directories and Application Pools in powershell.

This is how I create a new site:
New-WebSite -Name $InetSiteName -port $InetSitePort -id 555 -PhysicalPath "$InetPhysPath" -HostHeader $InetSiteName
if you need more binding you can use:
New-WebBinding -Name $InetSiteName -Port $InetSitePort2 -HostHeader $InetSiteName2

Looks like the real answer was that I didn't have a real pool created, here's my updated code...
Thanks Everyone
# --------------------------------------------------------------------
# Checking Execution Policy
# --------------------------------------------------------------------
#$Policy = "Unrestricted"
$Policy = "RemoteSigned"
If ((get-ExecutionPolicy) -ne $Policy) {
Write-Host "Script Execution is disabled. Enabling it now"
Set-ExecutionPolicy $Policy -Force
Write-Host "Please Re-Run this script in a new powershell enviroment"
Exit
}
# --------------------------------------------------------------------
# Define the variables.
# --------------------------------------------------------------------
[string]$InetSiteName = $( Read-Host "Site Name" )
[int]$InetSitePort = $( Read-Host "Site Port" )
[string]$InetPhysPath = $( $env:SystemDrive + "\inetpub" )
$PoolName = "BenAPI"
# --------------------------------------------------------------------
# Loading IIS Modules
# --------------------------------------------------------------------
Import-Module "WebAdministration"
# --------------------------------------------------------------------
# Test or Create App Pool
# --------------------------------------------------------------------
if (!(Test-Path "IIS:\AppPools\$PoolName" -pathType container))
{
# Create pool
$appPool = New-Item "IIS:\AppPools\$PoolName"
$appPool | Set-ItemProperty -Name "managedRuntimeVersion" -Value "v4.0"
}
# --------------------------------------------------------------------
# Configure and register.
# --------------------------------------------------------------------
$WebRoot = New-Item "$InetPhysPath\$InetSiteName" -type Directory
New-Item IIS:\Sites\$InetSiteName -physicalPath $WebRoot -bindings #{ protocol="http";bindingInformation="*:"+$InetSitePort+":" }
Set-ItemProperty IIS:\Sites\$InetSiteName -name applicationPool -value BenAPI
Set-Content "$WebRoot\default.htm" "Test Page: $InetSiteName"
Start-WebSite $InetSiteName
# --------------------------------------------------------------------
# Run.
# --------------------------------------------------------------------
$ie = New-Object -com InternetExplorer.Application
$ie.Visible = $true
$ie.Navigate("http://localhost:$InetSitePort/");

Related

Install Printer Remotely

I am currently working in Powershell. I have created a Script that allows me to install the specified printer to any given machine on our network. However, I need help with adding Parameters(Variables) so that I do not have to go back into the code each time and manually change the Printer and Its Driver/IP.
<# Declare name of the Port #>
$portName = "TCPPort:xx.xx.xx.xx"
<# Declares the name of the Printers Driver #>
$printDriverName = "HP LaserJet Pro M402-M403 PCL 6"
<# If the printer exists, get the port for it, and assign the name of it to -Name $portname #>
$portExists = Get-Printerport -Name $portname -ErrorAction SilentlyContinue
<# If the Port does not exist, Add the provided port name, assign it as the Printerhostaddress #>
if (-not $portExists) {
Add-PrinterPort -name $portName -PrinterHostAddress ""
}
$printDriverExists = Get-PrinterDriver -name $printDriverName -ErrorAction SilentlyContinue
<# Once the PrintDriver is obtained, Add the Printer, assign the name,portname, and driver name. Install to designated system #>
if ($printDriverExists){
Add-Printer -Name "CRCHRDirHP2" -PortName $portName -DriverName $printDriverName
}else{
Write-Warning "Printer Driver not installed"
}
This is an example of how your code would look if it was a function, I've changed the if statements for try / catch statements. The function is using [cmdletbinding()], this will allow you to use CommonParameters, in example, -Verbose if you want to display the Write-Verbose comments.
function Install-Printer {
[cmdletbinding()]
param(
[Parameter(Mandatory)]
[string]$PrinterName,
[Parameter(Mandatory)]
[string]$PortName,
[Parameter(Mandatory)]
[string]$DriverName
)
try
{
Write-Verbose 'Attempting to get Printer Port'
# If this fails, it will go to `catch` block
$null = Get-Printerport -Name $PortName
}
catch
{
Write-Verbose 'Port does not exist. Adding new Printer Port.'
Add-PrinterPort -Name $PortName -PrinterHostAddress ""
}
try
{
Write-Verbose 'Adding printer...'
$null = Get-PrinterDriver -Name $DriverName
Add-Printer -Name $PrinterName -PortName $PortName -DriverName $DriverName
Write-Verbose 'Printer added successfully.'
}
catch
{
Write-Verbose 'Failed to add printer, error was:'
$PSCmdlet.WriteError($_)
}
}
Usage:
Install-Printer -PrinterName "CRCHRDirHP2" -PortName "TCPPo...." -DriverName "HP LaserJet..."
In addition to Santiago's answer, if you have some source for the variables it would make things a lot easier. For instance consider the following CSV file 'printers.csv' in C:\Temp:
name,portName,driverName
CRCHRDirHP2,TCPPort123,HP LaserJet
CRCHRDirHP3,TCPPort1234,HP LaserJet
CRCHRDirHP4,TCPPort12345,HP LaserJet
Now we import the file and start looping through the printers to call the function that Santiago made and use the variables from the input file.
$printers = Import-CSV C:\Temp\printers.csv
foreach($printer in $printers){
Write-Verbose "Processing printer $($printer.name)"
try{
Install-Printer -PrinterName $printer.name -PortName $printer.portName -DriverName $printer.driverName
}catch{
Write-Verbose "Printer $($printer.name) failed with error: $_"
}
}

Cannot install fonts with Powershell on Windows 10

On my work computer, I don't have admin privileges.
Installing new fonts cannot be done "the easy way".
At the time I was using Windows 7, I managed to run a PowerShell script that was launched at session startup and that installed the fonts from a given folder.
Here is the code I used:
add-type -name Session -namespace "" -member #"
[DllImport("gdi32.dll")]
public static extern int AddFontResource(string filePath);
"#
$FontFolder = "C:\Users\myusername\Documents\Fonts"
$null = foreach($font in Get-ChildItem -Path $FontFolder -Recurse -Include *.ttf, *.otg, *.otf) {
Write-Host "Installing : $($font.FullName)"
$result = [Session]::AddFontResource($font.FullName)
Write-Host "Installed $($result) fonts"
}
Now that I have switched to Windows 10, I thought I could go back to installing fonts "the easy way", as it is supposed to be possible to install fonts for your user without admin privileges.
This however still does not work: there is a popup window saying that "The requested file is not a valid font file". One solution is apparently to start the Windows firewall, which of course is not allowed by my administrator... but it is already running (see Edit below)
Back to the PowerShell then. The script unfortunately does not work anymore and does not provide any interesting pointers to where the problem comes from:
Installing : C:\Users\myusername\Documents\Fonts\zilla-slab\ZillaSlab-SemiBold.otf
Installed 0 fonts
Installing : C:\Users\myusername\Documents\Fonts\zilla-slab\ZillaSlab-SemiBoldItalic.otf
Installed 0 fonts
Installing : C:\Users\myusername\Documents\Fonts\zilla-slab\ZillaSlabHighlight-Bold.otf
Installed 0 fonts
I tried using a try catch, but still have no identified error:
add-type -name Session -namespace "" -member #"
[DllImport("gdi32.dll")]
public static extern int AddFontResource(string filePath);
"#
$FontFolder = "C:\Users\myusername\Documents\Fonts"
$null = foreach($font in Get-ChildItem -Path $FontFolder -Recurse -Include *.ttf, *.otg, *.otf) {
try {
Write-Host "Installing : $($font.FullName)"
$result = [Session]::AddFontResource($font.FullName)
Write-Host $result
}
catch {
Write-Host "An error occured installing $($font)"
Write-Host "$($error)"
Write-Host "$($error[0].ToString())"
Write-Host ""
1
}
}
And the resulting output
Installing : C:\Users\myusername\Documents\Fonts\zilla-slab\ZillaSlabHighlight-Bold.otf
0
Installing : C:\Users\myusername\Documents\Fonts\zilla-slab\ZillaSlabHighlight-Regular.otf
0
Installing : C:\Users\myusername\Documents\Fonts\ZillaSlab-Light.otf
0
Any idea how to solve this issue?
Edit:
Regarding the status of the security applications, here is the McAfee status:
McAfee Data Exchange Layer OK
McAfee DLP Endpoint OK
Programme de mise à jour McAfee OK
McAfee Endpoint Security OK
"Programme de mise à jour" means "update program" in French.
I also checked the list of running services :
mpssvc service (Windows defender firewall) is running
mfefire (McAfee Firewall core service) is not running
Edit2:
My last attempt is the following:
I copied the font file manually to the $($env:LOCALAPPDATA)\Microsoft\Windows\Fonts\ folder
Using regedit, I added the entry as shown below
I restarted. Still no Bebas font in WordPad or Publisher
Here's how I do it with a com object. This works for me as non-admin based on Install fonts without administrative privileges. I can see the fonts installed to "$env:LOCALAPPDATA\Microsoft\Windows\Fonts" in the Fonts area under Settings. I have Windows 10 20H2 (it should work in 1803 or higher). I also see the fonts installed in Wordpad.
$Destination = (New-Object -ComObject Shell.Application).Namespace(20)
$TempFolder = "$($env:windir)\Temp\Fonts\"
New-Item -Path $TempFolder -Type Directory -Force | Out-Null
Get-ChildItem -Path $PSScriptRoot\fonts\* -Include '*.ttf','*.ttc','*.otf' |
ForEach {
If (-not(Test-Path "$($env:LOCALAPPDATA)\Microsoft\Windows\Fonts\$($_.Name)")) {
$Font = "$($env:windir)\Temp\Fonts\$($_.Name)"
Copy-Item $($_.FullName) -Destination $TempFolder
$Destination.CopyHere($Font)
Remove-Item $Font -Force
} else { "font $($env:LOCALAPPDATA)\Microsoft\Windows\Fonts\$($_.Name) already installed" }
}
Example REG_SZ registry entry:
dir 'HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Fonts*' | ft -a
Hive: HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion
Name Property
---- --------
Fonts Nunito Black (TrueType) : C:\Users\myuser\AppData\Local\Microsoft\Windows\Fonts\Nunito-Black.ttf
You can install fonts on windows using following powershell scripts.
param(
[Parameter(Mandatory=$true,Position=0)]
[ValidateNotNull()]
[array]$pcNames,
[Parameter(Mandatory=$true,Position=1)]
[ValidateNotNull()]
[string]$fontFolder
)
$padVal = 20
$pcLabel = "Connecting To".PadRight($padVal," ")
$installLabel = "Installing Font".PadRight($padVal," ")
$errorLabel = "Computer Unavailable".PadRight($padVal," ")
$openType = "(Open Type)"
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
$objShell = New-Object -ComObject Shell.Application
if(!(Test-Path $fontFolder))
{
Write-Warning "$fontFolder - Not Found"
}
else
{
$objFolder = $objShell.namespace($fontFolder)
foreach ($pcName in $pcNames)
{
Try{
Write-Output "$pcLabel : $pcName"
$null = Test-Connection $pcName -Count 1 -ErrorAction Stop
$destination = "\\",$pcname,"\c$\Windows\Fonts" -join ""
foreach ($file in $objFolder.items())
{
$fileType = $($objFolder.getDetailsOf($file, 2))
if(($fileType -eq "OpenType font file") -or ($fileType -eq "TrueType font file"))
{
$fontName = $($objFolder.getDetailsOf($File, 21))
$regKeyName = $fontName,$openType -join " "
$regKeyValue = $file.Name
Write-Output "$installLabel : $regKeyValue"
Copy-Item $file.Path $destination
Invoke-Command -ComputerName $pcName -ScriptBlock { $null = New-ItemProperty -Path $args[0] -Name $args[1] -Value $args[2] -PropertyType String -Force } -ArgumentList $regPath,$regKeyname,$regKeyValue
}
}
}
catch{
Write-Warning "$errorLabel : $pcName"
}
}
}

Set a variable equal to the output of a powershell script

This is a little difficult to explain, but I will do my best. I am writing some code to import AD contacts to users' mailboxes through EWS using Powershell.
I have a Main.ps1 file that calls all the other scripts that do work in the background (for example 1 imports the AD modules) another imports O365 modules.
I have 1 script container that connect to EWS. The code looks like this:
#CONFIGURE ADMIN CREDENTIALS
$userUPN = "User#domain.com"
$AESKeyFilePath = ($pwd.ProviderPath) + "\ConnectToEWS\aeskey.txt"
$SecurePwdFilePath = ($pwd.ProviderPath) + "\ConnectToEWS\password.txt"
$AESKey = Get-Content -Path $AESKeyFilePath -Force
$securePass = Get-Content -Path $SecurePwdFilePath -Force | ConvertTo-SecureString -Key $AESKey
#create a new psCredential object with required username and password
$adminCreds = New-Object System.Management.Automation.PSCredential($userUPN, $securePass)
Try
{
[Reflection.Assembly]::LoadFile("\\MBX-Server\c$\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll") | Out-Null
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1)
$service.Credentials = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials($userUPN,$adminCreds.GetNetworkCredential().Password)
$service.Url = new-object Uri("https://outlook.office365.com/EWS/Exchange.asmx");
return $service
}
Catch
{
Write-Output "Unable to connect to EWS. Make sure the path to the DLL or URL is correct"
}
The output of that code prints out the Service connection, but I want the information for that output stored in a variable such as $service.
Then I would pass that variable to another script that binds to the mailbox I want...
The problem I am having is $service doesn't seem to be storing that information. It only print it out once when I return it from the script above, but it doesn't append that information in the main script. When I print out $service it prints out once, but then it clears itself.
Here is my main script
CLS
#Root Path
$rootPath = $pwd.ProviderPath #$PSScriptRoot #$pwd.ProviderPath
Write-Host "Importing all necessary modules."
#******************************************************************
# PREREQUISITES
#******************************************************************
#Nuget - Needed for O365 Module to work properly
if(!(Get-Module -ListAvailable -Name NuGet))
{
#Install NuGet (Prerequisite) first
Install-PackageProvider -Name NuGet -Scope CurrentUser -Force -Confirm:$False
}
#******************************************************************
#Connect w\ Active Directory Module
& $rootPath\AD-Module\AD-module.ps1
#Load the O365 Module
& $rootPath\O365-Module\O365-module.ps1
#Clear screen after loading all the modules/sessions
CLS
#******************************************************************
# PUT CODE BELOW
#******************************************************************
#GLOBAL VARIABLES
$global:FolderName = $MailboxToConnect = $Service = $NULL
#Connect to EWS
& $rootPath\ConnectToEWS\ConnectToEWS.ps1
#Debug
$Service
#Create the Contacts Folder
& $rootPath\CreateContactsFolder\CreateContactsFolder.ps1
#Debug
$service
$ContactsFolder
#Clean up Sessions after use
if($NULL -ne (Get-PSSession))
{
Remove-PSSession *
}
[GC]::Collect()
The first time I output the $service variable, it prints fine. In the 2nd Debug output it doesn't print out anymore, and I believe that it why the script is failing when I launch "CreateContactsFolder.ps1"
Here is the content of "CreateContactsFolder.ps1"
CLS
Try
{
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxToConnect);
$RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
$RootFolder.Load()
#Check to see if they have a contacts folder that we want
$FolderView = new-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)
$ContactsFolderSearch = $RootFolder.FindFolders($FolderView) | Where-Object {$_.DisplayName -eq $FolderName}
if($ContactsFolderSearch)
{
$ContactsFolder = [Microsoft.Exchange.WebServices.Data.ContactsFolder]::Bind($service,$ContactsFolderSearch.Id);
#If folder exists, connect to it. Clear existing Contacts, and reupload new (UPDATED) Contact Info
Write-Output "Folder alreads exists. We will remove all contacts under this folder."
# Attempt to empty the target folder up to 10 times.
$tries = 0
$max_tries = 0
while ($tries -lt 2)
{
try
{
$tries++
$ErrorActionPreference='Stop'
$ContactsFolder.Empty([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete, $true)
$tries++
}
catch
{
$ErrorActionPreference='SilentlyContinue'
$rnd = Get-Random -Minimum 1 -Maximum 10
Start-Sleep -Seconds $rnd
$tries = $tries - 1
$max_tries++
if ($max_tries -gt 100)
{
Write-Output "Error; Cannot empty the target folder; `t$EmailAddress"
}
}
}
}
else
{
#Contact Folder doesn't exist. Let's create it
try
{
Write-Output "Creating new Contacts Folder called $FolderName"
$ContactsFolder = New-Object Microsoft.Exchange.WebServices.Data.ContactsFolder($service);
$ContactsFolder.DisplayName = $FolderName
$ContactsFolder.Save([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
}
catch
{
Write-Output "Error; Cannot create the target folder; `t$EmailAddress"
}
}
}
Catch
{
Write-Output "Couldn't connect to the user's mailbox. Make sure the admin account you're using to connect to has App Impersonization permissions"
Write-Output "Check this link for more info: https://help.bittitan.com/hc/en-us/articles/115008098447-The-account-does-not-have-permission-to-impersonate-the-requested-user"
}
return $ContactsFolder
In the Main script, capture the returned variable from the EWS script like
$service = & $rootPath\ConnectToEWS\ConnectToEWS.ps1
Or dot-source that script into the Main script, so the variables from EWS.ps1 are local to the Main script, so you don't need to do return $service in there:
. $rootPath\ConnectToEWS\ConnectToEWS.ps1
and do the same for the CreateContactsFolder.ps1 script
OR
define the important variables in the called scripts with a global scope $global:service and $global:ContactsFolder
See About_Scopes

Powershell MSUtil.LogQuery not closing SQL connection

I've written a PowerShell scrip that iterates through a large number of IIS W3C logfiles and inserts the values into a MSSQL database.
Set-Variable -Name "UnprocessedDir" -Value "X:\files" -Description "Folder for unprocessed log files" Scope Script
Set-Variable -Name "InputObject" -Value (New-Object -comObject MSUtil.LogQuery.IISW3CInputFormat) -Description "Log Parser input COM object" -Scope Script
Set-Variable -Name "OutputObject" -Value (New-Object -comObject MSUtil.LogQuery.SQLOutputFormat) -Description "Log Parser output COM object" -Scope Script
$OutputObject.clearTable = $false
$OutputObject.createTable = $false
$OutputObject.database = "Database_Name"
$OutputObject.driver = "SQL Server"
$OutputObject.dsn = "DSN_Name"
$OutputObject.fixColNames = $true
$OutputObject.ignoreIdCols = $true
$OutputObject.ignoreMinWarns = $true
$OutputObject.maxStrFieldLen = 511
$OutputObject.oConnString = $null
$OutputObject.password = $null
$OutputObject.server = "sqlserver.domain.com\INSTANCENAME"
$OutputObject.transactionRowCount = 5000
$OutputObject.username = $null
Set-Variable -Name "IISLogs" -Value #(Get-ChildItem -Path $UnprocessedDir -Recurse -File) -Description "Array of files to be imported into SQL" -Scope Script
Set-Variable -Name "LPComObj" -Value (New-Object -com MSUtil.LogQuery) -Description "COM Object used to import Log Parser records into MSSQL" -Scope Script
Write-Output "$(Get-ISOTimeStamp) Beginning SQL import. $($IISLogs.Count) Files to be imported"
$IISLogs | ForEach-Object { $loop = 0 } {
Set-Variable -Name "SubDir" -Value $(($_.FullName).Split('\')[-2]) -Description "Subdirectory where log file is located" -Scope Script
Set-Variable -Name "LogType" -Value $(($_.FullName).Split('\')[-3]) -Description "Type of log being imported" -Scope Script
Set-Variable -Name "ServerName" -Value $(($_.FullName).Split('\')[-4]) -Description "ServerName of file being imported" -Scope Script
Set-Variable -Name "LPQuery" -Description "Query to use in Log Parser" -Scope Script -Value #"
SELECT
-- FIELDS LogFilename,LogRow,date,time,c-ip,cs-username,s-sitename,s-computername,s-ip,s-port,cs-method,cs-uri-stem,cs-uri-query,sc-status,sc-substatus,sc-win32-status,sc-bytes,cs-bytes,time-taken,cs-version,cs-host,cs(User-Agent),cs(Cookie),cs(Referer),s-event,s-process-type,s-user-time,s-kernel-time,s-page-faults,s-total-procs,s-active-procs,s-stopped-procs
-- STANDARD FIELDS date,time,s-ip,cs-method,cs-uri-stem,cs-uri-query,s-port,cs-username,c-ip,cs(User-Agent),cs(Referer),sc-status,sc-substatus,sc-win32-status,time-taken
'$($ServerName)' as [servername],
'$($_.Name)' as [filename],
LogRow AS [row],
'$($LogType)' as [logtype],
TO_TIMESTAMP(date,time) AS [timestamp],
[s-ip],
[cs-method],
[cs-uri-stem],
[cs-uri-query],
TO_INT([s-port]),
[cs-username],
[c-ip],
[cs(User-Agent)],
[cs(Referer)],
TO_INT([sc-status]),
TO_INT([sc-substatus]),
TO_INT([sc-win32-status]),
TO_INT([time-taken]),
0 AS lock
INTO IIS_W3C
FROM '$($_.FullName)'
"#
Set-Variable -Name "LPResult" -Value ($LPComObj.ExecuteBatch($LPQuery, $InputObject, $OutputObject)) -Description "IIS Log File imported into SQL" -Scope Script
If ($LPResult -eq $false)
{
Write-Output "$(Get-ISOTimeStamp) Data imported from `"$($_.FullName)`""
Set-Variable -Name "loop" -Value ($loop + 1) -Description "Increase loop iteration Count" -Scope Script
}
Else
{
Write-Output "$(Get-ISOTimeStamp) Log Parser returned errors importing `"$($_.FullName)`""
Throw "$(Get-ISOTimeStamp) Log Parser returned errors importing `"$($_.FullName)`""
}
}
The number of logs I'm importing is tens of thousands; the code above works spectacularly for a few hundred files, but after a few hours, it crashes. From what I can tell, it looks like every iteration of the ForEach-Object loop creates a new SQL TCP connection which is not terminated at the end of the loop.
I've tried creating the $LPComObj both within and outside of the loop. I've tried Remove-Variable. I've tried some generic commands like $LPComObj.Close(), .Remove(), .Quit(), etc. The MSUtil.LogQuery method itself does not seem to contain any methods to close the SQL TCP connection, and as the script is running, I can see more and more TCP connections piling up. I tried a few things using [System.Runtime.Interopservices.Marshal]:: to release/remove the COM object, but none of them closed the TCP connection. Even closing the PowerShell session doesn't kill the connection.
The only way I was able to do this is by hunting down and finding the dllhost.exe" process that was using the ports and killing it. But from within the script, there isn't a clean way to get the PID of the offending dllhost.exe process. (Trying to kludge some variant of Get-Process | Stop-Process might work, but would add a lot of time to the execution of the script.)
What other ways might I be able to work around this problem?

Enable Windows 10 Developer Mode programmatically

I know you can enable Windows 10 Developer mode interactively by going to Settings | For developers, selecting 'Developer mode' and then rebooting.
Is there a way to enable this programmatically? (eg. via PowerShell or similar so that I can include it as a step in a Boxstarter script when refreshing my developer workstation)
Turns out Nickolaj Andersen has written an article which includes just such a PowerShell script..
http://www.scconfigmgr.com/2016/09/11/enable-ubuntu-in-windows-10-during-osd-with-configmgr/
Here are the relevant lines extracted from his post:
# Create AppModelUnlock if it doesn't exist, required for enabling Developer Mode
$RegistryKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
if (-not(Test-Path -Path $RegistryKeyPath)) {
New-Item -Path $RegistryKeyPath -ItemType Directory -Force
}
# Add registry value to enable Developer Mode
New-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense -PropertyType DWORD -Value 1
I modified the accepted answer and ended up with the following script:
param([Switch]$WaitForKey)
if (([Version](Get-CimInstance Win32_OperatingSystem).version).Major -lt 10)
{
Write-Host -ForegroundColor Red "The DeveloperMode is only supported on Windows 10"
exit 1
}
# Get the ID and security principal of the current user account
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
if ($myWindowsPrincipal.IsInRole($adminRole))
{
$RegistryKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
if (! (Test-Path -Path $RegistryKeyPath))
{
New-Item -Path $RegistryKeyPath -ItemType Directory -Force
}
if (! (Get-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense))
{
# Add registry value to enable Developer Mode
New-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense -PropertyType DWORD -Value 1
}
$feature = Get-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online
if ($feature -and ($feature.State -eq "Disabled"))
{
Enable-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online -All -LimitAccess -NoRestart
}
if ($WaitForKey)
{
Write-Host -NoNewLine "Press any key to continue..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
}
else
{
# We are not running "as Administrator" - so relaunch as administrator
# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";
# Specify the current script path and name as a parameter
$newProcess.Arguments = "-NoProfile",$myInvocation.MyCommand.Definition,"-WaitForKey";
# Indicate that the process should be elevated
$newProcess.Verb = "runas";
# Start the new process
[System.Diagnostics.Process]::Start($newProcess);
# Exit from the current, unelevated, process
exit
}
It automatically elevates itself, if not already running elevated and enables the optional feature mentioned by Julian Knight.