Link to home
Start Free TrialLog in
Avatar of Georges Orwell
Georges OrwellFlag for France

asked on

New-WebVirtualDirectory from local to Remote computers

Hi all,

I try to create a  New-WebVirtualDirectory on local and 3 other remote  IIS computers using powershell, but with  invoke-command this won't work (access denied).
Is there a way to do this simply with powershell ?

Thank you
Avatar of K B
K B
Flag of United States of America image

from an exchange server


New-WebServicesVirtualDirectory -Server <ServerName>

Open in new window

Avatar of Georges Orwell

ASKER

Thank you K B

But New-WebServicesVirtualDirectory is cmdlet for Exchange not for IIS.
Apologies.  

What is the full command you want to execute on the remote computer(s)
from module WebAdministration
New-WebVirtualDirectory -Name ASSO\$Identity  -site “FTPASSO” -PhysicalPath D:\webroot\ftpasso\$dentity
This work localy on any IIS but failed  from local to remote computers.
prior to executing script, from a PS prompt: winrm set winrm/config/client '@{TrustedHosts="10.20.10.10"}'   *substitute the IP addresses of each server needing the script to be run on it, separated by commas

$cred = Get-Credential
$session = new-pssession -computername "COMPUTER01" -credential $cred

invoke-command -session $session -scriptblock {
    New-WebVirtualDirectory 
}

Open in new window



$cred = Get-Credential
$identity = "something"
$id = $using:Identity

invoke-command -Credential $cred -ComputerName COMPUTER01, COMPUTER02 -scriptblock {
New-WebVirtualDirectory -Name ASSO\$id  -site “FTPASSO” -PhysicalPath D:\webroot\ftpasso\$id 
}

Open in new window

if there are variables in the script block you need to use the syntax like this


$id = $using:Identity

Open in new window



actually there is an error.. corrected here:

$cred = Get-Credential
$identity = "something"
$ScriptBlock = 
{
$id = $using:Identity
New-WebVirtualDirectory -Name ASSO\$id  -site “FTPASSO” -PhysicalPath D:\webroot\ftpasso\$id 
}

invoke-command -Credential $cred -ComputerName COMPUTER01, COMPUTER02 -scriptblock $scriptblock

Open in new window

Anyway, using -credential  or -session this is same result

Access is denied
    + CategoryInfo          : PermissionDenied: (D:\webroot\ftpasso\workdir:String) [Test-Path], UnauthorizedAc
   cessException
    + FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.TestPathCommand
    + PSComputerName        : srvftp2.ASSO.local
Here is my code
Invoke-Command -Credential $credObject -ComputerName $ftp2  -ScriptBlock { 
			Param($Identity, $BuildPath)
		
			If (-not (Test-Path -Path $BuildPath)) {
			
					Import-Module WebAdministration
                    New-WebVirtualDirectory -Name FTPASSO\$Identity  -site “FTP” -PhysicalPath $BuildPath
					Add-WebConfiguration -Filter /System.FtpServer/Security/Authorization -Value (@{AccessType="Allow"; Users="ASSO\$Identity"; Permissions=3}) -PSPath IIS: -Location "FTPASSO/$Identity"
				
			}
		} -ArgumentList $Identity, $BuildPath 

Open in new window


here is error

Access is denied
    + CategoryInfo          : PermissionDenied: (D:\webroot\ftpasso\workdir:String) [Test-Path], UnauthorizedAc
   cessException
    + FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.TestPathCommand
    + PSComputerName        : srvftp2.ASSO.local
What version of PS?

Use $USING:variable instead of argument
also,

Are you in the same domain?  don't use UNC path for this test.. once in.. try to execute your commands one by one.. let me know results please

Enter-PSSession -ComputerName COMPUTER01 -credentials (get-credential) 

Open in new window

Powershell Version 5.

Do you have an exemple with $USING ?
$cred = Get-Credential
$identity = "something"
$ScriptBlock = 
{
$id = $using:Identity
New-WebVirtualDirectory -Name ASSO\$id  -site “FTPASSO” -PhysicalPath D:\webroot\ftpasso\$id 
}

invoke-command -Credential $cred -ComputerName COMPUTER01, COMPUTER02 -scriptblock $scriptblock

Open in new window

This is already exactly what I do
I dont see using: in your code
All servers are in the same domain.
can you show me how to use $using  ?
Let's first start with if you can get to a remote powershell prompt on the remote server

Enter-PSSession -ComputerName COMPUTER01 -credentials (get-credential) 

Open in new window

