Monday, October 19, 2015

vSphere Replication Unable to obtain SSL certificate – Bad Server Response

While working on deploying vSphere 6.1 Replication appliances I got stuck on configuring the new appliance. After setting up the lookupservice address and clicking Save And Restart Services I ended up getting an error:

Unable to obtain SSL certificate: Bad server response; is a LookupService listening on the given address?

Some googling led me to:

http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2085155

The VMWare KB did not help as the vcenter and the new appliance could resolve each others by the FQDN.

Then I came across:

http://www.davidhill.co/2015/03/vsphere-replication-unable-to-obtain-ssl-certificate/

This was an interesting post since he mentions we need to enter the address for the LookupService Address and this does not match the actual VMWare documention which says the following:

In the LookupService Address text box, enter the IP address or domain name of the server where the lookup service runs.

Using the vcenter address did not work for me.  However, using the platform service controllers FQDN did work.  The configuration ended up working for me with either the FQDN of the Platform Services Controller or the full URL to the Lookup Service also pointing at the PSC.  So if you are in these shoes try:

[FQDN_vCenter_Platform_Services_Controller]

 and if that does not work, you can also try it with the full address:

https://[FQDN_vCenter_Platform_Services_Controller]:7444/lookupservice/sdk

Friday, September 20, 2013

Removing Outlook Email Attachments and Replace Them with a Links to a Saved Location

I have come across the need to strip out email attachments from Outlook a number of times in the past but never wanted to pay for a solution.  If you are in the same boat here is a method to do this for free.  There are a number of variations on this VBA script out there, just google the first line of the VBA code to find them.

My biggest issue with this method is that I never figured out a way to deploy it to all of my users at once but I still found this very useful for the big offenders and for keeping my own email box size under control.

Without further ado I present:

A VBA Macro for:

1. Removing attachments of selected messages (one or many) and saving them to a Local/Mapped drive location

2. Adding a link to the bottom of each email to the saved attachment files.

How to use it:

1. Add Developer Tools to the Outlook Ribbon:

 

 



2. Set Macro Security to “Prompt for All”




3. Create a New Macro. Title it “SaveAttachmentsSelected” (You open the window the type the name and click “Create”)





4. Replace everything in the window that appears with the contents of the code below.



5. Click save and close the VBA editor.

6. Add a “Quick Access Toolbar” link to the Macro









7. Find an email to use as a test (make sure it’s something you won’t miss in case there is a problem.)

8. Select the email, then click the Macro icon in the Toolbar



9. Check out the body of the email….it should have links to the attachment(s) at the very bottom of the email message. Make sure they work.

10. To do multiple emails, just select multiple items.

Be patient with this method. When you select a large number of emails just sit back and give Outlook time to crank through everything. Start with small batches so you can get a feel for how it works and the time it takes to save the files.
Sub SaveAttachmentsSelected()
Dim filesys, newfolder
Dim objOL As Outlook.Application
Dim pobjMsg As Outlook.MailItem 'Object
Dim objSelection As Outlook.Selection
Dim strFolderpath As String

'SETTINGS HERE AND THERE IS ANOTHER SET FURTHER DOWN
'I had issues using the HOMEDRIVE environmental variable so I hard coded this to the AD user drive.
'Feel free to try the other options as they might work for you
strFolderpath = "U:"

' Get the path to your Home Direcorty folder
'strFolderpath = Environ("HOMEDRIVE")

' The following line sets the base location as My Documents
'strFolderpath = CreateObject("WScript.Shell").SpecialFolders(16)
On Error Resume Next

strFolderpath = strFolderpath & "\Outlook_Attachments\"

'Create Base Directory
Set filesys = CreateObject("Scripting.FileSystemObject")
If Not filesys.FolderExists(strFolderpath) Then
   newfolder = filesys.CreateFolder(strFolderpath)
End If

' Instantiate an Outlook Application object.
Set objOL = CreateObject("Outlook.Application")

