The fix for the Alternate Access mapping issue has been published in August 2009 CU : http://support.microsoft.com/kb/973410.
The Kb states the following, amongst the issues fixed :
'You have a Web application that has more than five incoming URLs. You cannot change Alternate Access Mappings (AAM) settings after you install the 2007 Microsoft Office system cumulative update that was released in April 2008.'
<Update >
Please be aware of the new issue found in these updates. Read here.
</Update >
Thursday, August 27, 2009
Saturday, August 8, 2009
SharePoint Alternate Access Mappings explained....my way!
I see many people confused about AAMs, their role and their usage. Therefore, I decided to write a post about them and explain in clear English what are they, when to use them and where to define them.
What AAms are?
AAMs are different urls mapped to the same application in order to give access to the same content using different zones. We cannot talk about AAMs without talking about web application zones. Each SharePoint web application can have five zones : Default, Internet, Intranet, Extranet and Custom. Each zone can have its own authentication provider. For example, Integrated windows authentication for the Default zone, Anonymous access for the Internet zone and Form based authentication for the Extranet zone. All the five zones share the same Database. i.e. the same content, but each one has its proper IIS web Site. Each zone has a public url. When we create a web application, the default zone is created. Let's say "http://default.mycompany:80". Then, we can extend this web application to the four remaining zones if we need to. For our example, we will extend our web application to the Internet zone "http://www.mycompany.com" and the Extranet zone "http://extranet.mycompany.com".
When to use AAMs?
Suppose that for convenience, I want my internal users to access the default zone using a more simplified url. What to do? Create a new AAM (http://mycompany) and map it the default zone. Now, the default zone can be accessed using whether http://mycompany or http://default.mycompany:80.
Suppose again that my company has been sold to a rich man. The urls I created for my web application are no longer valid. What to do to rename my web application urls from mycompany to hiscompany? Backup the content, create a new web application with new urls then restore the content? Yes, it could be. However, there is a better and simpler solution : AAMs. Create new AAMs, i.e. "http://default.hiscompany:80", "http://mycompany", "http://www.hiscompany.com" and "http://extranet.hiscompany.com" and map each url to the appropriate zone.
Where do I define these AAMs?
I'm glad you asked! Go to Central Admin > Operations > Alternate Access Mappings, under the Global Configuration section.
It goes without saying that the urls we are talking about must be first defined in the DNS and IIS.
I hope I have shed more light on AAMs by now. Nevertheless, if you have any question, feel free to ask.
Hope this post is helpful.
What AAms are?
AAMs are different urls mapped to the same application in order to give access to the same content using different zones. We cannot talk about AAMs without talking about web application zones. Each SharePoint web application can have five zones : Default, Internet, Intranet, Extranet and Custom. Each zone can have its own authentication provider. For example, Integrated windows authentication for the Default zone, Anonymous access for the Internet zone and Form based authentication for the Extranet zone. All the five zones share the same Database. i.e. the same content, but each one has its proper IIS web Site. Each zone has a public url. When we create a web application, the default zone is created. Let's say "http://default.mycompany:80". Then, we can extend this web application to the four remaining zones if we need to. For our example, we will extend our web application to the Internet zone "http://www.mycompany.com" and the Extranet zone "http://extranet.mycompany.com".
When to use AAMs?
Suppose that for convenience, I want my internal users to access the default zone using a more simplified url. What to do? Create a new AAM (http://mycompany) and map it the default zone. Now, the default zone can be accessed using whether http://mycompany or http://default.mycompany:80.
Suppose again that my company has been sold to a rich man. The urls I created for my web application are no longer valid. What to do to rename my web application urls from mycompany to hiscompany? Backup the content, create a new web application with new urls then restore the content? Yes, it could be. However, there is a better and simpler solution : AAMs. Create new AAMs, i.e. "http://default.hiscompany:80", "http://mycompany", "http://www.hiscompany.com" and "http://extranet.hiscompany.com" and map each url to the appropriate zone.
Where do I define these AAMs?
I'm glad you asked! Go to Central Admin > Operations > Alternate Access Mappings, under the Global Configuration section.
It goes without saying that the urls we are talking about must be first defined in the DNS and IIS.
I hope I have shed more light on AAMs by now. Nevertheless, if you have any question, feel free to ask.
Hope this post is helpful.
Saturday, July 25, 2009
Batch solution deployment between farms
Last week, we have mounted a new two-servers farm for our development environment. Among the task I had to do was to copy and deploy about 50 solutions (wsps) from the old farm to the new one. To do this, I wrote a console application to help me extract all the solutions installed and create at the same time a command file with the necessary StsAdm commands to install them in the new farm.
Here is what the console applications has to do :
- Create a folder "SolutionsToDeploy" where the WSPs will be extracted and the command file created.
- Iterate through the solutions in the solutions store, extract the wsp file and generate the Stsadm command needed to install it.
Here is the code :
I executed the console application, copied the new folder created (SolutionsToDeploy) to the new farm and ran the command file (DeploySolutions.cmd). With one shot, all my 50 solutions were added and deployed to the new farm.
Hope this helps.
Here is what the console applications has to do :
- Create a folder "SolutionsToDeploy" where the WSPs will be extracted and the command file created.
- Iterate through the solutions in the solutions store, extract the wsp file and generate the Stsadm command needed to install it.
Here is the code :
Imports System.TextImports System.IOImports SystemImports Microsoft.SharePointImports Microsoft.SharePoint.AdministrationImports System.XmlImports System.WebModule ExtractSolutionsSub Main(ByVal args() As String)
TryDim solutions As SPSolutionCollection = SPFarm.Local.Solutions
Dim Path As String = "SolutionsToDeploy/"
Dim AllowGac As Boolean = False
Dim AllowCas As Boolean = False
' Create the folder if it does not existIf Not System.IO.Directory.Exists(Path) Then
System.IO.Directory.CreateDirectory(Path)
End If
'Create a file stream where StsAdm command will be addedDim wrt As System.IO.TextWriter = New StreamWriter(Path + "DeploySolutions.cmd")
For Each sol As SPSolution In solutions
'See if the parameters -AllowGacDeployment and -AllowCasPolicies are neededAllowGac = IIf(sol.ContainsGlobalAssembly, True, False)
AllowCas = IIf(sol.ContainsCasPolicy, True, False)
Dim strline As String = String.Empty
'Extract the solution into a fileDim wsp As SPPersistedFile = sol.SolutionFile
wsp.SaveAs(Path + sol.Name)
'For every solution we need two commands : AddSolution and DeploySolution strline = "Stsadm -o AddSolution -FileName " + sol.Namewrt.WriteLine(strline)
If sol.DeploymentState = SPSolutionDeploymentState.GlobalAndWebApplicationDeployed Or sol.DeploymentState = SPSolutionDeploymentState.WebApplicationDeployed Then
For Each wapp As SPWebApplication In sol.DeployedWebApplications
strline = "Stsadm -o DeploySolution -name " + sol.Name + " -url " + wapp.GetResponseUri(SPUrlZone.Default).ToString() + IIf(AllowGac, " -AllowGacDeployment", "") + IIf(AllowCas, " -AllowCasPolicies", "") + " -Immediate"
Next Elsestrline = "Stsadm -o DeploySolution -name " + sol.Name + IIf(AllowGac, " -AllowGacDeployment", "") + IIf(AllowCas, " -AllowCasPolicies", "") + " -Immediate"
End If
wrt.WriteLine(strline)
' Execute the timer job strline = "Stsadm -o ExecAdmSvcJobs"wrt.WriteLine(strline)
wrt.WriteLine()
Next 'Write the command file to disk and close the streamwrt.Flush()
wrt.Close()
wrt = NothingCatch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
End ModuleI executed the console application, copied the new folder created (SolutionsToDeploy) to the new farm and ran the command file (DeploySolutions.cmd). With one shot, all my 50 solutions were added and deployed to the new farm.
Hope this helps.
Friday, July 3, 2009
April 2009 CU Alternate Access Mapping issue... Again
I noticed in the last two weeks that many people are coming to my blog looking for information about the AAMs issue. Therefore, I want to tell you that Microsoft has been informed of the bug and that they are working on it. Let's just hope that the fix will be published in the August 2009 CU. Until then, if I find any workaround or get any information, I'll keep you informed.
Sunday, June 28, 2009
Extending STSADM...Develop your own commands : Part 2
In part 1, we have discussed how to extend STSADM using Visual Basic. We have developed our FramManifest command which we use to output the SharePoint farm content hierarchy into an XML file.
We have deployed our new command manually, which is not the way we deploy things in SharePoint. The best way is to build a SharePoint solution file (WSP), then deploy it to the farm.
A SharePoint solution is a cab file with a .wsp extension that tells SharePoint how to install this solution and how to use it.
There are two ways to build a SharePoint solution file :
- The automated way, using tools available on the web such as Wspbuilder or STSDEV or VSeWSS.
- The other way is to do the whole thing yourself. This is the best way if you want to understand how solutions files are built, and that's what we are going to do for our Farm Manifest STSADM extension project.
There are four steps to manually create and deploy a SharePoint solution file :
To begin, here is how our project looks in the solution explorer :
The project as you have noticed, contains two folders : "Config" whose content will be copied to the "/Config" folder of the 12 hive, and the folder "Solution" where the generated solution file will go.
Step 1 : Create the manifest.xml file
The manifest.xml contains a unique ID for the solution and the files this solution will deploy :
Step 2 : Create the the DDF (Diamond Directive File)
The .ddf file is used with the makecab.exe tool to build the .wsp file
Step 3 : Build the solution file (wsp)
I usually use a command file "MakeSolution.cmd" to build the solution :
The execution of this command file is done in the post build events of the project :

Now that every thing is ready, right-click on the project name in the solution explorer window then click "Build" or just press CTRL+SHIF+B. the "MyStsadmCommands.wsp" file should be by now created in the "/Solution" folder of the project.
Step 4 : Deploy the solution
To deploy the solution, we have to first add it to the solutions store by using the command :
Stsadm -o AddSolution -filename c:\vsprojects\Mystsadmcommands\solution\MyStsadmCommands.wsp 'Or whatever is the path of your solution file.
Then we have to deploy the solution to the farm :
Stsadm -o DeploySolution -name MyStsadmCommands.wsp -immediate -AllowGacDeployment
To verify that the solution is really deployed and can be used, go to "Central Administration > Operations > Solution Management". You should see it in the solutions list as shown below :

That's it. Hope this helps.
We have deployed our new command manually, which is not the way we deploy things in SharePoint. The best way is to build a SharePoint solution file (WSP), then deploy it to the farm.
A SharePoint solution is a cab file with a .wsp extension that tells SharePoint how to install this solution and how to use it.
There are two ways to build a SharePoint solution file :
- The automated way, using tools available on the web such as Wspbuilder or STSDEV or VSeWSS.
- The other way is to do the whole thing yourself. This is the best way if you want to understand how solutions files are built, and that's what we are going to do for our Farm Manifest STSADM extension project.
There are four steps to manually create and deploy a SharePoint solution file :
To begin, here is how our project looks in the solution explorer :
The project as you have noticed, contains two folders : "Config" whose content will be copied to the "/Config" folder of the 12 hive, and the folder "Solution" where the generated solution file will go.Step 1 : Create the manifest.xml file
The manifest.xml contains a unique ID for the solution and the files this solution will deploy :
<?xml version="1.0" encoding="utf-8" ?>
<Solution
SolutionId="2D7B9843-6285-4e67-B393-CB210278E976"
xmlns="http://schemas.microsoft.com/sharepoint/">
<Assemblies>
<Assembly Location="MyStsadmCommands.dll" DeploymentTarget="GlobalAssemblyCache" />
</Assemblies>
<RootFiles>
<RootFile Location="Config\stsadmcommands.MyStsadmCommands.xml" />
<RootFile Location="MyStsadmCommands.dll" />
</RootFiles>
</Solution>
Step 2 : Create the the DDF (Diamond Directive File)
The .ddf file is used with the makecab.exe tool to build the .wsp file
OPTION EXPLICIT
.Set CabinetNameTemplate=MyStsadmCommands.wsp
.Set DiskDirectory1=Solution
.Set Cabinet=on
.Set MaxDiskSize=0
.Set CompressionType=MSZIP;
.Set DiskDirectoryTemplate=CDROM;
manifest.xml manifest.xml
CONFIG\stsadmcommands.MyStsadmCommands.xml config\stsadmcommands.MyStsadmCommands.xml
bin\Debug\MyStsadmCommands.dll MyStsadmCommands.dll
.Set CabinetNameTemplate=MyStsadmCommands.wsp
.Set DiskDirectory1=Solution
.Set Cabinet=on
.Set MaxDiskSize=0
.Set CompressionType=MSZIP;
.Set DiskDirectoryTemplate=CDROM;
manifest.xml manifest.xml
CONFIG\stsadmcommands.MyStsadmCommands.xml config\stsadmcommands.MyStsadmCommands.xml
bin\Debug\MyStsadmCommands.dll MyStsadmCommands.dll
Step 3 : Build the solution file (wsp)
I usually use a command file "MakeSolution.cmd" to build the solution :
@echo off
makecab.exe /f Cab.ddf
makecab.exe /f Cab.ddf
The execution of this command file is done in the post build events of the project :

Now that every thing is ready, right-click on the project name in the solution explorer window then click "Build" or just press CTRL+SHIF+B. the "MyStsadmCommands.wsp" file should be by now created in the "/Solution" folder of the project.
Step 4 : Deploy the solution
To deploy the solution, we have to first add it to the solutions store by using the command :
Stsadm -o AddSolution -filename c:\vsprojects\Mystsadmcommands\solution\MyStsadmCommands.wsp 'Or whatever is the path of your solution file.
Then we have to deploy the solution to the farm :
Stsadm -o DeploySolution -name MyStsadmCommands.wsp -immediate -AllowGacDeployment
To verify that the solution is really deployed and can be used, go to "Central Administration > Operations > Solution Management". You should see it in the solutions list as shown below :

That's it. Hope this helps.
Labels:
custom command,
ddf,
manifest.xml,
stsadm
Tuesday, June 16, 2009
Extending STSADM...Develop your own commands : Part 1
It's been a long time since I last wrote about STSADM, this amazing and necessary tool for every SharePoint administrator. In this post, I will show you how to extend this tool and develop your own commands using Visual Studio.
You can read the MSDN article first to understand the different steps needed to develop custom STSADM commands : How to: Extend the STSADM Utility
Let's begin by enumerating what it takes to develop our custom command :
What sould our command do?
The STSADM command we will develop will help us have the content of our SharePoint farm in an XML file. We'll call it "FarmManifest". The command will iterate through the farm content service and write to the XML file the content hierarchy as shown below :
Our command should have the following syntaxe :
You may ask what's this command for? Honestly, I think it can be used for many purposes. Let's just say that it can show you all the content of your farm and help you compare the content of two farms, especially when it comes to compare features and solutions. Furthermore, you can enhance this command to include other content such as list items, event handlers, jobs, etc.
The project
Now that we know what should our command do, let's get to work. In Visual Studio we will :
- Create a new Visual Basic Class Library Project. Let's call it "MyStsadmCommands"
- Add a reference to "Windows SharePoint Services"
- Rename the default class "Class1.vb" to "FarmManifest.vb"
- Add the necessary "Imports" to the class :
- Add a "GetHelpMessage" method to our class :
- Add a "Run" method to the class :
- For our particular command the "FarmManifest.vb" class when finished, should look like this :
- Now that our custom command is ready, it's time to tell STSADM about it. To do that, we have to write a specific xml file and store it in "/config" directory of the "12 hive" (C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\CONFIG). The name of the xml file must begin with "stsadmcommands." Let's call ours "stsadmcommands.MyStsadmCommands.xml". In this file is a very simple xml, we put a "Commands" element under which we declare our custom commands. A line for each command. Each command must have a name and a class. In our case, we have just one command "FarmManifest", remember! So our file should look as below :
As you certainly have noticed, the class attribute of the command is a string containing {the class name, the namespace, the version, the culture, the public key token}. To obtain the public key token, we have to sign our project first. To do so, go to Project properties (right click on the projet name), select the "Signing" tab, select the "Sign the assembly" option and choose in the drop down list and give the name "MyStsadmCommands" to the signature key file :

The "MyStsadmCommands.snk" file is now added to the project. Now, build the project. We still do not have our public key token, I know ! Check out this link : Visual Studio tip to get the public key token.
For now, to begin to test our fresh StsAdm command, we only have to copy the "stsadmcommands.MyStsadmCommands.xml" file to "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\CONFIG" and register the "MyStsadmCommand.dll" into the GAC.
Congratulation! Now in a command line window, try this :
stsadm -o FarmManifest -filename c:\FarmManifest.xml -includeall
Warning : If you have a large farm, this command may take long to execute.
In part 2 of this post, I'll explain how to build a SharePoint solution file (wsp) to deploy our custom command. Until then, if you have any question, feel free to ask.
You can read the MSDN article first to understand the different steps needed to develop custom STSADM commands : How to: Extend the STSADM Utility
Let's begin by enumerating what it takes to develop our custom command :
- We need to know what should our command do
- Create a Visual Studio Class Library project and add a references to "Microsoft.SharePoint"
- Create a class that implements the "ISPStsadmCommand" interface
- Write the "GetHelpMessage" method
- Write the "Run" method
- Write the XML file that informs STSADM of our new command
- Sign, build and deploy the DLL of our project to the Global Assembly Cache.
What sould our command do?
The STSADM command we will develop will help us have the content of our SharePoint farm in an XML file. We'll call it "FarmManifest". The command will iterate through the farm content service and write to the XML file the content hierarchy as shown below :
- Web Applications
- Content Databases
- Site Collections
- Webs
- Lists
- Web Features
- Site Features
- Web Application Features
- Solutions
- Farm Features Definition
stsadm -o FarmManifest
-Filename (required)
[-IncludeAll] (optional) : Include all sites, webs and lists
[-IncludeSites] (optional) : Include only sites
[-IncludeWebs] (optional) : Include webs if IncludeSites
[-IncludeLists] (optional) : Include lists if IncludeSites and IncludeWebs
-Filename
[-IncludeAll] (optional) : Include all sites, webs and lists
[-IncludeSites] (optional) : Include only sites
[-IncludeWebs] (optional) : Include webs if IncludeSites
[-IncludeLists] (optional) : Include lists if IncludeSites and IncludeWebs
You may ask what's this command for? Honestly, I think it can be used for many purposes. Let's just say that it can show you all the content of your farm and help you compare the content of two farms, especially when it comes to compare features and solutions. Furthermore, you can enhance this command to include other content such as list items, event handlers, jobs, etc.
The project
Now that we know what should our command do, let's get to work. In Visual Studio we will :
- Create a new Visual Basic Class Library Project. Let's call it "MyStsadmCommands"
- Add a reference to "Windows SharePoint Services"
- Rename the default class "Class1.vb" to "FarmManifest.vb"
- Add the necessary "Imports" to the class :
Imports Microsoft.SharePointImports Microsoft.SharePoint.StsAdminImports Microsoft.SharePoint.AdministrationImports System.TextImports System.XmlImports System.IO- Add a "GetHelpMessage" method to our class :
Public Function GetHelpMessage(ByVal command As String) As String Implements Microsoft.SharePoint.StsAdmin.ISPStsadmCommand.GetHelpMessage
Dim strHelpMsg As String = String.Empty
strHelpMsg = "The help message goes here ..." Return strHelpMsgEnd Function
- Add a "Run" method to the class :
Public Function Run(ByVal command As String, ByVal keyValues As System.Collections.Specialized.StringDictionary, ByRef output As String) As Integer Implements Microsoft.SharePoint.StsAdmin.ISPStsadmCommand.Run
' Here goes the code of what sould the command do....End Function
- For our particular command the "FarmManifest.vb" class when finished, should look like this :
Imports Microsoft.SharePointImports Microsoft.SharePoint.StsAdminImports Microsoft.SharePoint.AdministrationImports System.TextImports System.XmlImports System.IO''' <summary>''' Classe implementing the StsAdm Extension''' </summary> ''' <remarks></remarks>Public Class FarmManifest
Implements ISPStsadmCommand 'Private variablesPrivate ManifestFile As String = String.Empty
Private writer As XmlTextWriter
Private mincludeAll As Boolean = False
Private mincludeSites As Boolean = False
Private mincludeWebs As Boolean = False
Private mincludeLists As Boolean = False
''' <summary> ''' Methode to return the help message ''' </summary> ''' <param name="command"></param> ''' <returns></returns> ''' <remarks></remarks>Public Function GetHelpMessage(ByVal command As String) As String Implements Microsoft.SharePoint.StsAdmin.ISPStsadmCommand.GetHelpMessage
Dim strHelpMsg As String = String.Empty
strHelpMsg = "stsadm -o FarmManifest" + Environment.NewLine strHelpMsg = strHelpMsg + " -Filename <filename> (required)" + Environment.NewLine strHelpMsg = strHelpMsg + " [-IncludeAll] (optional) : Include all sites, webs and lists" + Environment.NewLine strHelpMsg = strHelpMsg + " [-IncludeSites] (optional) : Include only sites" + Environment.NewLine strHelpMsg = strHelpMsg + " [-IncludeWebs] (optional) : Include webs if IncludeSites" + Environment.NewLine strHelpMsg = strHelpMsg + " [-IncludeLists] (optional) : Include lists if IncludeSites and IncludeWebs" + Environment.NewLine Return strHelpMsgEnd Function
''' <summary> ''' Method to execute the STSADM command ''' </summary> ''' <param name="command"></param> ''' <param name="keyValues"></param> ''' <param name="output"></param> ''' <returns></returns> ''' <remarks></remarks>Public Function Run(ByVal command As String, ByVal keyValues As System.Collections.Specialized.StringDictionary, ByRef output As String) As Integer Implements Microsoft.SharePoint.StsAdmin.ISPStsadmCommand.Run
'Read the commad entered parametersIf keyValues.ContainsKey("IncludeAll") Then
mincludeAll = True ElseIf keyValues.ContainsKey("IncludeSites") Then
mincludeSites = TrueIf keyValues.ContainsKey("IncludeWebs") Then
mincludeWebs = TrueIf keyValues.ContainsKey("IncludeLists") Then
mincludeLists = TrueEnd If
End If
End If
End If
'Using the farm content serviceDim contentService As SPWebService = SPWebService.ContentService
If keyValues.ContainsKey("FileName") And keyValues("FileName") IsNot Nothing Then
Try ' Open a new XML file stream for writingDim stream As IO.FileStream
stream = File.OpenWrite(keyValues("FileName")) writer = New XmlTextWriter(stream, Encoding.UTF8) ' Causes child elements to be indentedwriter.Formatting = Formatting.Indented
writer.WriteProcessingInstruction("xml", "version=""1.0"" encoding=""utf-8""")
writer.WriteStartElement("Manifest") ' Web Applications element writer.WriteStartElement("WebApplications") writer.WriteAttributeString("Count", contentService.WebApplications.Count.ToString)Dim webApp As SPWebApplication
For Each webApp In contentService.WebApplications
If Not webApp.IsAdministrationWebApplication Then
writer.WriteStartElement("WebApplication") writer.WriteAttributeString("ID", webApp.Id.ToString) writer.WriteAttributeString("Name", webApp.Name) writer.WriteAttributeString("AppPool", webApp.ApplicationPool.Name) 'Content databases element writer.WriteStartElement("ContentDataBases") writer.WriteAttributeString("Count", webApp.ContentDatabases.Count.ToString)Dim contentDatabases As SPContentDatabaseCollection = webApp.ContentDatabases
Dim database As SPContentDatabase
For Each database In contentDatabases
writer.WriteStartElement("ContentDataBase") writer.WriteAttributeString("Name", database.Name) writer.WriteAttributeString("DataBaseServer", database.Server) writer.WriteAttributeString("SiteCount", database.Sites.Count.ToString) writer.WriteAttributeString("WarningSiteCount", database.WarningSiteCount.ToString) writer.WriteAttributeString("MaximumSiteCount", database.MaximumSiteCount.ToString) writer.WriteEndElement() ' Content DataBase Next database writer.WriteEndElement() ' Content DataBases 'Site CollectionsIf mincludeAll Or mincludeSites Then
writer.WriteStartElement("SiteCollections") writer.WriteAttributeString("Count", webApp.Sites.Count.ToString)For Each site As SPSite In webApp.Sites
writer.WriteStartElement("Site") writer.WriteAttributeString("ID", site.ID.ToString) writer.WriteAttributeString("Url", site.Url) writer.WriteAttributeString("OwnerName", site.Owner.Name) writer.WriteAttributeString("OwnerEmail", site.Owner.Email) ' WebsIf mincludeAll Or mincludeWebs Then
For Each web As SPWeb In site.AllWebs
If web.IsRootWeb Then
'RootWeb writer.WriteStartElement("RootWeb") writer.WriteAttributeString("ID", web.ID.ToString) writer.WriteAttributeString("Url", web.Url) writer.WriteAttributeString("Title", web.Title)writer.WriteAttributeString("Template", web.WebTemplate & "#" & web.WebTemplateId.ToString)
writer.WriteStartElement("SubWebs") writer.WriteAttributeString("Count", web.Webs.Count.ToString) 'WebsProcessWebs(web.Webs)
writer.WriteEndElement() 'SubWebs writer.WriteEndElement() ' RootWebEnd If
web.Dispose()
NextEnd If
'Site features ProcessFeatures(site.Features, "SiteFeatures") writer.WriteEndElement() ' Sitesite.Dispose()
Next site writer.WriteEndElement() 'Site CollectionsEnd If
'Web App features ProcessFeatures(webApp.Features, "WebAppFeatures") writer.WriteEndElement() ' Web ApplicationEnd If
Next webApp writer.WriteEndElement() ' Web Applications 'Solutions writer.WriteStartElement("Solutions") writer.WriteAttributeString("Count", SPFarm.Local.Solutions.Count.ToString)For Each solution As SPSolution In SPFarm.Local.Solutions
writer.WriteStartElement("Solution") writer.WriteAttributeString("ID", solution.Id.ToString) writer.WriteAttributeString("Name", solution.Name) writer.WriteAttributeString("Deployed", solution.Deployed.ToString) writer.WriteAttributeString("ContainsGlobalAssembly", solution.ContainsGlobalAssembly.ToString) writer.WriteAttributeString("ContainsCodeAccessSecurityPolicy", solution.ContainsCasPolicy.ToString) writer.WriteAttributeString("LastOperationResult", solution.LastOperationResult.ToString) writer.WriteAttributeString("LastOperationTime", solution.LastOperationEndTime.ToString) writer.WriteEndElement() ' Solution Next solution writer.WriteEndElement() ' Farm features 'Features writer.WriteStartElement("Features") writer.WriteAttributeString("Count", SPFarm.Local.FeatureDefinitions.Count.ToString)For Each feature As SPFeatureDefinition In SPFarm.Local.FeatureDefinitions
writer.WriteStartElement("Feature") writer.WriteAttributeString("ID", feature.Id.ToString) writer.WriteAttributeString("Name", feature.DisplayName) writer.WriteAttributeString("Scope", feature.Scope.ToString) writer.WriteAttributeString("SolutionID", feature.SolutionId.ToString) writer.WriteEndElement() ' Feature Next feature writer.WriteEndElement() ' Farm features writer.WriteEndElement() ' Manifest ' Flush the writer and close the streamwriter.Flush()
stream.Close()
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
ElseThrow New ArgumentException("FileName parameter is empty")
End If
End Function
''' <summary> ''' 'Recusive method to process webs and sub webs ''' </summary> ''' <param name="webcollection"></param> ''' <remarks></remarks>Private Sub ProcessWebs(ByVal webcollection As SPWebCollection)
For Each web As SPWeb In webcollection
writer.WriteStartElement("Web") writer.WriteAttributeString("ID", web.ID.ToString) writer.WriteAttributeString("Url", web.Url) writer.WriteAttributeString("Title", web.Title)writer.WriteAttributeString("Template", web.WebTemplate & "#" & web.WebTemplateId.ToString)
'Web ListsIf mincludeLists Or mincludeAll Then
writer.WriteStartElement("Lists") writer.WriteAttributeString("Count", web.Lists.Count.ToString)For Each list As SPList In web.Lists
writer.WriteStartElement("List") writer.WriteAttributeString("ID", list.ID.ToString) writer.WriteAttributeString("Title", list.Title) writer.WriteAttributeString("Author", list.Author.Name) writer.WriteAttributeString("Template", list.BaseTemplate.ToString) writer.WriteAttributeString("ItemsCount", list.ItemCount.ToString) writer.WriteEndElement() ' List Next writer.WriteEndElement() 'ListsEnd If
'Web features ProcessFeatures(web.Features, "WebFeatures") 'Sub WebsProcessWebs(web.Webs)
writer.WriteEndElement() ' Webweb.Dispose()
NextEnd Sub
''' <summary> ''' Processing features ''' </summary> ''' <param name="features"></param> ''' <param name="element"></param> ''' <remarks></remarks>Private Sub ProcessFeatures(ByVal features As SPFeatureCollection, ByVal element As String)
writer.WriteStartElement(element)
writer.WriteAttributeString("Count", features.Count.ToString)For Each feature As SPFeature In features
writer.WriteStartElement("Feature") writer.WriteAttributeString("ID", feature.Definition.Id.ToString) writer.WriteAttributeString("Name", feature.Definition.DisplayName) writer.WriteAttributeString("Scope", feature.Definition.Scope.ToString) writer.WriteAttributeString("SolutionID", feature.Definition.SolutionId.ToString) writer.WriteEndElement() ' Feature Next feature writer.WriteEndElement() 'FeaturesEnd Sub
End Class
- Now that our custom command is ready, it's time to tell STSADM about it. To do that, we have to write a specific xml file and store it in "/config" directory of the "12 hive" (C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\CONFIG). The name of the xml file must begin with "stsadmcommands." Let's call ours "stsadmcommands.MyStsadmCommands.xml". In this file is a very simple xml, we put a "
<?xml version="1.0" encoding="utf-8" ?>
<commands>
<command name="FarmManifest"
class="MyStsadmCommands.FarmManifest, MyStsadmCommands, Version=1.0.0.0, Culture=neutral, PublicKeyToken=606cd03fbee78308" />
</commands>
As you certainly have noticed, the class attribute of the command is a string containing {the class name, the namespace, the version, the culture, the public key token}. To obtain the public key token, we have to sign our project first. To do so, go to Project properties (right click on the projet name), select the "Signing" tab, select the "Sign the assembly" option and choose

The "MyStsadmCommands.snk" file is now added to the project. Now, build the project. We still do not have our public key token, I know ! Check out this link : Visual Studio tip to get the public key token.
For now, to begin to test our fresh StsAdm command, we only have to copy the
Congratulation! Now in a command line window, try this :
stsadm -o FarmManifest -filename c:\FarmManifest.xml -includeall
Warning : If you have a large farm, this command may take long to execute.
In part 2 of this post, I'll explain how to build a SharePoint solution file (wsp) to deploy our custom command. Until then, if you have any question, feel free to ask.
Labels:
custom command,
GetHelpMessage,
ISPStsadmCommand,
stsadm,
visual basic
Friday, May 29, 2009
SharePoint SP2 Alternate Access Mapping issue...continued
In this post I want to mention that finally, it seems that the issue with the AAMs appears after installing April Cumulative Updates. The WSS 3.0 SP2 and MOSS SP2 are "innocents". This has been said, and now that you are aware of this problem, it's up to you to decide wether to install April CU or not.
The following links may help make your decision :
The following links may help make your decision :
- FAQ concerning Service Pack 2 and the Cumulative Updates for April 2009 for the 2007 Microsoft® Office System and Microsoft Office servers
- Description of the Windows SharePoint Services 3.0 cumulative update package: April 30, 2009
- Description of the SharePoint Server 2007 cumulative update package (MOSS server package): April 30, 2009
Subscribe to:
Posts (Atom)