PS D:\scripts> $s=Enter-PSSession -ComputerName srvftp2 -Credential (get-credential)

Open in new window



cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential
[srvftp2]: PS C:\Users\asso_adm\Documents> Invoke-Command -Session $s -Command cmd.exe
Invoke-Command : Cannot validate argument on parameter 'Session'. The argument is null or empty. Provide an argument
that is not null or empty, and then try the command again.
At line:1 char:25
+ Invoke-Command -Session $s -Command cmd.exe
+                         ~~
    + CategoryInfo          : InvalidData: (:) [Invoke-Command], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.InvokeCommandCommand

once you see:

[srvftp2]: PS

you are on the server so you wouldn't use invoke command.. you would just run the commands as if you were sitting at the mouse and keyboard of the server you want to administer.

Import-Module WebAdministration
$identity = 'Something'
$BuildPath = 'SomePath'
New-WebVirtualDirectory -Name FTPASSO\$Identity  -site “FTP” -PhysicalPath $BuildPath
Add-WebConfiguration -Filter /System.FtpServer/Security/Authorization -Value (@{AccessType="Allow"; Users="ASSO\$Identity"; Permissions=3}) -PSPath IIS: -Location "FTPASSO/$Identity"

Open in new window

$BuildPath = 'pathofsomething'
$Identity  = 'someidentity'
$ftp2      = 'srvftp2'
$cred      = (Get-Credential) 
$SB =
{ 
    $bpath = $using:buildpath
    $id    = $using:identity
    If (!(Test-Path -Path $BuildPath)) {	
        Import-Module WebAdministration
        New-WebVirtualDirectory -Name FTPASSO\$id -site “FTP” -PhysicalPath $BuildPath
        Add-WebConfiguration -Filter /System.FtpServer/Security/Authorization -Value (@{AccessType = "Allow"; Users = "ASSO\$id"; Permissions = 3}) -PSPath IIS: -Location "FTPASSO/$id"		
    }
}

Invoke-Command -Credential $cred -ComputerName $ftp2  -ScriptBlock $SB

Open in new window

Ok but localy it work on any of 4 servers
I  want to run my script on anyone of these server and  I don't want to have to  connect on each to run it...
Okay try the above command.. then if that works we can use a foreach-object
$BuildPath = 'pathofsomething'
$Identity  = 'someidentity'
$ftp2      = 'srvftp2'
$cred      = (Get-Credential) 
$SB =
{ 
    $bpath = $using:buildpath
    $id    = $using:identity
    If (!(Test-Path -Path $BuildPath)) {	
        Import-Module WebAdministration
        New-WebVirtualDirectory -Name FTPASSO\$id -site “FTP” -PhysicalPath $bpath
        Add-WebConfiguration -Filter /System.FtpServer/Security/Authorization -Value (@{AccessType = "Allow"; Users = "ASSO\$id"; Permissions = 3}) -PSPath IIS: -Location "FTPASSO/$id"		
    }
}

Invoke-Command -Credential $cred -ComputerName $ftp2  -ScriptBlock $SB

Open in new window


corrected one variable.
There is no change. Same error.
On each server I already runned this

"Enable-PSRemoting -Force"
"Set-Item wsman:\localhost\client\trustedhosts *"
"Restart-Service WinRM"
You would run that on the machine you are running FROM

So you make the script and make it a ps1  and run it from a ps prompt (not remote)

Can you post full error you get please?
And please post full script you used
Here is my code

$username = "asso_adm@asso.local"
$pwdTxt = get-content "D:\nfsftp\scripts\CipherPasswd.txt"
[Byte[]] $key = (1..32)
$securePwd = $pwdTxt | ConvertTo-SecureString -Key $key
$credObject = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $securePwd

$s = New-PSSession -ComputerName srvftp2 -Credential $credObject 

$PWDexp = $true
$PWDmod = $false
$OU = "OU=FTP_account,OU=HELPER,DC=ASSO,DC=local"
$inputfile = "D:\nfsftp\scripts\NewFtpUsers.csv"

$outputfile = "D:\nfsftp\scripts\FtpPasswordLogfile.log"

Import-Module ActiveDirectory
Import-Module WebAdministration	#  Module d'administration IIS
Import-Module NTFSSecurity		#  Module de gestion des ACL FileSystem
Import-Module MlkPwgen          #  Password Generator 