' Get the collection of selected objects.
Set objSelection = objOL.ActiveExplorer.Selection

For Each pobjMsg In objSelection
SaveAttachments_Parameter pobjMsg
Next

ExitSub:

Set pobjMsg = Nothing
Set objSelection = Nothing
Set objOL = Nothing
End Sub

Public Sub SaveAttachments_Parameter(objMsg As MailItem)
Dim filesys, newfolder, colProcessEnvVars
Dim objAttachments As Outlook.Attachments
Dim i As Long
Dim lngCount As Long
Dim HomeDir As String
Dim strFile As String
Dim strFolderpath As String
Dim strDeletedFiles As String

'SETTINGS HERE AND THERE IS ANOTHER SET ABOVE
'I had issues using the HOMEDRIVE environmental variable so I hard coded this to the AD user drive.
'Feel free to try the other options as they might work for you
strFolderpath = "U:"

' Get the path to your Home Direcorty folder
'strFolderpath = Environ("HOMEDRIVE")

' The following line sets the base location as My Documents
'strFolderpath = CreateObject("WScript.Shell").SpecialFolders(16)

On Error Resume Next

' Set/Create the Attachment folder. 
' This Adds a folder structure which places attachements in folders based on Year and Month
strFolderpath = strFolderpath & "\Outlook_Attachments\" & Format(objMsg.ReceivedTime, "yyyy-MM") & "\"

Set filesys = CreateObject("Scripting.FileSystemObject")
If Not filesys.FolderExists(strFolderpath) Then
   newfolder = filesys.CreateFolder(strFolderpath)
End If

' Get the Attachments collection of the item.
Set objAttachments = objMsg.Attachments
lngCount = objAttachments.Count

If lngCount > 0 Then

' We need to use a count down loop for removing items
' from a collection. Otherwise, the loop counter gets
' confused and only every other item is removed.
For i = lngCount To 1 Step -1

' Save attachment before deleting from item.
' Get the file name.
strFile = objAttachments.Item(i).FileName

' Combine with the path to the Temp folder.
' This Creates a filename which includes the senders name, the date the email was sent, and the filename.
strFile = strFolderpath & objMsg.SenderName & "_" & Format(objMsg.ReceivedTime, "yyyy-MM-dd-h-mm-ss") & "_" & i & "_" & strFile

' Save the attachment as a file.
objAttachments.Item(i).SaveAsFile strFile

' Delete the attachment.
objAttachments.Item(i).Delete

'write the save as path to a string to add to the message
'check for html and use html tags in link
If objMsg.BodyFormat <> olFormatHTML Then
strDeletedFiles = strDeletedFiles & vbCrLf & "<file://" & strFile & ">"
Else
strDeletedFiles = strDeletedFiles & "<br>" & "<a href='file://" & strFile & "'>" & strFile & "</a>"
End If
Next i
End If

' Adds the filename string to the message body and save it
' Check for HTML body
If objMsg.BodyFormat <> olFormatHTML Then
objMsg.Body = objMsg.Body & vbCrLf & vbCrLf & "The attachment(s) were saved to " & strDeletedFiles
Else
objMsg.HTMLBody = objMsg.HTMLBody & "<br>" & "<br>" & "The attachment(s) were saved to " & strDeletedFiles & ""
End If
objMsg.Save
ExitSub:

Set objAttachments = Nothing
Set objMsg = Nothing
Set objOL = Nothing
End Sub

Monday, April 1, 2013

Powershell - RunAsAdminAlways Function

Here is a function I use in many of my powershell scripts to check if they have been launched with elevated privileges. If it has then it just changes the window color and title. If it has not it is relaunched with those privileges.

If you create a PS1 file with all the code below and then replace everything below the "Stuff to be run in an elevated prompt goes below here." comment it will ensure that code is executed in an elevated command prompt.

Of course you could also always grab just the function and call it from one of your own existing script(s).

