Thursday, February 26, 2009

I have spent some time on forums inquiring about the ability to transfer files between machines using PowerShell Remoting. I have an inkling I've been asking the wrong question. Until I have PowerShell Remoting configured correctly I'm not going to be able to do any useful experimentation.

Now, there are some breaking changes to PowerShell in v2 CTP3. For example Invoke-Expression no longer has a ComputerName parameter; this is now the domain of Invoke-Command. It makes searching for useful information on Remoting a little more interesting. So lets have a look at the result of issuing a remote Get-Process:

PS C:\Windows\System32> invoke-command -computername w2k8r2 {get-process}Invoke-Command : [w2k8r2] Connecting to remote server failed with the following error message : The WinRM client cannot processthe request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, thenHTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Usewinrm.cmd to configure TrustedHosts. You can get more information about that by running the following command: winrm help config.At line:1 char:15+ invoke-command <<<< -computername w2k8r2 {get-process} + CategoryInfo : OpenError: (:) [Invoke-Command], PSRemotingTransportException + FullyQualifiedErrorId : RemoteRunspaceStateBroken

Fair enough, neither the client nor the server is part of a domain. Adding the destination machine to the TrustedHosts configuration setting is generally accompanied by warnings not to use this option for a production environment. That leaves me the option of using HTTPS and that requires a certificate. For my purposes a self-signed certificate is going to be perfectly adequate.

Server Activation

As an aside, I've not been able to activate my new server. Whenever I tried I would get error 0x80072EE7. The penny finally dropped - without a DNS server there is no way to identify the IP address of the Microsoft site used for activation, doh. So lets get that out of the way. The product key for the server is available on the Windows Server 2008 R2 Beta download page:

MakeCert

Sweet, now back to the task at hand. I'm using information from Brian Ehlert's IT Proctology to configure WinRM with a self-signed certificate. I don't have a copy of SelfSSL.exe but I do have two copies of makecert.exe. Past experience tells me all makecert.exe's are not created equal and as usual the first one I try fails. So the version I'm using is C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\makecert.exe which I suspect was installed with Visual Studio. Attempting to run makecert on my server doesn't work:

WoW64

Obviously I'm missing something. WoW64 support for 32 bit applications is now an optional feature in Server Core and isn't installed by default. I also have the 32-bit .Net Frameworks installed so I figure I might as well include support for that. And a restart is required.

Using PowerShell to eject CD/DVD

Another short aside, the Mac Mini I'm using for my web server doesn't have an eject button. As I mentioned way back here it is possible to eject media by holding down the left mouse button during a restart. Even for a non-server environment this isn't particularly practical. Googling turned up a lot of suggestions around scripting the Windows media player, not an option for Server Core. I finally tracked down a workable solution (and useful explanation) on the vistax64 PowerShell forum. Note in the script below there is no ampersand in the verb Eject. This is contrary to the forum post and had me scratching my head for a while - I could hear the drive being accessed but the DVD wasn't being ejected. Notice also the call to ReleaseComObject and Remove-Variable, a little bit of tidying up to ensure the Shell.Application COM object is disposed of and the associated object reference is deleted:

Creating a self-signed certificate

So lets see about creating a certificate. I need to create one that can be used for Server Authentication and because it's not from an existing Trusted Certificate Authority I'm going to also install it into the Trusted Root Certification Authorities store using the command line utility certutil.exe.

Configuring WinRM

The certificate hash from the previous step is the thumbprint required for setting up a new WinRM listener. Note in the next step we're not using PowerShell, just the standard command prompt. Trying to issue this same command in PowerShell gives Error: Invalid use of command line. Type "winrm -?" for help. I figure there must be some escaping needed to get it to work in PowerShell, something to look at another day.

Cool, now lets see if I can get a file transferred between the server and my Vista VM. I specify a -ReadCount argument of 0 to indicate the entire contents should be sent at one time (the default is 1 line and for a binary file is extremely slow):

Wednesday, February 25, 2009

Remote management of IIS 7 would be nice. I spent some time trying to figure out why the IIS Management Console on Vista didn't have the option to add a new connection to my new Win2K8 server. Turns out I need Internet Information Services (IIS) 7.0 Manager. Installing and starting it displays a substantially different UI.