#parsing du fichier csv 
Import-Csv $inputfile -Delimiter ";" | ForEach-Object {
	

		$user = Get-ADUser -Filter {SamAccountName -eq "$($_.Login)"}
		Write-Output $user
		If (-not $user) {

            #[Reflection.Assembly]::LoadWithPartialName("System.Web")
            #$password = [System.Web.Security.Membership]::GeneratePassword(12,10)
            $password = New-Password -Length 15 -Upper -Lower -Digits -RequiredSets $@#*
            

			# build des comptes AD pour user ftp
			New-ADUser -Name $_.FullName `
				-Path $OU `
				-SamAccountName $_.Login `
				-AccountPassword (ConvertTo-SecureString $password -AsPlainText -Force) `
				-PasswordNeverExpires:$PWDexp `
				-CannotChangePassword:$PWDmod `
				-Description $_.info `
				-Enabled:$True

            Write-Output "password for $($_.login) is $($password)"
            Write-Output "password for $($_.login) is $($password)" | Out-File $outputfile -Append
            Write-Output ""
            Write-Output ""

		}
		
		$BuildPath = "$($_.Path)\$($_.Login)" # Construction du chemin physique
		$Identity = $($_.Login)
		Write-Host $Identity

		If (-not (Test-Path -Path $BuildPath)) {
			# Config Dossier physique + ACL NTFS
			mkdir $BuildPath
			Get-Item $BuildPath | Set-NTFSOwner -Account BUILTIN\Administrators
			Get-Item $BuildPath | Add-NTFSACCESS -Account ASSO\$Identity -AccessRights Modify 
            New-WebVirtualDirectory -Name ASSO\$Identity  -site “FTPASSO” -PhysicalPath $BuildPath
			Add-WebConfiguration -Filter /System.FtpServer/Security/Authorization -Value (@{AccessType="Allow"; Users="$Identity"; Permissions=3}) -PSPath IIS: -Location "FTPASSO/$Identity"
		}
		
		Get-content D:\nfsftp\scripts\IIS.txt | foreach-Object {
		
		

		Invoke-Command -Credential $credObject -ComputerName $_.  -ScriptBlock { 
			$bpath = $using:buildpath
            $id    = $using:identity
		
			If (-not (Test-Path -Path $BPath)) {
			
					Import-Module WebAdministration
                    New-WebVirtualDirectory -Name ASSO\$Id  -site “FTPASSO” -PhysicalPath $BPath
					Add-WebConfiguration -Filter /System.FtpServer/Security/Authorization -Value (@{AccessType="Allow"; Users="ASSO\$Id"; Permissions=3}) -PSPath IIS: -Location "FTPASSO/$Id"
				
			}
		} #-ArgumentList $Identity, $BuildPath 
		
		}

	} 

Open in new window

and what is the error?  Can you hard code the $Buildpath for one test?
Access is denied
    + CategoryInfo          : PermissionDenied: (D:\webroot\ftpasso\workdir:String) [Test-Path], UnauthorizedAc
   cessException
    + FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.TestPathCommand
    + PSComputerName        : srvftp2.ASSO.local
As a test try this to make sure you can connect:

$cred = (Get-Credential) 
$SB    = {Get-ChildItem -Path "C:\"}
Invoke-Command -Credential $cred -ComputerName "srvftp2.ASSO.local" -ScriptBlock $SB  -AsJob -JobName test

Open in new window


then run this:
Get-Job | Receive-Job

Open in new window

If that works. . . I would slowly add small parts of your script into that framework... starting with the Test-Path -Path $BuildPath but instead of $Buildpath use the actual path - without any variables all.
That work.

I will add  some other line step by step to show  which line is in error.
I changed my script like this:

Invoke-Command -Credential $credObject -ComputerName $IIS  -ArgumentList $inputfile  -ScriptBlock { 
		
		    param($inputfile)
            $rows= Import-Csv  $inputfile -Delimiter ";"
             
            foreach($row in $rows)
            {

			  If (-not (Test-Path -Path $row.Path)) 
              {
			
					Import-Module WebAdministration
                    New-WebVirtualDirectory -Name ASSO\$row.login  -site “FTP” -PhysicalPath $("$row.Path\$row.Login")
			    	Add-WebConfiguration -Filter /System.FtpServer/Security/Authorization -Value (@{AccessType="Allow"; Users="ASSO\$row.login"; Permissions=3}) -PSPath IIS: -Location "FTPASSO/$row.login"
			
              }
			}

Open in new window


This show me that is permission problem when I try to run remotely  the scriptblock

Access to the path 'D:\nfsftp\script\NewFtpUsers.csv' is denied.
    + CategoryInfo          : OpenError: (:) [Import-Csv], UnauthorizedAccessException
    + FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.ImportCsvCommand
    + PSComputerName        : SRVFTP3.ASSO.local

Even so D:\nfsftp\script\NewFtpUsers.csv  is  readable from the same account on other IIS computers.

Is there specific ACL (system,network service ...) to set on this path ?
Sorry I missed to specify that D:\nfsftp\script\NewFtpUsers.csv is content of $Inputfile variable.
I have had issues with ArgumentList in PowerShell 5..can you try $using... same issue?

Also, can you disable UAC on remote machine please to test.

$inputfile = "D:\nfsftp\script\NewFtpUsers.csv"
Invoke-Command -Credential $credObject -ComputerName $IIS  -ScriptBlock { 
		
		 $inputcsv = $using:inputfile
            $rows     = Import-Csv  $inputcsv -Delimiter ";"
             
            foreach($row in $rows)
            {

			  If (-not (Test-Path -Path $row.Path)) 
              {
			
					Import-Module WebAdministration
                    New-WebVirtualDirectory -Name ASSO\$row.login  -site “FTP” -PhysicalPath $("$row.Path\$row.Login")
			    	Add-WebConfiguration -Filter /System.FtpServer/Security/Authorization -Value (@{AccessType="Allow"; Users="ASSO\$row.login"; Permissions=3}) -PSPath IIS: -Location "FTPASSO/$row.login"
			
              }
			}

Open in new window

Avatar of Dan McFadden
An access denied error would indicate, to me, that the account running the script does not have proper administrative permissions on the server.

For example:  from my computer, running a powershell console as my domain admin account, I can successfully run the following command:

Invoke-Command -ComputerName "webserver.domain.com" -ScriptBlock {New-WebVirtualDirectory -Site "wwww.test.domain.com" -Name "sales" -PhysicalPath "C:\_webapps\http\www.test.domain.com\sales"}

Open in new window


When running the script, how are you executing it?  Can you test the above command (adapted for your environment) using a "Run as" powershell console?

Dan
Thak you Dan and KB,

Its same account used to run this script. Furthermore this account is member of  Enterprise Administrators AD group.

moreover d:\nfsftp is linked  filesystem ( by mklink) so that it can be reachable from any of my 4 servers. May be this is the raison of issue ?

I will test to disable UAC as suggested by KB
I recommend you test without mklink and as a domain admin.  process of elimination.
Ok i will test like this.

For me -ArgumentList work when  it precedes -ScriptBlock. Powershell ISE indicate token error when ArgumentList appears after  -scriptblock.
1. Can you post the mklink command that was used to create the link?
2. What type of link did you create?  Hard, symbolic or junction?

There are known issues using mklink.  Essentially, mklink used to create a hard link or junction to a network share will not function.

Reference link:  https://msdn.microsoft.com/en-us/library/aa365006(v=VS.85).aspx

If a symbolic link was created, it may work but you may experience oddities in functionality.  Info about symbolic links:

https://msdn.microsoft.com/en-us/library/aa365680(v=vs.85).aspx

Also, what version of Server OS is this?

Dan

Dan
I'm not sure of the command used, it was so long time. But I think it should be  mklink /d.
All serverS are Windows 2008 R2 SP1
Meaning you created symbolic links.

1.  Where does the link go, local partition or to a mapped drive/UNC path?
2.  Is the destination of the symbolic link an NTFS share or NFS share?

I asked for the command because with symbolic links, if you created the link without a trailing slash, the link has been known to throw access denied issues.  The forum post mentions the issue, but with no resolution but to delete it and recreate:

https://social.technet.microsoft.com/Forums/en-US/775f6b9b-8f69-47ae-8859-7350210264db/mklink-d-symbolic-link-created-but-file-access-is-denied-via-the-link?forum=win10itprogeneral

3.  What functionality are you trying to implement with the link?
4.  Is that where the site content is saved?

If yes to #4, there are better ways to implement.  I understand there is legacy configuration here, but sometimes it is worth noting that there may be limitations to some configuration implementations.  This just may be one of them.

Dan
Thanks you Dan,

Its UNC path from SAMBA share (by NETAPP). This is to delegate bulk account creation to another colleague.
Unfortunatly yes its where the FTP site content it stored.
I think I will work around this by copying the csvfile from d:\nfsftp  on each servers  in c:\temp and I will tell to the script to read from c:\temp.
What version of SMB is the NetApp device set to use?

Hopefully SMB v2.x or greater...

PS:  this is not a powershell issue, this is a setup issue, where the limitations of the implementation are showing its ugly head.

You may want to try to invoke the appcmd.exe command throw powershell to create the vDirs.

Let me check on the syntax.

Dan
Can you try this command?

Invoke-Command -ComputerName "webserver.domain.com" -ScriptBlock {c:\windows\system32\inetsrv\appcmd.exe add vdir /app.name:"www.test.domain.com/" /path:/test /physicalpath:C:\_webapps\http\www.test.domain.com\test}

Open in new window


Note:  the slashes are not typos.  They are necessary in the app.name and the path command switches, otherwise the command throws an error.

Dan
I will ask to my hosting provider  which version of SMB use netapp.
I will try  like you say me using appcmd.exe
Now I have modified my input csv file to swap d:\nfsftp... by  \\192.168.33.20\nfsftp...  and  this cause new error everytime  in scriptblock

Parameter 'PhysicalPath' should point to existing path.
    + CategoryInfo          : NotSpecified: (:) [New-WebVirtualDirectory], ArgumentException
    + FullyQualifiedErrorId : System.ArgumentException,Microsoft.IIs.PowerShell.Provider.NewVirtualDirectoryCommand
    + PSComputerName        : srvftp3.ASSO.local

Filename:
Error:
    + CategoryInfo          : NotSpecified: (:) [Add-WebConfiguration], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Microsoft.IIs.PowerShell.Provider.AddConfigu
   rationCommand
    + PSComputerName        : srvftp3.ASSO.local

Parameter 'PhysicalPath' should point to existing path.
    + CategoryInfo          : NotSpecified: (:) [New-WebVirtualDirectory], ArgumentException
    + FullyQualifiedErrorId : System.ArgumentException,Microsoft.IIs.PowerShell.Provider.NewVirtualDirectoryCommand
    + PSComputerName        : srvftp3.ASSO.local

Filename:
Error:
    + CategoryInfo          : NotSpecified: (:) [Add-WebConfiguration], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Microsoft.IIs.PowerShell.Provider.AddConfigu
   rationCommand
    + PSComputerName        : srvftp3.ASSO.local

Parameter 'PhysicalPath' should point to existing path.
    + CategoryInfo          : NotSpecified: (:) [New-WebVirtualDirectory], ArgumentException
    + FullyQualifiedErrorId : System.ArgumentException,Microsoft.IIs.PowerShell.Provider.NewVirtualDirectoryCommand
    + PSComputerName        : srvftp3.ASSO.local

Filename:
Error:
    + CategoryInfo          : NotSpecified: (:) [Add-WebConfiguration], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Microsoft.IIs.PowerShell.Provider.AddConfigu
   rationCommand
    + PSComputerName        : srvftp3.ASSO.local
This is realy hard to use appcmd in powershell because powershell fail to run script and say me "Unexpected token add"  at this line

$appCmd add vdir /app.name:"FTPASSO/" /path:/ASSO/$row.login /physicalPath:$("$row.Path\$row.Login")

Open in new window

$appCmd is like this

$appCmd = "c:\windows\system32\inetsrv\appcmd.exe"

Open in new window

Before trying to integrate the command in your script, I recommend first manually testing the command to see if it resolves your issue.  Then the scripting issues can be addressed.

Dan
Yes one shot command appcmd without variable it works
ASKER CERTIFIED SOLUTION
Avatar of Dan McFadden
Dan McFadden
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thank you Dan

I rewriting the script as you suggest me.

How can I add in scriptblock the following config ?

Add-WebConfiguration -Filter /System.FtpServer/Security/Authorization -Value (@{AccessType="Allow"; Users="$Identity"; Permissions=3}) -PSPath IIS: -Location "FTPASSO/$Identity"

Open in new window


Is there a way to do it with appcmd  instead of  .net  module ?
Again, build the command line switch variables first, then build the command line:

$FilterValue = "/System.FtpServer/Security/Authorization"
$AuthValue = @{AccessType="Allow"; Users="$Identity"; Permissions=3}
$PSPathValue = "IIS:"
$LocationValue = "FTPASSO/$Identity"
Add-WebConfiguration -Filter $FilterValue -Value $AuthValue -PSPath $PSPathValue -Location $LocationValue

Open in new window


I find this the safest and easiest way to assemble commands.  It also help in troubleshooting yoru scripts.

Dan
Thank you so much Dan. It work fine like this.

I was forced to use appcmd into the scriptblock to set authorization rule because Add-webconfiguration did not work (Insufficient permissions ...)