Function RunAsAdminAlways ($command) {
#*=============================================
#* Function: RunAsAdminAlways
#* Created: [02/15/2011]
#* Author: Charles Ulrich
#* Arguments: Should always be called with 
#* $myInvocation.MyCommand.Definition as the only argument
#* Ex: $y = RunAsAdminAlways ($myInvocation.MyCommand.Definition)
#*=============================================
#* Purpose: Checks if script is running as admin
#* and relaunches if not.
#*
#*=============================================

# 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
  
# Check to see if we are currently running "as Administrator"
if ($myWindowsPrincipal.IsInRole($adminRole))
    {
    # We are running "as Administrator" - so change the title and background color to indicate this
    $Host.UI.RawUI.WindowTitle = $command + "(Elevated)"
    $Host.UI.RawUI.BackgroundColor = "DarkBlue"
    clear-host
    }
else
    {
    # We are not running "as Administrator" - so relaunch as administrator
    Start-Process powershell -ArgumentList $command -verb "runas"
    
    # Exit from the current, unelevated, process
    exit
    }
}

$y = RunAsAdminAlways ($myInvocation.MyCommand.Definition)

# Stuff to be run in an elevated prompt goes below here.

Write-Host "Looks Good!"
Write-Host "Press any key to continue."

$z = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

Monday, March 11, 2013

Lync Server 2013 - Bulk Updating Contact Groups

I have been working on a rollout of Lync 2013 at my company over the last couple weeks. We are moving from an OpenFire/Spark setup to Lync mostly because we wanted to give our users the ability to do video chats and screen share's easily and Spark did not really provide this functionality for us.

The biggest thing we found lacking in Lync 2013 is no central management of Contact Groups. With Spark we setup the groups and they are loaded whenever a user signs on.



Our users really like having this list and we did not want them to have to manually configure all of the groups. I was not able to find any specifics of exactly how to do this on the web so I decided to roll up my sleeves and do it myself.  There are a few more things I would like to add to it and clean up but I wanted to get it out in the wild for other people to use.

Currently this script will only update the Contact Groups for a user who has logged into the server at least once. Additionally if the user has already added some of the groups in the past it will retain the original order of the previously added groups and append the new ones on at the bottom.

You will need 7-Zip installed on your link server to run this script.

Here are the basic steps to use the script:
  1. Create a reference account and add the Contact Groups you want to load for your other users.

  2. Log into your Lync Server and from the Lync Management Shell export the user you updated with the following command.

    Export-CsUserData -PoolFqdn "YOURLINKPOOL" -UserFilter first.last@something.com -FileName "c:\temp\ExportedUserData.zip"
    

  3. Extract the Zip file and open the DocItemSet.xml file.  I recommend using Notepad++ with the XML Tools Plug to format that file into something readable.

    We are interested in the "ContactGroups" XML node.  Each ContactGroup entry in this list corresponds to one of the contact groups you added to the reference account.  Copy and paste this out of the XML file.  It should look something like this:

     
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
     
    
  4. You're ready to copy the script below. Update variables as you see fit in the the Settings section on top. You will definitely have to update the following variables:

    $lyncpoolfqdn - to your own FQDN

    $replacement - paste your default ContactGroups XML in place of my sanitzed version


##########################################################################################################################
#
#    Name:            Update_Lync_2013_Groups.ps1    
#    Author:         Charles Ulrich
#    Date:            03/08/2013
#    Description:    Script to update the Contact List for 1 or All Lync 2013 users.
#
#    Requirements:    1. MUST BE RUN ON LYNC SERVER - Export-CSUserData does not like Remote Shell or Implicit Remoting
#                    2. 7-Zip must be installed - Used to manage the ZIP files from the export and needed for the import.
#                    3. If not running under Powershell v3.0 see comments in MultipleSelectionBox function.
#
#
##########################################################################################################################

# Settings

#Lync Server
$lyncpoolfqdn = "YOURLINKSERVERPOOL"