On the server, the Web Management Service needs to be started. Also, checking the firewall configuration on the server reveals there is open access through the firewall for the service. I'm going to set the startup for the service to automatic and I'm also going to restrict incoming traffic to the local subnet:

Now I can connect through the firewall but on my first connection attempt I get a Server Certificate Alert because I don't have the servers self-signed certificate in my trusted certificate store. Viewing the certificate provides an option to install it into my local store which is nice. Sadly the remote server is not accepting connections. So how to enable remote management for IIS. Googling reveals any number of helpful pictures of where in the IIS Management Console to tick the box enabling remote management. Unfortunately there isn't any obvious information about how to do this on Core. I'm having a wee stab in the dark using regedit to modify the entry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WebManagement\Server\EnableRemoteManagement, changing the REG_DWORD value from 0 to 1. Restart the Web Management Service and I can now successfully add the server as a new connection. Unfortunately when I open that connection there are only three options available to me; Feature Delegation; IIS Manager Permissions; and IIS Manager Permissions.

This is an issue with how clr looks for managed assemblies - in vista SP1, Microsoft.Web.Management.IisClient, version=7.0.0.0 (and AspnetClient) are also installed to the inetsrv directory along with being in the GAC - now when connecting to win7, version 7.5 of these dlls are downloaded, but when trying to load them, CLR first finds the 7.0 versions in the inetsrv directory and complains that the version does not match. The fix in vista sp2 is just to make sure that the 7.0 versions are not installed to inetsrv directory.

I created a sub directory C:\Windows\System32\inetsrv\Microsoft.Web and moved Microsoft.Web.*.dll into that directory. I did have to take ownership of those particular files and then allow administrators full control. But, yes it does work and I now have access to the complete list of features in iis7 on my remote server.

Monday, February 16, 2009

The journey to administration of IIS 7 on Windows Web Server 2008 R2 Core has not been a happy time. Hopefully perseverance will eventually pay some dividends. So my objective right now is to install the IIS 7 PowerShell snap-in. I know where it is: http://66.129.69.178/downloads/files/powershell/iis7psprov_x64_rc1.msi. I just need to download it to my server. Note my server doesn't currently have a DNS entry which means providing a hostname is just not going to work, I need to replace www.iis.net with the equivalent IP Address 66.129.69.178. Google throws up a number of wget scripts; after trying various versions and failing to get anything to retrieve the file I went back to basics and used a page from B#.Net Blog. This uses .Net Framework class System.Net.WebClient to retrieve the file. The problem with most of the scripts I found is that error reporting is generally left to the default PowerShell Exception report which by and large is not particularly useful:

In order to get a little more detail I need to trap the exception and delve into it to find out what caused it. This is relatively straight forward using the trap mechanism. To begin with lets display the methods available on the Exception object.

As I said, a fair amount of output. But it does contain some useful information - Error creating the Web Proxy specified in the 'system.net/defaultProxy' configuration section. ---> System.DllNotFoundException: Unable to load DLL 'rasapi32.dll'. I guess I should have read the release notes:

If you run applications that use managed code that communicates with the Internet with autoproxydetection, the operation will fail with an unhandled exception that mentions an error creating theWeb proxy because Rasapi32.dll could not be found.

To correct this, open the Machine.config file (default location isC:\Windows\Microsoft.NET\Framework64\v2.0.50727\CONFIG), locate the final closing </configuration>tag, and append the following:

where <replace_with_proxy_address> is the address and port, if needed, of the proxy serverused by the client application to access the .NET application. For example, proxyaddress="<http://proxyserver:80>".

I'm not actually using a proxy server, what I want to do is avoid the system trying to load the rasapi32.dll. What I need to do is somehow indicate that I don't wish to use the default proxy, whatever that is defined as, when I'm making my WebClient.DownloadFile() request. The correct sequence of calls is then:

This next step ends up just being stupid, annoying, insulting, you get the picture, not happy. Installing the IIS 7.0 PowerShell Snap-in effectively says "go get the installer and run the installer". So I've finally got the installer. I go ahead and run the installer and the installation fails with an error IIS Version 7.0 or greater is required to install Microsoft Windows PowerShell provider for IIS 7.0. I'm running Windows Server 2008 r2, I've got IIS 7 installed. A forum post adds fuel to the fire, particularly this post:

This is "by design". What you have in Win7 beta is practically the same code as RC of powershell snapin, which is for downlevel versions only.

Really sucks. If something like this is "by design" then it should be documented somewhere obvious. This is just a waste of time for anyone who is trying to get enthusiastic about using this stuff. The workaround is also documented in the thread (thanks 13xforever), change minor version of IIS from 5 to 0 in HKLM:\SOFTWARE\Microsoft\InetStp (and back again after the installation is complete). Now I need to let PowerShell run scripts:

And to make my life easier on Core I've created $home\Bin\IIS.cmd containing the Target: for the IIS PowerShell Management Console menu item shortcut installed with the snap-in. Unfortunately I can't post that information because blogger thinks I'm doing something dodgy and removes the offending code and the rest of the post (I guess if you've read this far you might consider that a bonus). I've added the Bin folder to the PATH environment variable. To avoid adding the Administrator's Bin folder to the path for all users it's first necessary to locate the appropriate registry key for the Administrator's environment variables. On my server it looks something like HKEY_USERS\S-1-5-21-1234567890-1234567890-1234567890-500\Environment. Then it's just a case of adding a new Expandable String Value called PATH and giving it a value of %PATH%;%UserProfile%\Bin. The command prompt needs to be restarted in order for the change to take effect.

To cap off a fairly unproductive day, I've just been reminded that I shouldn't be using the Administrator account for administration. Absolutely correct and in fact the Administrator account should be renamed and disabled. Way back in Installing Windows Server 2008 Core part 2 I created another administrative account. Time to start using it. This means I need to move my Bin directory and apply the registry changes to a different user profile. Finally:

Sunday, February 15, 2009

So I want to be able to administer IIS 7 remotely using PowerShell. Installation on W2K8r2 is easy via start /w ocsetup MicrosoftWindowsPowerShell. And there is a PowerShell Snap-in for IIS 7.0. And in case you're wondering, the location of powershell.exe is added to the path during installation, you just need to restart the console for it to take effect (i.e. there is no need to try to hack the registry in order to add the location of powershell.exe to the PATH environment variable). So a few common commands to get started:

Help

Get help for a particular cmdlet, e.g. help get-process.

Get-Alias

Returns a list of aliases for PowerShell cmdlets.

Get-Command

Returns a list of PowerShell cmdlets.

Get-Process

Returns a list of all the running processes

Get-Service | sort status,name

Get-Service | Where-object{$_.status -like "running"}

Get a list of services - in the first case sort by status and name, in the second case filter output to include only running services

Now it would be kind of cool to be able to use the new Windows PowerShell Integrated Scripting Environment to administer the server. Turns out in some cases it just works. Remoting however requires the Windows PowerShell V2 Community Technology Preview 3 (CTP3) on your Vista box. Check out the help text related to remoting:

PS C:\> help about_remote_requirementsTOPIC about_remote_requirements

...

NOTE: Many cmdlets, including the Get-Service, Get-Process, Get-WMIObject, Get-Eventlog and Get-Event cmdlets get objects from remote computers, but they use .NET methods to retrieve the objects. They do not use the Windows PowerShell remoting infrastructure. The requirements in this document do not apply to these cmdlets.

Currently the WinRM service is stopped on the server. Looks like I need to do some configuration:

CONFIGURE WS-MANAGEMENT

The remoting features of Windows PowerShell are supported by the WinRM service, which is the Microsoft implementation of the WS-Management protocol. To use the remoting features, you need to change the default configuration of WS-Management on the system.

Windows PowerShell provides a script to configure WS-Management. The script is located in the Windows PowerShell installation directory ($pshome).

PS C:\> winrm quickconfigWinRM already is set up to receive requests on this machine.WinRM is not set up to allow remote access to this machine for management.The following changes must be made:

Create a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine.Enable the WinRM firewall exception.Configure LocalAccountTokenFilterPolicy to grant administrative rights remotely to local users.

Make these changes [y/n]? y

WinRM has been updated for remote management.

Created a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine.WinRM firewall exception enabled.Configured LocalAccountTokenFilterPolicy to grant administrative rights remotely to local users.

Cool, that all looks good. If only the same thing worked on Vista:

