I've attached a couple custom tasks that allow manipulation of ADSI via NAnt. Some
examples of usage:
<!-- set the IIS root path to allow Anonymous access -->
<adsisetprop path="IIS://localhost/W3SVC/1/Root" propname="AuthAnonymous"
propvalue="true"/>
<!-- retrieve the setting for NTLM and echo it. -->
<adsigetprop path="IIS://localhost/W3SVC/1/Root" propname="AuthAnonymous"
storein="bar"/>
<echo message="${bar}"/>
<!-- multiple sets are possible using this syntax -->
<adsisetprop path="${iis.path}/Root/GWSSample">
<properties>
<option name="AuthBasic" value="true"/>
<option name="AuthNTLM" value="false"/>
</properties>
</adsisetprop>
If people think this is a useful task, I'm happy to have it be a part of NAntContrib.
// NAnt - A .NET build tool
// Copyright (C) 2002 Galileo International
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Gordon Weakliem ([EMAIL PROTECTED])
//
using System;
using System.DirectoryServices;
using SourceForge.NAnt;
using SourceForge.NAnt.Tasks;
using SourceForge.NAnt.Attributes;
namespace Galileo.NAnt.Tasks
{
/// <summary>
/// Base NAnt task for working with ADSI. This task contains only the path of the
ADSI
/// object that you want to work with.
/// </summary>
public abstract class ADSIBaseTask : Task
{
public ADSIBaseTask()
{
_path = null;
}
private string _path;
/// <summary>
/// The ADSI path of the location where we want to work with.
/// </summary>
[TaskAttribute("path", Required=true)]
public String Path
{
get { return _path; }
set { _path = value; }
}
// TODO: not at all tested at this point!
#if false
private string _host = "LocalHost";
/// <summary>
/// The Host where the target object is located, default is LocalHost
/// </summary>
[TaskAttribute("host", Required=false)]
public String Host
{
get { return _host; }
set { _host = value; }
}
private string _username;
/// <summary>
/// The username to authenticate on the host with
/// </summary>
/// <remarks>
/// When you connect using IIS Admin Objects (IIS ADSI provider)from a remote
client, you can't
/// specify explicit credentials, the program must run with the credentials valid
to manipulate
/// the metabase on the remote server. That means that the user running this
program must be
/// an account with "administrative" privileges on the IIS server (unless you
changed the master
/// operators property settings of IIS), with the same password as this account on
the remote
/// server.
/// Say you have Server A running IIS, Server B running this program. Say you
have on A an
/// account named "IISADMIN" with pwd "IISPWD" and this account is member of the
/// administrators alias on A, and on serverB the same account named "IISADMIN"
with pwd "IISPWD"
/// When you run this program, in a logon session as "IISADMIN",
password="IISPWD", the program
/// will succeed. Anything else will fail with "Access Denied".
/// </remarks>
[TaskAttribute("username", Required=false)]
public String Username
{
get { return _username; }
set { _username = value; }
}
private string _password;
/// <summary>
/// The password to authenticate with.
/// </summary>
[TaskAttribute("password", Required=true)]
public String Password
{
get { return _password; }
set { _password = value; }
}
#endif
}
}
// NAnt - A .NET build tool
// Copyright (C) 2002 Galileo International
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Gordon Weakliem ([EMAIL PROTECTED])
//
using System;
using System.DirectoryServices;
using SourceForge.NAnt;
using SourceForge.NAnt.Tasks;
using SourceForge.NAnt.Attributes;
namespace Galileo.NAnt.Tasks
{
/// <summary>
/// Used to get the value of a property from an ADSI object.
/// </summary>
[TaskName("adsigetprop")]
public class ADSIGetPropertyTask : ADSIBaseTask {
public ADSIGetPropertyTask() {
}
private string _propName;
private string _storeIn;
/// <summary>
/// The name of the property to get
/// </summary>
[TaskAttribute("propname",Required=true)]
public String PropName
{
get { return _propName; }
set { _propName = value; }
}
/// <summary>
/// The system property to store the value in.
/// </summary>
[TaskAttribute("storein",Required=true)]
public String StoreIn
{
get { return _storeIn; }
set { _storeIn = value; }
}
/// <summary>
/// Sets the specified property
/// </summary>
protected override void ExecuteTask()
{
try
{
// Get the directory entry for the specified path and
set the
// property.
using (DirectoryEntry pathRoot = new
DirectoryEntry(Path))
{
pathRoot.RefreshCache();
if (Project.Properties[StoreIn] == null)
{
Project.Properties.Add(StoreIn,"");
}
if
(pathRoot.Properties[PropName].Value.GetType().IsArray)
{
System.Text.StringBuilder sb = new
System.Text.StringBuilder();
foreach (object propValue in
(Array)pathRoot.Properties[PropName].Value)
{
sb.AppendFormat("{0}" +
Environment.NewLine,propValue);
}
Project.Properties[StoreIn] =
sb.ToString();
}
else
{
Project.Properties[StoreIn] =
pathRoot.Properties[PropName].Value.ToString();
}
Log.WriteLine("{0}{3}: Property {1} = {2}",
LogPrefix, PropName,
Project.Properties[StoreIn],Path);
}
}
catch (Exception e)
{
Log.WriteLine("{0}Error reading property {1}: {2}",
LogPrefix, PropName,e.Message);
throw new BuildException(String.Format("Error reading
property {0}: {1}",
PropName,e.Message),e);
}
}
}
}
// NAnt - A .NET build tool
// Copyright (C) 2002 Galileo International
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Gordon Weakliem ([EMAIL PROTECTED])
//
using System;
using System.DirectoryServices;
using SourceForge.NAnt;
using SourceForge.NAnt.Tasks;
using SourceForge.NAnt.Attributes;
namespace Galileo.NAnt.Tasks
{
/// <summary>
/// Sets a property on an ADSI object.
/// </summary>
/// <remarks>
/// This task uses a heuristic to determine the type of the property in ADSI. The
following cases are notable:
/// <list type="bulleted">
/// <item>If the property does not exist on the item, it is inserted as a
string.</item>
/// <item>If the property already exists, this method will attempt to preserve
/// the type of the property. The types this method knows about are String,
/// Boolean, and Int32.</item>
/// <item>If the property exists and is an array, the value is added to
/// the array, but only if it is not already present.</item>
/// </list>
/// </remarks>
/// <example>
/// <adsisetprop path='${iis.path}/Root' propname='AuthAnonymous'
propvalue='true'/>
///
/// <adsisetprop path='${iis.path}/Root/GWSSample'>
/// <properties>
/// <option name='AuthBasic' value='false'/>
/// <option name='AuthNTLM' value='true'/>
/// </properties>
/// </adsisetprop>
/// </example>
[TaskName("adsisetprop")]
public class ADSISetPropertyTask : ADSIBaseTask
{
public ADSISetPropertyTask()
{
_propertyList = new OptionSet();
}
private string _propName;
private string _propValue;
/// <summary>
/// The name of the property to set
/// </summary>
[TaskAttribute("propname",Required=false)]
public String PropName
{
get { return _propName; }
set { _propName = value; }
}
/// <summary>
/// The new value of the property
/// </summary>
[TaskAttribute("propvalue",Required=false)]
public String PropValue
{
get { return _propValue; }
set { _propValue = value; }
}
/// <summary>
/// A group of properties to set with this task.
/// </summary>
private OptionSet _propertyList;
[OptionSet("properties",Required=false)]
public OptionSet PropertyList
{
get { return _propertyList; }
}
/// <summary>
/// Sets the specified property
/// </summary>
protected override void ExecuteTask()
{
if ( (this.PropName == null || this.PropValue == null) &&
this.PropertyList.Count == 0 )
{
throw new BuildException("propname and propvalue attributes or <properties>
option set is required");
}
try
{
// Get the directory entry for the specified path and set the
// property.
using (DirectoryEntry pathRoot = new DirectoryEntry(Path))
{
pathRoot.RefreshCache();
// if there was a property named in the attributes, set it.
if (PropName != null)
{
SetProperty(pathRoot, PropName, PropValue);
}
// set the properties named in the child property list.
foreach (OptionValue ov in PropertyList)
{
SetProperty(pathRoot, ov.Name, ov.Value);
}
pathRoot.CommitChanges();
}
}
catch (BuildException)
{
// rethrow any BuildExceptions from farther down.
throw;
}
catch (Exception e)
{
// log any other exception and wrap it in a BuildException.
Log.WriteLine(LogPrefix + "Error setting property {0}: {1}",
PropName,e.Message);
throw new SourceForge.NAnt.BuildException(String.Format("Unable to set
property {0} to value {1}: {2}",PropName,PropValue,e.Message),e);
}
}
/// <summary>
/// Sets the named property on the DirectoryEntry passed to the method to the
given value.
/// </summary>
/// <param name="entry">The DirectoryEntry we're modifying</param>
/// <param name="propName">The name of the property to set</param>
/// <param name="propValue">The value to set the property to.</param>
/// <remarks>
/// The following cases are notable:
/// <list type="bulleted">
/// <item>If the property does not exist on the item, it is inserted as a
string.</item>
/// <item>If the property already exists, this method will attempt to preserve
/// the type of the property. The types this method knows about are String,
/// Boolean, and Int32.</item>
/// <item>If the property exists and is an array, the value is added to
/// the array, but only if it is not already present.</item>
/// </list>
/// </remarks>
private void SetProperty(DirectoryEntry entry, String propName, String propValue)
{
if (!entry.Properties.Contains(propName))
{
entry.Properties[propName].Insert(0,propValue);
}
// TODO: can I find out the property type from the entry's schema?
if (entry.Properties[propName].Value.GetType().IsArray)
{
// with arrays, don't add the value if it's already there.
object objToSet = Convert(entry.Properties[propName][0],propValue);
if (!entry.Properties[propName].Contains(objToSet))
{
entry.Properties[propName].Add(objToSet);
}
}
else
{
//entry.Properties[propName].Clear();
object originalValue = entry.Properties[propName].Value;
object newValue= Convert(originalValue,propValue);
entry.Properties[propName][0] = newValue;
}
Log.WriteLine(LogPrefix + "{2}: Property {0} set to {1}",
propName, propValue, Path);
}
private object Convert(object existingValue, String newValue)
{
if (existingValue is String)
{
return newValue.ToString();
}
else if (existingValue is Boolean)
{
return Boolean.Parse(newValue);
}
else if (existingValue is Int32)
{
return Int32.Parse(newValue);
}
else
{
throw new BuildException(String.Format("Don't know how to set property type
{0}",existingValue.GetType().FullName));
}
}
}
}