#Base File Path
$BaseFilePath = "c:\scripts\lync_user_export"

#7-Zip Paths
$7ZipPath = "c:\Program Files\7-zip\7z.exe"
$7ZipOutputDir = $BaseFilePath + "\working"
$7ZipOutputParam = "-o" + $7ZipOutputDir
$7ZipIncludeFiles = $7ZipOutputDir + "\*.xml"

#Lync XML and ZIP file paths
$LyncXMLFile = $7ZipOutputDir + "\DocItemSet.xml"
$ExportFileNamePath = $BaseFilePath + "\ExportedUserData.zip"
$UpdatedFileNamePath = $BaseFilePath + "\UpdatedUserData.zip"

# replacement node with child nodes
[xml]$replacement = @'
    <ContactGroups>
      <ContactGroup Number="1" DisplayName="fg=="/>
      <ContactGroup Number="2" DisplayName="WJvopsjer3ojfaopj" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="3" DisplayName="SFDEW3523j" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop39357673425hdfbsart3462twqgaedfgajoisdfoipqhuy38tpy32u980hfiaosdhiogpkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="4" DisplayName="35uyh" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="5" DisplayName="fbdsfh346" ExternalUri="Pfaohiweifgouwe27y0ahsvokja;sdC9n3904kjlkJKSL904="/>
      <ContactGroup Number="6" DisplayName="sfdhw3466" ExternalUri="Pfaohvbasrtyq4tyasv58osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="7" DisplayName="asetg4363y6" ExternalUri="Pfaohiweifgouwe90vsdgwerhjudfbsdfkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="8" DisplayName="634tsagasdg" ExternalUri="Pfaohidsvsdgherhysdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="9" DisplayName="ngfd56w3" ExternalUri="Pfaohiweifgouwe90ufjdasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="10" DisplayName="asdgsa46" ExternalUri="Pfaohiweifgouwe90ufSDfsdtewtgvjopaiwejr302582385ioskjgl;skdaopsok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="11" DisplayName="agasg" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="12" DisplayName="235wgsdfa" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="13" DisplayName="236gvbasetaw" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="14" DisplayName="asdgawqt3aqw" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
      <ContactGroup Number="15" DisplayName="casdrwqt5" ExternalUri="Pfaohiweifgouwe90ufjSDAW$jop3958osdkpodasok32480mVkR3JvdXAiPjxlbWFpbC8+PC9n3904kjlkJKSL904="/>
    </ContactGroups>
'@


Function MultipleSelectionBox ($inputarray,$prompt,$listboxtype) {

# Taken from Technet - http://technet.microsoft.com/en-us/library/ff730950.aspx
# This version has been updated to work with Powershell v3.0.
# Had top replace $x with $Script:x throughout the function to make it work. 
# This specifies the scope of the X variable.  Not sure why this is needed for v3.
# http://social.technet.microsoft.com/Forums/en-SG/winserverpowershell/thread/bc95fb6c-c583-47c3-94c1-f0d3abe1fafc

$Script:x = @()

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 

$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = $prompt
$objForm.Size = New-Object System.Drawing.Size(300,600) 
$objForm.StartPosition = "CenterScreen"

$objForm.KeyPreview = $True

$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") 
    {
        foreach ($objItem in $objListbox.SelectedItems)
            {$Script:x += $objItem}
        $objForm.Close()
    }
    })

$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") 
    {$objForm.Close()}})

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,520)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"

$OKButton.Add_Click(
   {
        foreach ($objItem in $objListbox.SelectedItems)
            {$Script:x += $objItem}
        $objForm.Close()
   })

$objForm.Controls.Add($OKButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,520)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20) 
$objLabel.Size = New-Object System.Drawing.Size(280,20) 
$objLabel.Text = "Please make a selection from the list below:"
$objForm.Controls.Add($objLabel) 