PS C:\> winrm quickconfigWinRM is not set up to allow remote access to this machine for management.The following changes must be made:

Set the WinRM service type to delayed auto start. Start the WinRM service. Create a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine. Enable the WinRM firewall exception.

PS C: Enable-PSRemotingWindows PowerShell remoting features are not enabled or not supported on this machine.This may be because you do not have the correct version of WS-Management installed or this version of Windows does not support remoting currently. For more information, type 'get-help about_remote_requirements'.At line:13 char:37

But I know where to get the WinRM CTP. This is a Windows Update and requires a restart. Now things are looking up:

PS C:\Windows\System32> enable-psremotingWinRM has been updated to receive requests.WinRM service type changed successfully. WinRM service started. Configured LocalAccountTokenFilterPolicy to grant administrative rights remotely to local users.

WinRM has been updated for remote management.Created a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine. WinRM firewall exception enabled.

But I'm not quite there yet:

PS C:\Windows\System32> Enter-PSSession -computerName myServerEnter-PSSession : Connecting to remote server failed with the following error message : The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. You can get more information about that by running the following command: winrm help config.At line:1 char:16+ Enter-PSSession <<<< -computerName myServer + CategoryInfo : InvalidArgument: (myServer:String) [Enter-PSSession], PSRemotingTransportException + FullyQualifiedErrorId : CreateRemoteRunspaceFailed,Microsoft.PowerShell.Commands.EnterPSSessionCommand

So no joy. I need to remove the HTTP Listener and add an HTTPS Listener. I'm going to save that exercise for another post - this one's starting to go feral. Right now I'm more interested in getting the IIS PowerShell snap-in installed so I can start managing IIS. If you've read this far only to discover there is no resolution I apologise and I hope you appreciate that I share your frustration.

Wading through each of the options should give some indication of what I need to install. Be warned that a sinlge misplaced character will cause the install to fail and tracking that sucker down can be a right pain. When the install has completed the last error can be checked to ensure the install was successful. An error code of 0 indicates success, an error code of -2146498548 indicates you might have accidentally typed an s on the end of IIS-ManagementService which annoyingly happens to be the last item in the command:

C:\> echo %errorlevel%

IIS-WebServerRole

The Web server that provides the Web application infrastructure for all versions of the Windows operating system.

IIS-WebServer

Installs the IIS Web server, which enables support for HTML Web sites, and the optional support for ASP.NET, classic ASP, and Web server extensions.

IIS-CommonHttpFeatures

Installs support for static Web server content, such as for HTML and image files, for custom errors, and for redirections.

IIS-StaticContent

Serves the HTM, the HTML, and the image files from a Web site.

IIS-DefaultDocument

Enables the specification of a default file, which is loaded if a user does not specify a file in a request URL.

IIS-HttpErrors

Installs the HTTP error files and enables the customization of the error messages.

IIS-HttpRedirect

Supports client request redirections.

IIS-ASPNET

Enables support for ASP.NET applications.

IIS-NetFxExtensibility

Enables a Web server to support the .NET Framework-managed module extensions.

Monday, February 9, 2009

So not exactly square one but time to repeat a couple of steps including configuring an IP address, changing the computer name, and enabling Remote Desktop. And why does the command prompt disable QuickEdit Mode by default — would anyone complain if Microsoft took it upon themselves to set some reasonable default settings?

Having installed R2, getting PowerShell is easy:

C:\> start /w ocsetup MicrosoftWindowsPowerShell

And starting it:

C:\> windows\system32\windowspowershell\v1.0\powershell.exe

The TechNet blog indicates .Net 2.0 is an optional install on Core. It's required for PowerShell and it is installed along with PowerShell as a prereq if it isn't already installed. And while I'm doing the .Net thing I might as well install 3.0 and 3.5 (note the command prompt, not PowerShell):

C:\> start /w ocsetup NetFx3-ServerCore

Set the date/time/timezone:

C:\> control timedate.cpl

And the international settings:

C:\> control intl.cpl

Allow the Event Viewer MMC snap-in to connect through the Windows Firewall. As an aside, I originally copied the following command from a TechNet webpage and when I ran it I was getting an error Group cannot be specified with other identification conditions. It turns out the quotes used on the webpage were the issue; replacing them with the standard double quotes fixed the problem:

And the same for Services, Windows Firewall with Advanced Security. I would kind of like to know how to restrict the MMC connectivity to particular machines on the local network, something to add to my TODO list. Also note the group name Remote Service Management has Service singular. The sites I've been referencing, including TechNet, all have this as Services plural:

After some digging I believe I can now at least restrict MMC connectivity to my local subnet. This obviously isn't a panacaea but does adhere to the principle of defence in depth. Note these commands can't be applied to a rules group but must instead be applied to each individual rule in the group:

Managing services remotely from my Vista VM using the Services snap-in doesn't provide an option to specify alternate credentials and as a consequence the connection fails with access denied. The convenient workaround for this is to add an administrative account with the same username and password as my Vista account to Win2K8:

C:\> net user dave letmein /add

C:\> net localgroup Administrators /add dave

So now I have convenient access to view the event log on the server and a familiar interface for managing services. So I might as well take the opportunity to disable DHCP, DNS Client, TCP/IP NetBIOS Helper.

Monday, February 2, 2009

As a Mac-using Windows developer I have had for good measure a small and extremely low traffic OpenBSD web server running on an Apple Cube. It's been a fun project. The Cube happily runs headless on my shelf and lacking a fan is extremely quiet. I was intending to replace it with an Intel Mac Mini which is slightly louder but also a nice form factor and has a bit more grunt. Now I find myself needing to upskill my knowledge of Windows server products. I have in the past been responsible for maintaining and securing Internet-facing Windows 2003 servers. Time to transition to Windows Server 2008 Core Web.

An iso for W2K8 Web Server is available for download here. Leopard's Disk Utility can be used to burn the iso to a DVD. Microsoft allows the use of the software for trial purposes for a period of 60 days and provides instructions to extend that for a further 180 days. The attraction of the Web edition of W2K8 is the lowish price compared to their other server products. The limitation is the inability to run any Microsoft server products except IIS. For my purposes the limitation is not an issue.

So armed with the DVD, the next step is to partition the Mini using Boot Camp Assistant and boot from the install DVD. The install process is short and sweet, and fairly quickly the Windows login prompt is displayed. Unfortunately there's no indication what the initial username and password might be. Without referring to the documentation my first guess was that the initially entered values for username and password would be used as the identity for the administrator. No joy, no joy, fail. My second guess was correct, a username of Administrator and a blank password were accepted and a change password form displayed.

So far so good. Unfortunately it's not as simple as unplugging the display, keyboard, and mouse from the Mini. The Mini requires a video signal in order to boot successfully. A simple hack using a resistor is required, details are available from Blackfriars.

OK, at this point I've just discovered that ASP.Net is not supported on Server Core. That seems like a fairly major oversight and a little googling reveals it will be supported in Windows Server 2008 R2 which is currently in beta. So time to go back and download the beta, which succeeded finally after 4 failed attempts I can only ascribe to gremlins.

Hurdles coming thick and fast now. There's no eject button on a Mac Mini; how to extract the disk currently resident in the drive? Sadly there is no obvious mechanism to do this from a command prompt and there are many vb scripts that instantiate a media player and issue it a command to eject the drive — not feasible on Windows 2008 Server Core. Fortunately there is a simple Mac mechanism that only requires the Mini to be restarted while holding down the left mouse button. Not exactly intuitive but achieves the desired result. Next, attempting to boot the Mini from the new disk displayed a prompt "Select CD-ROM Boot Type :" and no matter how hard I press either "1" or "2" the installation progresses no further. Without going into detail there is a solution. The first step is to download oscdimg.exe. This .exe is also available in the Windows Automated Installation Kit which is a 992.2 MB download. Next and using instructions borrowed from Sergio Mcfly, assuming your Parallels Vista install maps D:\ to the DVD drive in your MBP, run the following command oscdimg.exe -n -m -bd:\boot\etfsboot.com d:\ c:\w2k8r2Web.iso. Burn the resulting iso to a new DVD and the Mini now boots from the install disk.

This time, with a clean install, I don't have to guess the Administrator username. Instead I'm immediately prompted to set the password for Administrator and I now have a command prompt and the desktop background informs me I'm running Windows Web Server 7 For testing purposes only. Build 7000.