![]() |
|
Spaces home Just Powershell itProfileFriendsBlogMore ![]() | ![]() |
|
|
August 16 VMware COM APIIt has been a while since I last posted a new entry, but time has yet again not been my best friend.
The last months I've been busy with virtualizations and VMware VI3 in particulair. I wouldn't be me if I wouldn't try leveraging the power of Powershell with VMware. I'm still working on ways to use the VMware SDK with powershell and that means a whole challenge of really understanding C# and the VMware SDK to write cmdlets. Still nothing to post on this, but there are ways to use powershell to manage VMware infrastructures. This is done throught the VMware COM API.
As shown with earlier postings about Citrix it is easy to use COM objects in Powershell. This is the same for VMware.
I will not post an indepth description of how to do all the possible tasks, but it should be enough to get you going. The VMware scripting API is less powerfull than the SDK. Tasks like for instance migrating a VM or creating a VM are not possible through the SDK, but tasks like power operations, taking snapshot, deleting snapshots and so on are possible and that makes it exciting!
Before starting working with the API, first download and install the API at: http://www.vmware.com/download/sdk/api.html
For detailed information about the API download the manual from VMware at: http://www.vmware.com/pdf/Scripting_API_23.pdf
There are some perl and vbscript samples in the document and this can easily be translated in powershell.
Oke, now let's just connect to a VI3 host. Just type the following in the commandline.
ps/> $cp=new-object -com vmcom.vmconnectparams
ps/> $cp.hostname= "ESXhostname"
ps/> $cp.username= "root" #use a username with enough permissions on the ESX host.
ps/> $cp.password= "password"
ps/> $srv=new-object -com vmcom.vmserverctl
ps/> $srv.connect($cp)
now you are connected to the host. If you type $srv you'll get the vm's registered on the host. A better readable list is created by using:
ps/> $srv.RegisteredVmNames
You'll see that the listing actually shows the .vmx files on the host.
Of cource you can get the members of the object by using get-member
ps/> $srv|gm
To connect to a particulair VM do the following
option 1:
copy the complete name of a vmx file path from the previous listing and put it in a variable, lets say $vmname
Now type the following:
ps/> $vm=new-object -com vmcom.vmctl
ps/> $vm.connect($cp,$vmname)
If we than look at the members by using get-member we see what we can do with it.
Let say we want to see if the virtual machine has snapshots. You do the following:
ps/> $vm.HasSnapshot
If there is a snapshot, 1 is returned. Otherwise 0 is returned.
Option 2:
If you know the servername of the virtual machine (say server15) and you want to query it directly you can do the following:
ps/> $srv.RegisteredVmNames|where {$_ -like "*server15*"}|$vm.connect($cp,$_)
ps/> $vm.HasSnapshot
Of cource you must allready have created the $vm object as shown above. Same is for the example below.
Option 3:
If we want to know which VM's have snapshots we can do the following:
ps/> $srv.RegisteredVmNames| foreach {
>> $vm.connect($cp,$_)
>> if ($vm.HasSnapshot -gt 0)
>> {
>> $_
>> }
>> }
ps/>
As said before, this is not a complete description of the VMware API, but just a brief introduction. This should get you on the go and create some nice scripts to make you're life at least a little easier.
Oke, this is it for now, but I promise updates will follow soon.
Greetings
Dennis Verweij
January 17 searching msdn within powershellWhat if you're in Powershell and need to know how to use a specific namespace or class? Of course you can start internet explorer, go to msdn and do a search.
But why not do this from within powershell?
Function msdn {
param([string]$searchstring="") $inet = New-Object -Com Internetexplorer.Application $netsearch = "http://msdn2.microsoft.com/en-us/forms?queryTerm=" $inet.Navigate($netsearch+$searchstring) $inet.Visible=$TRUE } And now, we can just type:
PS:> msdn forms
The result will be a new internet explorer window with you're search results.
You can put this function in you're profile so you can use it all the time.
have fun
Grt
Dennis January 09 A complete directory listingSometimes there are things I just want to try the hard way. Of course you can get a complete directory listing by using
get-childitem -recurse, but you can also do it differently. Same output, but much more fun to try and héy, you want to learn powershell or you don't. I do.
So the stuff below might seem useless effort, but you can use it for much more than just a directorylisting.
It's easy to get a directory listing one level down, or two, or three, or whatever. Just as long as you know how many subs there are. So what if you don't know how many levels down a directorystructure goes or if you just don't care. Then you can do the following:
PS>>Function dirlist {ls $_.FullName|Foreach {$_;if ($_.Mode -like "d*"){dirlist}}};Get-Item * |Foreach {$_;if
($_.Mode -like "d*"){dirlist}} If you do the above you'll get the info, but in an pretty unreadable format, so just format the output:
PS>>Function dirlist {ls $_.FullName|Foreach {$_;if ($_.Mode -like "d*"){dirlist}}};Get-Item * |Foreach {$_;if ($_.Mode -like "d*"){dirlist}} |ft name,fullname And you can sort on what and how you want
PS>>Function dirlist {ls $_.FullName|Foreach {$_;if ($_.Mode -like "d*"){dirlist}}};Get-Item * |Foreach {$_;if ($_.Mode -like "d*"){dirlist}} |Sort-Object -descending creationtime|ft name,creationtime Or just export to a csv so that later, you can do whatever you want. Sort it, filter, choose output format, etc, etc.
PS>>Function dirlist {ls $_.FullName|Foreach {$_;if ($_.Mode -like "d*"){dirlist}}};Get-Item * |Foreach {$_;if ($_.Mode -like "d*"){dirlist} | Export-csv dirlist.csv **update**
As Vinicius Canto mentioned in the reactions, there is a better way to determine if a childitem is a container (directory) or not. This is done by checking the NoteProperty "$_.PSIsContainer", instead of what I used: "if ($_.Mode -like "d*")" or using the -band operator as Vinicius used on his blog at http://viniciuscanto.blogspot.com/2007/01/dir-ad-no-powershell-onde-ele-foi-parar.html"
The NoteProperty "PSIsContainer" works faster than doing a compaire. It simply results in a $True of $False
This results in:
PS>>Function dirlist {ls $_.FullName|Foreach {$_;if ($_.PSIsContainer){dirlist}}};Get-Item * |Foreach {$_;if
($_.PSIsContainer){dirlist} | Export-csv dirlist.csv Thanks Vinicius. And Jeffrey Snover who gave the initial hint.
Grt
Dennis
December 29 Changing the incorrect homedir path of all usersThis powershell script is to correct the users homedrive path in Active Directory.
The following steps are done by the script:
- First it looks up the users with an incorrect homedrive annotation
- For those users it changes the, in this case, servername to an alias
- save the info an continue with the next one.
# Powershell script
# Citrix Export published applications
# D. Verweij
#
###
# Connect to the AD
$root = [adsi]'LDAP://OU=users,OU=application,OU=services,DC=face,DC=foo,DC=nl'
# Configure the searcher
$searcher = new-object directoryservices.directorysearcher
$searcher.searchroot = $root $searcher.filter = "(objectclass=user)" # Loop up accounts with an incorrect homedrive
$searcher.findall()|where {$_.properties.item("homedirectory") -like "*servername*"}|foreach { $userrt="LDAP://"+$_.properties.item("distinguishedName") $userads = [adsi]$userrt $userhome=$userads.homedirectory $userads.put("homedirectory",$userhome.psbase.value.replace("servername","serveralias")) $userads.setinfo() } Grt
Dennis Import Citrix published applicationsAfter exporting the published application setting in an earlier post, here is the script I used to import the applications in the new farm.
# Powershell script
# Citrix Export published applications
# D. Verweij
#
###
# First the known steps to connect to the mfcom and initialize the object $c_farm = new-object -com metaframecom.metaframefarm
$c_farm.initialize(1) # Then we import the csv file and for each we do..
import-csv c:\citrix\Citrix_apps.csv | foreach{
$c_app=$c_farm.addapplication($_.apptype)
$w_app = $c_app.winappobject $c_app.Appname = $_.appname $c_app.description = $_.description $w_app.defaultencryption = $_.defaultencryption $w_app.defaultinitprog = $_.defaultinitprog $w_app.defaultworkdir = $_.defaultworkdir $w_app.browsername = $_.browsername $w_app.enableapp = $_.enableapp $w_app.hidefrompnenum = $_.hidefrompnenum $w_app.hidefrombrowserenum = $_.hidefrombrowserenum $w_app.defaultwindowcolor = $_.defaultwindowcolor $w_app.defaultwindowtype = $_.defaultwindowtype $w_app.defaultsoundtype = $_.defaultsoundtype $w_app.desktopintegrate = $_.desktopintegrate $w_app.mfattributes = $_.mfattributes $w_app.publishingflags = $_.publishingflags $w_app.PNAttributes = $_.PNAttributes $w_app.AllowMultiInstancePerUser = $_.AllowMultiInstancePerUser $w_app.StartMenuFolder = $_.StartMenuFolder $w_app.PlaceUnderProgramsFolder = $_.PlaceUnderProgramsFolder $w_app.WaitOnPrinterCreation = $_.WaitOnPrinterCreation $w_app.defaultwindowwidth = $_.defaultwindowwidth $w_app.defaultwindowheight = $_.defaultwindowheight $w_app.defaultwindowscale = $_.defaultwindowscale $w_app.parentfolderDN = $_.parentfolderDN $w_app.pnfolder = $_.pnfolder $c_app.savedata() } Of course there are much more settings to specify and the above are just the one I needed. To see the complete list, just use the "get-member" :
$farm = new-object -com metaframecom.metaframefarm
$farm.initialize(1)
$farm.applications|get-member
note! for some additional settings you need the winappobject properties (some properties in the script above are winappobject properties). The members of winappobject are shown the same way, but you will first have to choose an application:
For instance:
$sampleapp = $farm.applications|where {$_.appname -like "sampleapp"} # appname is the displayname of the published app
$sampleapp.winappobject|get-member
**update**
If you haven't already, you should take a look at the post on http://blogs.msdn.co/powershell where Jeffrey Snover did some tuning on my script. I haven't had the time to test it yet, but I will and you should too. It's great.
The post can be found here:
Have fun!
Grt
Dennis December 28 Export Citrix published applicationsDuring a migration of our Citrix farm to Presentation Server 4.0, I needed to export the published applications from the old Metaframe farm.
I wanted the export to be a csv file with all the properties to recreate the applications in the new farm.
So, for this I made the following script.
# Powershell script
# Citrix Export published applications
# D. Verweij
#
###
# First the known steps to connect to the mfcom and initialize the object
$c_farm = new-object –com metaframecom.metaframefarm
$c_farm.initialize(1)
# Now we read the properties of the published applications
$c_farm.applications|select-object
@{e={$_.appname};n='appname'},
@{e={$_.groups|foreach {$_.groupname}};n='groupname'},
@{e={$_.users|foreach{$_.username}};n='username'},
@{e={$_.distinguishedname};n='distinguishedname'},
@{e={$_.description};n='description'},
@{e={$_.apptype};n='apptype'},
@{e={$_.browsername};n='browsername'},
@{e={$_.winappobject.enableapp};n='enableapp'},
@{e={$_.winappobject.hidefrompnenum};n='hidefrompnenum'},
@{e={$_.winappobject.hidefrombrowserenum};n='hidefrombrowserenum'},
@{e={$_.winappobject.defaultencryption};n='defaultencryption'},
@{e={$_.winappobject.defaultinitprog};n='defaultinitprog'},
@{e={$_.winappobject.defaultworkdir};n='defaultworkdir'},
@{e={$_.winappobject.defaultwindowwidth};n='defaultwindowwidth'},
@{e={$_.winappobject.defaultwindowheight};n='defaultwindowheight'},
@{e={$_.winappobject.defaultwindowcolor};n='defaultwindowcolor'},
@{e={$_.winappobject.defaultwindowtype};n='defaultwindowtype'},
@{e={$_.winappobject.defaultsoundtype};n='defaultsoundtype'},
@{e={$_.winappobject.defaultwindowscale};n='defaultwindowscale'},
@{e={$_.winappobject.desktopintegrate};n='desktopintegrate'},
@{e={$_.winappobject.mfattributes};n='mfattributes'},
@{e={$_.winappobject.publishingflags};n='publishingflags'},
@{e={$_.winappobject.PNAttributes};n='PNAttributes'},
@{e={$_.winappobject.PNFolder};n='PNFolder'},
@{e={$_.winappobject.parentfolderDN};n='parentfolderDN'},
@{e={$_.winappobject.AllowMultiInstancePerUser};n='AllowMultiInstancePerUser'},
@{e={$_.winappobject.StartMenuFolder};n='StartMenuFolder'},
@{e={$_.winappobject.PlaceUnderProgramsFolder};n='PlaceUnderProgramsFolder'},
@{e={$_.winappobject.WaitOnPrinterCreation};n='WaitOnPrinterCreation'}|
# The last step is to pipe this to the export-csv cmdlet
export-csv c:\citrix\Citrix_apps.csv
Later on I'll go on with importing these apps.
Grt
Dennis Move computer objects in Active DirectoryFor our test enviroment we use a seperate OU in Active Directory to test WSUS updates and patches. Servers are put in this OU when patches have to be tested on curtain servers. To automate this, I of course, created a powershell script to do just this.
The following had to be realized:
- Create a script that moves the server to the test OU
- The script had to be launched as an Altiris job with the abbility to run it on several server at the same time
- For every server the original OU must be remembered to, with the next job, move it back
- Another job (script) has to put the computer in the original OU after the updates are finished
- One problem..the Altiris server runs in a different domain and there's no trust between the two domains
So I had to move the server to the test OU and had to remember the original OU to move it back and this for an unlimited number of servers at the same time.
Since the job must run from Altiris, dragged to a server, servergroup or scheduled on several servers, the server name must be given as a parameter.
The Altiris job does the following things:
- copy the script to the specified server in the %systemroot%\Temp directory
- start the script as in a scheduled task :
%systemroot%\system32\WindowsPowershell\v1.0\powershell.exe %systemroot%\Temp\scriptname %computername%
The following script is to move the server.
# Powershell script
# WSUS Moving computer object to the Test OU
# D. Verweij
#
###
# First read the given parameter in the variable $servnm
param([string]$servnm="none")
# Specify the name of the logfile to save the original OU in. This file is saved on the machine you move and run the script
$ServFL = "C:\Altiris\logfiles\$servnm" # Specify the target OU
$patchOU="LDAP://OU=WSUSTest,OU=Services,DC=Dennis,DC=Verweij,DC=nl" # Connect to the root of the domain
$ad1=[adsi]'' # Setup the search criteria
$ad1search = new-object directoryservices.directorysearcher $ad1search.searchroot = $ad1 $ad1search.filter="(objectclass=computer)" $serv2=$ad1search.findall()|where {$_.properties.item("cn") -like $servnm} # Save the distinguishedname of the original OU in the logfile. You need the distinguished name to move it back $serv2.properties.item("distinguishedname") > $ServFL
$servDN=$serv2.properties.item("distinguishedname") # Connect to the computerobject $serv=[adsi]"LDAP://$servDN"
# And move it
$serv.psbase.MoveTo($patchOU) To move it back the Altiris job is pretty much the same except for the script name
The script to put it back:
# Powershell script
# WSUS Moving computer object to the original OU
# D. Verweij
#
###
# First read the given parameter in the variable $servnm
param([string]$servnm="none")
# Specify the Test OU
$patchOU="LDAP://OU=WSUSTest,OU=Services,DC=Dennis,DC=Verweij,DC=nl" # Specify the name of the logfile the original OU is saved in
$ServFL = "C:\Altiris\logfiles\$servnm" # Check if the logfile exists
if(test-path $ServFL)
{ # Read the logfile and get rid of the computername gc $ServFL|%{$servDN = $_.ToUpper().replace("CN=$servnm,","")}
# Connect to the computerobject
$serv = [adsi]"LDAP://CN=$servnm,$patchOU" # And move it back were it came from
$serv.psbase.MoveTo("LDAP://$servDN") } # Finally delete the logfile
del $ServFL Hope you can do something with it!
Grt
Dennis deleting files created 48 hours agoIn adition to an earlier post, there was another thing I had to do with created reports by users.
Namely: Deleting reports created more than 48 hours ago.
In a curtain application users were able to create reports and save them temporaraly in there homedir for further use. To avoid there report directories from growing to big I made a powershell script wich we scheduled to run every 24 hours that removes reports with a creationtime of more than 48 hours ago. This would result in reports beeing available for at least 48 hours with a maximum age of 71 hours and 59 seconds.
# Powershell script
# Deleting reports with creationtime more than 48 hours ago
# D. Verweij
#
ls \\fileserver\homedir$\app | foreach{
if (test-path \\fileserver\homedir$\app\$_\reports ){ $user = $_ $RepDir = $_.getdirectories()|where-object {$_ -like "*reports*"} $RepDir.getfiles()|foreach{ $contrtime = get-date $timediff = $contrtime - $_.lastwritetime if ($timediff.totalhours -gt 48){ $name = \\fileserver\homedir$\app\$user\reports\ + $_.name del $name} }}} Grt
Dennis Windows PowerShell: TFM™ ... Yes! it's out.I've been eagerly waiting for this book from sapien and can't wait to get my hands on it.
some samples of the book can be found here:
happy reading and learning
Grt
Dennis December 27 the beauty of simple things: listing directory contentSo, why not posting the simple things in this blog.
Once I had to list the content of a directory in the homedir of all 2500+ users. Not all users had this directory and I didn't know who did or didn't. Of course I could have done a search with explorer, but why not do it with powershell. It would only take one simple line and then I would have all the information I needed and in the end I could do with it whatever I wanted. You can format the outcome, export it to a txt, csv of whatever you want. Not that I wanted to, if just needed the info, but......I could have....
By using "test-path" I would't get any errors if directories don't exist or if there files in de search root.
#--
ls|% {if (test-path ./$_/reports){ls ./$_/reports}}
#--
#--
#--
I want a directorylisting = ls
With the output I want to .. = |
For every childitem it want.. = % {
If the directory exists If want to do the following = if (test-path ......){
remember the directory you search = $dirname=$_.name
and list the content of the report directory in this directory = ls ./$dirname/reports
End the "if" = }
End the "foreach"= }
With the output I want to .. = | and from this output only select the homedirectory and the name of the report in the report directory and name these "homedir" and "report" = select @{e={$dirname};n="homedir"},@{e={$_.name};n="report"}
With the output I want to .. = | export this data to a csv file in systemroot\temp and name it reports.csv = export-csv $env:systemroot\temp\reports.csv
So it's really simple. Just play arround and have fun.
grt
Dennis Citrix connectionFor the one that doesn't know: Yes, you can do all the crazy stuff in citrix from the wonderfull world of the commandline as you can in the cmc....and more.
A lot of stuff about the com, named MFCOM is written in the pdf "scripting metaframe". (http://support.citrix.com/article/CTX106885) This document is namely for doing stuff with vbscript, but with al little trail and error and a bit of smart thinking you can use this document to begin powershelling the citrix world.
I'll just post some simple scripts to begin with and I hope this wil trigger you to try things yourself and ofcourse.....share it !
Let's do an automated enable and disable of a number of published applications.
First we close them
#--
$citrixfarm = new-object -com metaframecom.metaframefarm
#Then, the farm has to be initialized.
$citrixfarm.initialize(1) #To close multiple published apps, we need al list of applications. I've put those in a text file. Note that you have to specify the distinguishedname of the application. So, for instance: Applications/Windows/Notepad
gc "c:\scripts\citrixapps\citrixapps.txt" |foreach {
$disting=$_ #To see the progress in the commandline I've put in the echo below
echo "Busy closing --> "+$disting $citrixapps = $citrixfarm.applications|where {$_.DistinguishedName -like $disting} $citrixapps.enableapp = 0 $citrixapps.savedata() } #--
#--
$citrixfarm.initialize(1) gc c:\scripts\citrixapps\citrixapps.txt |foreach {
$disting=$_ "Busy opening --> "+$disting $citrixapps = $citrixfarm.applications|where {$_.DistinguishedName -like $disting} $citrixapps.enableapp = 1 $citrixapps.savedata() } #--
Dennis |
|
|