$objListbox = New-Object System.Windows.Forms.Listbox 
$objListbox.Location = New-Object System.Drawing.Size(10,40) 
$objListbox.Size = New-Object System.Drawing.Size(260,20) 

$objListbox.SelectionMode = $listboxtype

$inputarray | ForEach-Object {[void] $objListbox.Items.Add($_)}

$objListbox.Height = 470
$objForm.Controls.Add($objListbox) 
$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

Return $Script:x
}


#Load Lync Powershell Commands
Import-Module Lync

#Who should we update
$sora = Read-Host "Do you want to update (A)ll users or a (S)ingle user? (S or A)"

If ($sora -eq "S")
    {
        # Get user to update 
        $userlist = Get-CSUser
        $user_email=MultipleSelectionBox $userlist.UserPrincipalName "Choose Lync 2013 User" "One"
        
        #Export Single Users Data
        Export-CsUserData -PoolFqdn $lyncpoolfqdn -UserFilter $user_email -FileName $ExportFileNamePath
    }
Else
    {
        #Export All Users Data
        Export-CsUserData -PoolFqdn $lyncpoolfqdn -FileName $ExportFileNamePath
    }

#Extract the Exported Zip file. Requires 7-Zip
Write-Host " "
&$7ZipPath e $ExportFileNamePath $7ZipOutputParam
Write-Host " "
Write-Host " "
$original = [xml] (Get-Content $LyncXMLFile)

#Set our loop counter to 0
$count = $original.DocItemSet.DocItem.Count + 1

Write-Host "########################################################################"

#Loop through all DocItem Elements and replace any with ContactGroups in them
For ($i=0; $i -lt $count; $i++) {
        If (($original.DocItemSet.DocItem[$i].Data.HomedResource.ContactGroups.ContactGroup.Count -gt 0)) 
            {
                # get the target node
                
                Write-Host " "
                Write-Host "Working on XML Node: " $original.DocItemSet.DocItem[$i].Name
                Write-Host " "
                Write-Host "Contact Groups Before: "$original.DocItemSet.DocItem[$i].Data.HomedResource.ContactGroups.ContactGroup.Count
                Write-Host " "
                $inner = $original.DocItemSet.DocItem[$i].Data.HomedResource.ContactGroups
                
                # import the replacement values
                $new = $original.ImportNode($replacement.ContactGroups, $true)
                
                # replace old node with new one (replacement node)
                $dump = $original.DocItemSet.DocItem[$i].Data.HomedResource.ReplaceChild($new, $inner)
                
                Write-Host "Contact Groups After: "$original.DocItemSet.DocItem[$i].Data.HomedResource.ContactGroups.ContactGroup.Count
        }
    }

Write-Host " "
Write-Host "########################################################################"
Write-Host " "

#Remove blank xmlns tags created by importing the node
$original = [xml] $original.OuterXml.Replace(" xmlns=`"`"", "")

# save changes (full path to file)
$original.Save($LyncXMLFile)

# create updated zip file
& $7ZipPath a $UpdatedFileNamePath $7ZipIncludeFiles

Write-Host " "
Write-Host "########################################################################"
Write-Host " "
Write-Host "The XML file has been updated with the default groups."
Write-Host " "
Write-Host "If you want to take a look at the file its path is"
Write-Host " " 
Write-Host $LyncXMLFile
Write-Host " "
Write-Host "This file will be deleted once this script has finished."
Write-Host " "
Write-Host "########################################################################"
Write-Host " "

$sure = Read-Host "About to upload the changes to the server.  Are you sure? (Y or N)"

If ($sure -eq "Y")
    {
        Write-Host " "
        Write-Host "Updating Server with the new Contact Group Settings."
        Write-Host " "
        Write-Host "The user(s) will have no Contact groups until they" 
        Write-Host "log off and back on to Lync."
        
        If ($sora -eq "S")
            {
                # Update the server with the new User Data
                Update-CsUserData -Filename $UpdatedFileNamePath -UserFilter $user_email
            }
        Else
            {
                # Update the server with the new User Data
                Update-CsUserData -Filename $UpdatedFileNamePath
            }
        Write-Host " "
    }
Else
    {
        Write-Host " "
        Write-Host "Update Aborted!!!!!!"
        Write-Host " "
        Write-Host "Please Come Again!  :) " 
        Write-Host " "
    }

Write-Host "I will clean up the files and close the window after you hit Enter."
Write-Host " "

PAUSE

# Clean Up
Remove-Item ($BaseFilePath + "\*") -recurse

Friday, March 8, 2013

Powershell Multi-Select List Box Function

I use a bit of code for creating a multiple selection list box from an old technet article (http://technet.microsoft.com/en-us/library/ff730950.aspx) in a number of my powershell scripts.

Recently I was putting together a script on my new Windows 8 box and found that the function no longer returned what was selected. I ended up finding the solution to my problem here.

The issue seems to be related to the way Powershell 3.0 handles variable scopes. To make it work I had to add the script scope to the variable in the function.

Originally the variable was $x, now its $Script:x.

In case anyone finds themselves in the same boat i did here is an updated version of the function.

Function MultipleSelectionBox ($inputarray,$prompt,$listboxtype) {

# Taken from Technet - http://technet.microsoft.com/en-us/library/ff730950.aspx
# This version has been updated to work with Powershell v3.0.
# Had to replace $x with $Script:x throughout the function to make it work. 
# This specifies the scope of the X variable.  Not sure why this is needed for v3.
# http://social.technet.microsoft.com/Forums/en-SG/winserverpowershell/thread/bc95fb6c-c583-47c3-94c1-f0d3abe1fafc
#
# Function has 3 inputs:
#     $inputarray = Array of values to be shown in the list box.
#     $prompt = The title of the list box
#     $listboxtype = system.windows.forms.selectionmode (None, One, MutiSimple, or MultiExtended)

$Script:x = @()

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 

$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = $prompt
$objForm.Size = New-Object System.Drawing.Size(300,600) 
$objForm.StartPosition = "CenterScreen"

$objForm.KeyPreview = $True

$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") 
    {
        foreach ($objItem in $objListbox.SelectedItems)
            {$Script:x += $objItem}
        $objForm.Close()
    }
    })

$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") 
    {$objForm.Close()}})

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,520)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"

$OKButton.Add_Click(
   {
        foreach ($objItem in $objListbox.SelectedItems)
            {$Script:x += $objItem}
        $objForm.Close()
   })

$objForm.Controls.Add($OKButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,520)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20) 
$objLabel.Size = New-Object System.Drawing.Size(280,20) 
$objLabel.Text = "Please make a selection from the list below:"
$objForm.Controls.Add($objLabel) 

$objListbox = New-Object System.Windows.Forms.Listbox 
$objListbox.Location = New-Object System.Drawing.Size(10,40) 
$objListbox.Size = New-Object System.Drawing.Size(260,20) 

$objListbox.SelectionMode = $listboxtype

$inputarray | ForEach-Object {[void] $objListbox.Items.Add($_)}

$objListbox.Height = 470
$objForm.Controls.Add($objListbox) 
$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

Return $Script:x
}
After an extremely long hiatus........I am going to try this once again.  I have some good stuff coming. The first post I am working on will be posted in the next few days.  It is a powershell script for updating Lync 2013 Contact Groups from the server side.


Friday, March 13, 2009

VMWare Player Networking on Windows 7 Host

After having trouble getting any VMWare Virtual Machines to have internet access when running in VMWare player 2.51 with Windows 7 Beta as the host system I resolved the get the problem solved today. I ended up finding all of the information I needed spread around on multiple sites throughout the web. So if you are having trouble hopefully this can help.

Here are the issues I had:
  1. After installation a user account _vmware_user is shown on the Windows 7 Welcome Screen

  2. Virtual Machines can ping out to the internet without a problem, but when trying to load webpages they fail to load.

  3. After installation Windows Firewall defaults to Public Network settings.
Here is how I took care of these issues
  1. This solution was found on another blog (http://blog.stealthpuppy.com/windows/hiding-the-vmware_user-account-in-windows-7)

  2. This problem involved a little bit more digging and combing pieces found on multiple sites. It seems that some people do not have this problem but others do. If you are one of these people, and your problem is similar to the issue I described above this worked for me.


    Step 1:

    Follow the procedure below which is outlined here. We are going to setup a virtual network adapter to use Windows 7 ICS to get its internet connection. There is another procedure outlined in the comments on this page which sets up the virtual adapter to get its address from our local network, but this did not work for me and resulted again in being able to ping internet addresses, but not load them. Also I tweaked the procedure a bit to make it a bit easier.

    1. Start vmnetcfg.exe as administrator. This executable is located in \Program Files\VMware\VMware Player.

    2. Goto Host Virtual Adapters and remove all VMNet instances (VMNet1 and VMNet8 typically).

    3. Click Apply.

    4. Add and Assign it the new adapter to VMnet1.

    5. Select the Host Virtual Networking tab.

    6. Click the > next to VMnet1, choose Subnet, and change the IP addresses and Subnet mask to the ICS network (192.168.0.0 / 255.255.255.0). Click OK and then Apply

    7. Click the > next to VMnet1, choose DHCP, and make the Start IP address and End IP Address are in the correct network. Mine defaulted to 192.168.0.128 and 192.168.0.254 respectively. If you had to change them click OK and then Apply. Otherwise go to the next step.

    8. Click the > next to VMnet1, choose NAT, and change the Default Gateway Address to the ICS Network default gateway (192.168.0.1). Click OK and then Apply.

    9. You can now close the Virtual Network Editor program.

    Step 2: Now we need to enable ICS in Windows 7.

    10. Open up the Network and Sharing Center

    11. Left click on the adapter you are using to connect to the web a. If you are using a wired connection this will most likely be Local Area Connection 1 and if you are using Wireless is will be Wireless Network Connection. This will open the Network Connection Status window.

    12. Click Properties

    13. Select the Sharing tab.

    14. Check the box "Allow other network users to connect through this computers internet connection."

    15. In the Home Networking Connection Box choose "VMware Network Adapter VMnet1".

    16. Click OK.

    17. Click Close.

    Now we are almost there, but we still have one configuration problem. Windows Networking thinks that the Virtual Network Adapter is on a Public Network, which causes Windows Firewall to default to Public Network settings, which disables ICS. You have two options now. One will fix the issue till your next reboot, then other is a more permenant solution.

    Option #1: Tell Windows the Virtual Network is Private

    1. You should still be looking at the Network and Sharing Center, if not, open it.

    2. Click the Unidentified Network and change its setting to Home or Work which will make it a Private network.

    3. That's it, open up VMWare Player, Click Devices, choose Network Adapter, and Host-only. Your VM should now be able to connect to the internet.

    4. The only problem with this solution is next time you restart Windows you will need to go back into the Network and Sharing Center and reclassify the Unidentified Network as Private again. Or check out Option #2.

    Option #2: Classify Virtual Network Adapter as an Endpoint Device
    (Also the solution to Issue #3)

    1. Just follow the procedure outlined here. (http://www.nivot.org/2008/09/05/VMWareVMNETAdaptersTriggeringPublicProfileForWindowsFirewall.aspx)

    NOTE: In order to execute a PowerShell script you will need to enable PowerShell scripts. This involves a registry edit as outlined here. (http://computerperformance.co.uk/powershell/powershell_cmdlet.htm)

    2. Thats it, open up VMWare Player, Click Devices, choose Network Adapter, and Host-only. Your VM should now be able to connect to the internet.
Well that is it for now. Hopefully this helps a few of you who are still having trouble. This solution seems to be working great for me.