# # ---------------------------- # XSD SCHEMA DEFINATION # ---------------------------- # # # ----------------------------- # SCHEMATRON RULES DEFINITION # ----------------------------- # # # ----------------------------- # TRANSLATIONS DEFINITION # ----------------------------- # data _system_translations { ConvertFrom-StringData @' # check DFS Namespace service status DFSNServiceStatusCheck_Title=The DFS Namespace service should be started DFSNServiceStatusCheck_Problem=The DFS Namespace service is not started. DFSNServiceStatusCheck_Impact=The namespace server is unavailable to clients and no further diagnostic information can be gathered. If the server is a domain controller, domain referral and domain controller referral requests to the domain controller will fail. DFSNServiceStatusCheck_Resolution=Start the DFS Namespace service. DFSNServiceStatusCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check DFS Namespace service start type DFSNServiceStartupTypeCheck_Title=The DFS Namespace service startup type should be set to Automatic DFSNServiceStartupTypeCheck_Problem=The DFS Namespace service startup type is not set to Automatic. DFSNServiceStartupTypeCheck_Impact=After restarting the server, the namespace server will be offline until you manually start the DFS Namespace service. If the server is a domain controller, domain referral and domain controller referral requests to the domain controller will fail. DFSNServiceStartupTypeCheck_Resolution=Set the DFS Namespace service to start automatically. DFSNServiceStartupTypeCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check IP address to site mapping IPAddressToSiteMappingCheck_Title=All IP addresses for the server should map to the same AD DS site IPAddressToSiteMappingCheck_Problem=Not all network adapters are configured with the same Active Directory Domain Services (AD DS) site. IPAddressToSiteMappingCheck_Impact=DFS Namespaces site awareness will behave incorrectly, with the namespace server potentially providing referrals to client computers at a site that is not local to the server. IPAddressToSiteMappingCheck_Resolution=Configure all network adapters to use IP addresses from subnets that belong to the same AD DS site. IPAddressToSiteMappingCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check LdapTimeoutValue has been set to 30 seconds LdapTimeoutValueCheck_Title=The LDAP timeout value should be set to the default value of 30 seconds LdapTimeoutValueCheck_Problem=The Lightweight Directory Access Protocol (LDAP) timeout value is different from the default, which is 30 seconds. LdapTimeoutValueCheck_Impact=The DFS Namespace service may not be able to query or recover from a temporary failure of Active Directory Domain Services (AD DS). LdapTimeoutValueCheck_Resolution=Use the Set-DfsnServerConfiguration Windows PowerShell cmdlet or the Dfsutil command-line tool to set the LDAPTimeoutValueInSeconds registry value on this namespace server to 30. LdapTimeoutValueCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check SyncIntervalInSeconds has been set to 3600 seconds SyncIntervalInSecondsCheck_Title=The DFS Namespaces sync interval should be set to the default value of 1 hour SyncIntervalInSecondsCheck_Problem=The sync interval value is different from the default, which is 1 hour (3600 seconds). SyncIntervalInSecondsCheck_Impact=The DFS Namespace service may put more load than necessary on Active Directory Domain Services (AD DS), or be unable to recover from temporary failures to update AD DS. SyncIntervalInSecondsCheck_Resolution=Use the Set-DfsnServerConfiguration Windows PowerShell cmdlet or the Dfsutil command-line tool to set the sync interval on this namespace server to the default value of 1 hour. SyncIntervalInSecondsCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check SiteCostedReferral has been enabled SiteCostedReferralCheck_Title=Site-costed referrals should be enabled on domain controllers SiteCostedReferralCheck_Problem=Site-costed referrals are disabled on a domain controller. SiteCostedReferralCheck_Impact=If a domain controller becomes unavailable, client computers that are using the SYSVOL folder of the domain controller might fail over to a distant domain controller instead of a closer domain controller. SiteCostedReferralCheck_Resolution=Use the Set-DfsnServerConfiguration Windows PowerShell cmdlet or the Dfsutil command-line tool to enable site-costed referrals on this domain controller. SiteCostedReferralCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check SysvolNetlogonTargetFailback has been enabled SysvolNetlogonTargetFailbackCheck_Title=Client failback should be enabled for the Netlogon and SYSVOL folders on domain controllers SysvolNetlogonTargetFailbackCheck_Problem=Client failback is disabled for the Netlogon and SYSVOL folders on a domain controller. SysvolNetlogonTargetFailbackCheck_Impact=Client computers could experience slower response times if they fail over to a remote domain controller and do not fail back to the local domain controller when it comes back online. SysvolNetlogonTargetFailbackCheck_Resolution=Use Registry Editor to enable client failback for the Netlogon and SYSVOL folders on each domain controller in this domain. SysvolNetlogonTargetFailbackCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check if standalone root is clustered IsStandaloneClusterCheck_Title=Failover Clustering should be enabled on the server hosting the following standalone namespace: {0} IsStandaloneClusterCheck_Problem=Failover Clustering is not enabled on the server hosting the following standalone namespace: {0} IsStandaloneClusterCheck_Impact=If the namespace server fails, client computers will not be able to receive new referrals to folder targets, which limits their ability to use the following namespace: {0} IsStandaloneClusterCheck_Resolution=Enable Failover Clustering on the server that is hosting the following standalone namespace: {0} IsStandaloneClusterCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # Domain namespace should have at least two root targets RootTargetsCountCheck_Title=Additional namespace servers should be added to the following domain-based namespace: {0} RootTargetsCountCheck_Problem=The following domain-based namespace has only a single namespace server (root target): {0} RootTargetsCountCheck_Impact=If the namespace server fails, client computers will not be able to receive new referrals to folder targets, which limits their ability to use the following namespace: {0} RootTargetsCountCheck_Resolution=Add one or more namespace servers to the following domain-based namespace: {0} RootTargetsCountCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check SiteCosting has been enabled SiteCostingCheck_Title=Namespace root referrals should use the Lowest Cost ordering method on the following DFS namespace: {0} SiteCostingCheck_Problem=Site-costed referrals are disabled on the following DFS namespace: {0} SiteCostingCheck_Impact=If a namespace server or folder target becomes unavailable, client computers might fail over to a distant namespace server or folder target instead of a closer server. SiteCostingCheck_Resolution=Use DFS Management to set the ordering method for namespace root referrals to Lowest Cost on the following namespace: {0} SiteCostingCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check ABDE settings ABDECheck_Title=The access-based enumeration setting should be identical in the DFS namespace database and on the shared folder hosting the namespace root for the following namespace: {0} ABDECheck_Problem=Access-based enumeration is enabled on a DFS namespace, but not on the shared folder hosting the namespace root. Namespace: {0} ABDECheck_Impact=Access-based enumeration will not work, resulting in DFS folders being visible to users who do not have permission to view the folders. ABDECheck_Resolution=Use DFS Management, the Set-DfsnRoot Windows PowerShell cmdlet, or the Dfsutil command-line tool to disable and then enable access-based enumeration on the namespace, and then do not use other tools to modify this setting. ABDECheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check FFL and DFL settings FFLDFLCheck_Title=Windows Server 2008 mode can be used on the following domain-based namespace: {0} FFLDFLCheck_Problem=The following domain-based namespace meets the prerequisites for using the Windows Server 2008 mode, but is instead using the Windows 2000 Server mode. Namespace: {0} FFLDFLCheck_Impact=The namespace administrator cannot enable access-based enumeration of DFS folders or expand the namespace to more than 5,000 folders with targets (links). Additionally, the DFS Namespace service takes longer to start when hosting namespaces with thousands of folders with targets. FFLDFLCheck_Resolution=Migrate the following domain-based namespace from Windows 2000 Server mode to Windows Server 2008 mode: {0} FFLDFLCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check TargetFailback has been enabled TargetFailbackCheck_Title=Client failback should be enabled on the following namespace: {0} TargetFailbackCheck_Problem=Client failback is disabled on the following namespace: {0} TargetFailbackCheck_Impact=Client computers could experience slower response times if they fail over to a remote folder target and do not fail back to the local folder target when it comes back online. TargetFailbackCheck_Resolution=Use DFS Management to enable client failback on the following namespace: {0} TargetFailbackCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. # check RootTargetState has been enabled RootTargetStateCheck_Title=The namespace server should be enabled on the following namespace: {0} RootTargetStateCheck_Problem=The server is a namespace server for the following namespace, but is not enabled. Namespace: {0} RootTargetStateCheck_Impact=If no other namespace servers are available, client computers will not be able to access the following namespace: {0} RootTargetStateCheck_Resolution=Remove all namespace servers that have been permanently disabled and then enable all remaining namespace servers. To do so, use DFS Management, the Remove-DfsnRootTarget and Set-DfsnRootTarget Windows PowerShell cmdlets, or the Dfsutil command-line tool. RootTargetStateCheck_Compliant=The File and Storage Services Best Practices Analyzer scan has determined that you are in compliance with this best practice. '@ } Import-LocalizedData -BindingVariable _system_translations -filename DFSN.psd1 # # ------------------ # FUNCTIONS - START # ------------------ # # # Function Description: # # This function will add the Server Manager module so that Roles # can be queried # # Arguments: # # None # # Return Value: # # None # function Setup { Import-Module ServerManager import-module failoverclusters } # # Function Description: # # This function will remove the Server Manager module after the Roles # have been queried # # Arguments: # # None # # Return Value: # # None # function TearDown { Remove-Module ServerManager Remove-Module failoverclusters } # # Function Description: # # check the status of specifed service # # Arguments: # # $computer - computer name # $serviceName - service name # # Return Value: # # $true - if the service is running # $false - otherwise # function Check-ServiceStatus($computerName, $serviceName) { trap { write-debug $error[0] continue } $service = get-service -computername $computerName -name $serviceName if($service -ne $null -and ($service.Status -eq [System.ServiceProcess.ServiceControllerStatus]"Running")) { $true } else { $false } } # # Function Description: # Creates the Document element for the Xml Document # # Arguments: # $ns - Namespace Name # $name - Name of the document element # # Return Value: # returns the created document element # function Create-DocumentElement( $ns, $name ) { [xml] "<$name xmlns='$ns'>" } # # Function Description: # # This function does a check to see if the specified service is installed # # Arguments: # # $serviceName - Name of the service # # Return Value: # # $true - If service is Installed # $false - If service is not Installed # function Check-ServiceInstallStatus ( $serviceName ) { trap { write-debug $error[0] continue } $serviceInstalled = $false $service = get-service -name $serviceName if ( $service -ne $null ) { $serviceInstalled = $true } $serviceInstalled } # # Function Description: # Check whether the current node is in cluster # # Arguments: # none # # Return Value: # $true - if it is in cluster # $false - if it is not in cluster # function Get-ServerInClusterStatus() { trap { write-debug $error[0] continue } $result = $false $clusvc = get-service -name clussvc if($clusvc -and $clusvc.Status -eq [System.ServiceProcess.ServiceControllerStatus]"Running") { $nodes = get-clusternode $computerName = [System.Net.Dns]::GetHostName() foreach($node in $nodes) { if($node.Name -eq $computername) { $result = $true break } } } $result } # # Function Description: # Create XmlElement and append it into the specified parent # # Arguments: # $doc - XmlDocument manipulated # $parent - parent node # $ns - namespace used for the element # $elementName - element name # $elementValue - element value # # Return Value: # none # function Append-XmlElement($doc, $parent, $ns, $elementName, $elementValue) { $element = $doc.CreateElement($elementName, $ns) $element.set_InnerText($elementValue) [void]$parent.AppendChild($element) } # # Function Description: # formalize the text of the boolean value # # Arguments: # $value - boolean value # # Return Value: # "true" - if $value -eq $true # "false" - if $value -eq $false # function Formalize-BoolValue($value) { if($value) { "true" } else { "false" } } # # Function Description: # Compiles the C# code # # Arguments: # $code - C# code # $Reference - reference assembley # # Return Value: # none # function Compile-Csharp ([string] $code, [Array]$References) { # Get an instance of the CSharp code provider $cp = New-Object Microsoft.CSharp.CSharpCodeProvider $refs = New-Object Collections.ArrayList $refs.AddRange( @("System.dll", "System.Windows.Forms.dll", "System.Data.dll", "System.XML.dll")) if ($References -and ($References.Count -ge 1)) { $refs.AddRange($References) } # Build up a compiler params object... $cpar = New-Object System.CodeDom.Compiler.CompilerParameters $cpar.GenerateInMemory = $true $cpar.GenerateExecutable = $false $cpar.IncludeDebugInformation = $false $cpar.CompilerOptions = "/target:library" $cpar.ReferencedAssemblies.AddRange($refs) $cr = $cp.CompileAssemblyFromSource($cpar, $code) if ( $cr.Errors.Count) { $codeLines = $code.Split("`n"); foreach ($ce in $cr.Errors) { write-host "Error: $($codeLines[$($ce.Line - 1)])" $ce | out-default } Throw "INVALID DATA: Errors encountered while compiling code" } } #CSharp code goes here function Call-NetAPIs() { $code = @' using System; using System.Globalization; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Net; using System.Net.Sockets; using System.Net.NetworkInformation; using System.Diagnostics; using System.ComponentModel; using Microsoft.Win32; namespace Dfsn.Bpa { // //Information related to Dfs namespace // public class DfsNamespaceInfo { public string Name; public string Type; public UInt32 RootTargetsCount; public Boolean IsSitecostingEnabled; public Boolean IsTargetFailbackEnabled; public Boolean IsABEEnabled; public Boolean IsABEEnabledOnRootShare; public Boolean IsRootTargetOnline; } public class IPAddrInfo { public string IpAddress; public string ADSiteName; } public class NetDfsApis { public const int DFS_VOLUME_FLAVOR_AD_BLOB = 0x0200; public const int DFS_STORAGE_STATE_OFFLINE = 0x0001; public const uint SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM = 0x0800; public const int MAX_PREFERRED_LENGTH = -1; public const int NERR_Success = 0; public const int ERROR_NO_MORE_ITEMS = 259; public const int RPC_S_INVALID_TAG = 1733; public const int RPC_S_SERVER_UNAVAILABLE = 1722; public const int ERROR_INVALID_LEVEL = 124; public const int ERROR_INVALID_PARAMETER = 87; public const int RPC_X_BAD_STUB_DATA= 1783; public const int ERROR_NOT_FOUND = 1168; public const int NERR_Base = 2100; public const int NERR_DfsNoSuchVolume = NERR_Base + 562; public const string WINDOWS2008MODE_NAMESPACE = "Windows2008"; public const string WINDOWS2000MODE_NAMESPACE = "Windows2000"; public const string STANDALONE_NAMESPACE = "Standalone"; // //NetDfsApi Info levels // public enum DfsInfoLevel : int { Level1 = 1, Level2 = 2, Level3 = 3, Level4 = 4, Level6 = 6, Level9 = 9, Level50 = 50, Level100 = 100, Level101 = 101, Level102 = 102, Level105 = 105, Level106 = 106, Level150 = 150, Level200 = 200, Level300 = 300, } // //Dfs Namespace properties flags // [Flags] internal enum DfsProperties : uint { InSite = 0x00000001, RootScalability = 0x00000002, SiteCosting = 0x00000004, TargetFailBack = 0x00000008, ClusterEnabled = 0x00000010, ABEEnabled = 0x00000020, } public enum ShareInfoLevel : int { Level1 = 1, Level502 = 502, Level503 = 503, Level1005 = 1005, } [DllImport("Netapi32.dll", CharSet = CharSet.Auto)] public static extern int NetApiBufferFree(IntPtr buffer); [DllImport("Netapi32.dll", CharSet = CharSet.Unicode)] public static extern int NetShareGetInfo( string servername, string netname, ShareInfoLevel level, out IntPtr bufptr); [DllImport("Netapi32.dll", CharSet = CharSet.Auto)] public static extern int NetDfsEnum( string dfsName, uint level, int prefMaxLen, out IntPtr bufptr, out uint entriesRead, ref uint resumeHandle); [DllImport("Netapi32.dll", CharSet = CharSet.Auto)] public static extern int NetDfsGetInfo( string dfsEntryPath, string serverName, string shareName, uint level, out IntPtr bufptr); [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal struct SHARE_INFO_1005 { public uint flags; } internal enum DfsTargetPriorityClass : int { InvalidPriorityClass = -1, SiteCostNormalPriorityClass = 0, GlobalHighPriorityClass = 1, SiteCostHighPriorityClass = 2, SiteCostLowPriorityClass = 3, GlobalLowPriorityClass = 4, } [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal struct DFS_TARGET_PRIORITY { public DfsTargetPriorityClass targetPriorityClass; public ushort targetPriorityRank; public ushort reserved; } [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal sealed class DFS_INFO_300 { public uint flags; [MarshalAs(UnmanagedType.LPWStr)] public string dfsName; } [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal sealed class DFS_INFO_4 { [MarshalAs(UnmanagedType.LPWStr)] public string entryPath; [MarshalAs(UnmanagedType.LPWStr)] public string comment; public uint state; public uint timeout; public Guid guid; public uint numberOfStorages; public IntPtr storage; } [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal sealed class DFS_INFO_6 { [MarshalAs(UnmanagedType.LPWStr)] public string entryPath; [MarshalAs(UnmanagedType.LPWStr)] public string comment; public uint state; public uint timeout; public Guid guid; public uint propertyFlags; public uint metaDatasSize; public uint numberOfStorages; public IntPtr storage; } [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal sealed class DFS_STORAGE_INFO_1 { public uint state; [MarshalAs(UnmanagedType.LPWStr)] public string serverName; [MarshalAs(UnmanagedType.LPWStr)] public string shareName; public DFS_TARGET_PRIORITY priority; } [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal sealed class DFS_STORAGE_INFO { public uint state; [MarshalAs(UnmanagedType.LPWStr)] public string serverName; [MarshalAs(UnmanagedType.LPWStr)] public string shareName; } [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal sealed class DFS_INFO_50 { public uint namespaceMajorVersion; public uint namespaceMinorVersion; public ulong namespaceCapabilities; } // //Function Description: // Checks if access based enumeration is enabled on a share // //Arguments: // serverName - Name of the server // shareName - Name of the share // IsABEEnabled - Set to true if ABE is enabled; otherwise false // //Return Value: // NERR_Success on success // ERROR status code otherwise // public static int GetABEStatus( string serverName, string shareName, out bool isABEEnabled) { int result = NERR_Success; isABEEnabled = false; if (String.IsNullOrEmpty(serverName) || String.IsNullOrEmpty(shareName)) { return ERROR_INVALID_PARAMETER; } IntPtr buffer = IntPtr.Zero; try { result = NetShareGetInfo( String.Format(CultureInfo.InvariantCulture, "\\\\{0}", serverName), shareName, ShareInfoLevel.Level1005, out buffer); if (result == NERR_Success) { SHARE_INFO_1005 shi1005 = (SHARE_INFO_1005)Marshal.PtrToStructure( buffer, typeof(SHARE_INFO_1005)); isABEEnabled = ( 0 < (shi1005.flags & SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM)); } return result; } finally { if (buffer != IntPtr.Zero) { NetApiBufferFree(buffer); } } } // //Function Description: // Checks if access based enumeration is enabled on a share // //Arguments: // computerName - Name of the server // fqdn - Fully qualified domain name of the share // rootPath - DFS namespace path // //Return Value: // DfsNamespaceInfo object on success // null otherwise // public static DfsNamespaceInfo GetRoot(string computerName, string fqdn, string rootPath) { IntPtr buffer = IntPtr.Zero; int result = NERR_Success; uint level = (uint) DfsInfoLevel.Level6; DfsNamespaceInfo oDfsNamespace = new DfsNamespaceInfo(); // //Initialize // oDfsNamespace.Name = rootPath; oDfsNamespace.Type = String.Empty; oDfsNamespace.IsSitecostingEnabled = false; oDfsNamespace.IsTargetFailbackEnabled = false; oDfsNamespace.IsABEEnabled = false; oDfsNamespace.IsRootTargetOnline = false; oDfsNamespace.IsABEEnabledOnRootShare = false; // // First try the up level query, then revert to down level if necessary // for (int i = 0; i < 2; i++) { try { result = NetDfsGetInfo( rootPath, null, null, level, out buffer); if (result != NERR_Success) { if (result == ERROR_INVALID_LEVEL || result == RPC_X_BAD_STUB_DATA) { level = (uint) DfsInfoLevel.Level4; continue; } return null; } if (level == (uint) DfsInfoLevel.Level6) { DFS_INFO_6 info6 = (DFS_INFO_6)Marshal.PtrToStructure(buffer, typeof(DFS_INFO_6)); oDfsNamespace.RootTargetsCount = info6.numberOfStorages; oDfsNamespace.IsSitecostingEnabled = ((info6.propertyFlags & (uint) DfsProperties.SiteCosting) == (uint) DfsProperties.SiteCosting); oDfsNamespace.IsTargetFailbackEnabled = ((info6.propertyFlags & (uint) DfsProperties.TargetFailBack) == (uint) DfsProperties.TargetFailBack); oDfsNamespace.IsABEEnabled = ((info6.propertyFlags & (uint) DfsProperties.ABEEnabled) == (uint) DfsProperties.ABEEnabled); if ((info6.state & DFS_VOLUME_FLAVOR_AD_BLOB) == DFS_VOLUME_FLAVOR_AD_BLOB) { DFS_INFO_50 dfsInfo50 = null; result = GetDfsVersion(rootPath, out dfsInfo50); if (dfsInfo50 != null) { if (dfsInfo50.namespaceMajorVersion == 2) { oDfsNamespace.Type = WINDOWS2008MODE_NAMESPACE; } else { oDfsNamespace.Type = WINDOWS2000MODE_NAMESPACE; } } else if (result == ERROR_INVALID_LEVEL || result == RPC_X_BAD_STUB_DATA) { oDfsNamespace.Type = WINDOWS2000MODE_NAMESPACE; } } else { oDfsNamespace.Type = STANDALONE_NAMESPACE; } for (int j = 0; j < info6.numberOfStorages; ++j) { IntPtr storageBuffer = (IntPtr)((long)info6.storage + Marshal.SizeOf(typeof(DFS_STORAGE_INFO_1)) * j); DFS_STORAGE_INFO_1 dsi = (DFS_STORAGE_INFO_1)Marshal.PtrToStructure(storageBuffer, typeof(DFS_STORAGE_INFO_1)); if (dsi.serverName.Equals(computerName, StringComparison.OrdinalIgnoreCase) || ((fqdn != null) && (dsi.serverName.Equals(fqdn, StringComparison.OrdinalIgnoreCase)))) { oDfsNamespace.IsRootTargetOnline = ((dsi.state & DFS_STORAGE_STATE_OFFLINE) == 0); // //Do not check the ABE status on root share if namespace is windows2000 mode namespace // if (!oDfsNamespace.Type.Equals("Windows2000", StringComparison.OrdinalIgnoreCase)) { GetABEStatus(computerName, dsi.shareName, out oDfsNamespace.IsABEEnabledOnRootShare); } break; } } } else { DFS_INFO_4 info4 = (DFS_INFO_4)Marshal.PtrToStructure(buffer, typeof(DFS_INFO_4)); oDfsNamespace.RootTargetsCount = info4.numberOfStorages; if ((info4.state & DFS_VOLUME_FLAVOR_AD_BLOB) == DFS_VOLUME_FLAVOR_AD_BLOB) { DFS_INFO_50 dfsInfo50 = null; result = GetDfsVersion(rootPath, out dfsInfo50); if (dfsInfo50 != null) { if (dfsInfo50.namespaceMajorVersion == 2) { oDfsNamespace.Type = WINDOWS2008MODE_NAMESPACE; } else { oDfsNamespace.Type = WINDOWS2000MODE_NAMESPACE; } } else if (result == ERROR_INVALID_LEVEL || result == RPC_X_BAD_STUB_DATA) { oDfsNamespace.Type = WINDOWS2000MODE_NAMESPACE; } } else { oDfsNamespace.Type = STANDALONE_NAMESPACE; } for (int j = 0; j < info4.numberOfStorages; ++j) { IntPtr storageBuffer = (IntPtr)((long)info4.storage + Marshal.SizeOf(typeof(DFS_STORAGE_INFO)) * j); DFS_STORAGE_INFO dsi = (DFS_STORAGE_INFO)Marshal.PtrToStructure(storageBuffer, typeof(DFS_STORAGE_INFO)); if (dsi.serverName.Equals(computerName, StringComparison.OrdinalIgnoreCase) || ((fqdn != null) && (dsi.serverName.Equals(fqdn, StringComparison.OrdinalIgnoreCase)))) { oDfsNamespace.IsRootTargetOnline = ((dsi.state & DFS_STORAGE_STATE_OFFLINE) == 0); break; } } } break; } catch { return null; } finally { if (buffer != IntPtr.Zero) { NetApiBufferFree(buffer); } } } return oDfsNamespace; } // //Function Description: // Returns the Dfs Namespace version // //Arguments: // rootPath - DFS namespace path // dfsInfo50 - DFS_INFO_50 structure // //Return Value: // NERR_Success on success // ERROR status code otherwise // private static int GetDfsVersion(string rootPath, out DFS_INFO_50 dfsInfo50) { IntPtr buffer = IntPtr.Zero; uint level = (uint)DfsInfoLevel.Level50; int result = NERR_Success; dfsInfo50 = null; try { result = NetDfsGetInfo( rootPath, null, null, level, out buffer); if (result == NERR_Success) { dfsInfo50 = (DFS_INFO_50)Marshal.PtrToStructure(buffer, typeof(DFS_INFO_50)); } } catch { dfsInfo50 = null; return result; } finally { if (buffer != IntPtr.Zero) { NetApiBufferFree(buffer); } } return result; } // //Function Description: // Enumerate and returns the list of Dfs Namespaces // //Arguments: // computerName - Name of the server to enumerate the namespaces on // //Return Value: // List of DfsNamespaceInfo objects on success // null otherwise // public static List GetDfsNamespaceInfo(string computerName) { uint resumeHandle = 0; int result = NERR_Success; uint level = (uint) DfsInfoLevel.Level300; List dfsNamespaceList = new List(); IntPtr buffer = IntPtr.Zero; string fqdn = null; try { IPHostEntry host = Dns.GetHostEntry(computerName); fqdn = host.HostName; } catch { fqdn = null; } try { uint entriesRead = 0; result = NetDfsEnum( computerName, level, MAX_PREFERRED_LENGTH, out buffer, out entriesRead, ref resumeHandle); if (result == NERR_Success) { for (int i = 0; i < entriesRead; ++i) { IntPtr rootBuffer = (IntPtr)((long)buffer + Marshal.SizeOf(typeof(DFS_INFO_300)) * i); DFS_INFO_300 info300 = (DFS_INFO_300)Marshal.PtrToStructure(rootBuffer, typeof(DFS_INFO_300)); // //Add "\\" at the begining of the root path // string rootPath = "\\" + info300.dfsName; // //Get namespace information // DfsNamespaceInfo oDfsNamespace = GetRoot(computerName, fqdn, rootPath); if (oDfsNamespace != null) { dfsNamespaceList.Add(oDfsNamespace); } } } else if (result != ERROR_NO_MORE_ITEMS) { throw new Win32Exception((Int32)result, "NetDfsEnum failed."); } } finally { if (buffer != IntPtr.Zero) { NetApiBufferFree(buffer); } } return dfsNamespaceList; } } public class DsApis { public const int NERR_Success = 0; public const int ERROR_INVALID_FLAGS = 1004; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct SOCKET_ADDRESS { public IntPtr sockaddr; public int sockaddrLength; } [StructLayout(LayoutKind.Sequential)] internal struct SOCKADDR_IN { public short sin_family; public ushort sin_port; public in_addr sin_addr; public UInt64 sin_zero; // 8 reserved bytes } [StructLayout(LayoutKind.Sequential)] public struct SOCKADDR_IN6 { public short sin6_family; public ushort sin6_port; public Int32 sin6_flowinfo; public byte s_b1; public byte s_b2; public byte s_b3; public byte s_b4; public byte s_b5; public byte s_b6; public byte s_b7; public byte s_b8; public byte s_b9; public byte s_b10; public byte s_b11; public byte s_b12; public byte s_b13; public byte s_b14; public byte s_b15; public byte s_b16; public Int32 sin6_scope_id; } [StructLayout(LayoutKind.Sequential)] internal struct in_addr { public byte s_b1; public byte s_b2; public byte s_b3; public byte s_b4; } // // DomainControllerInfo structure used in DsGetDCName function. // [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal struct DOMAIN_CONTROLLER_INFO { public string DomainControllerName; public string DomainControllerAddress; public uint DomainControllerAddressType; public Guid DomainGuid; public string DomainName; public string DnsForestName; public uint Flags; public string DcSiteName; public string ClientSiteName; } [Flags] internal enum ADLocatorFlags : uint { None = 0, ForceRediscovery = 0x00000001, DirectoryServicesRequired = 0x00000010, DirectoryServicesPreferred = 0x00000020, GCRequired = 0x00000040, PdcRequired = 0x00000080, IPRequired = 0x00000200, KdcRequired = 0x00000400, TimeServerRequired = 0x00000800, WriteableRequired = 0x00001000, GoodTimeServerPreferred = 0x00002000, AvoidSelf = 0x00004000, OnlyLdapNeeded = 0x00008000, IsFlatName = 0x00010000, IsDnsName = 0x00020000, TryNextClosestSite = 0x00040000, DirectoryServices6Required = 0x00080000, WebServiceRequired = 0x00100000, ReturnDnsName = 0x40000000, ReturnFlatName = 0x80000000, } [DllImport("Netapi32.dll", CharSet=CharSet.Auto)] internal static extern int DsGetDcName ( string ComputerName, string DomainName, int DomainGuid, string SiteName, uint Flags, out IntPtr pDOMAIN_CONTROLLER_INFO ); [DllImport("Netapi32.dll", CharSet = CharSet.Auto)] public static extern int NetApiBufferFree(IntPtr buffer); [DllImport("Netapi32.dll", CharSet=CharSet.Auto)] public static extern int DsAddressToSiteNames( [In] string computerName, [In] int entryCount, [In] SOCKET_ADDRESS[] socketAddresses, [Out] out IntPtr siteNames); // //Function Description: // Return domain controller of a current host's domain // //Arguments: // none // //Return Value: // Name of the domain controller on success // null otherwise // private static string GetDc() { int result = NERR_Success; IntPtr pDCInfo = IntPtr.Zero; string dcName = null; try { uint dcLocFlags = (uint) ADLocatorFlags.DirectoryServicesPreferred; result = DsGetDcName(null, null, 0, null, dcLocFlags, out pDCInfo); //check return value for error if (result == NERR_Success) { DOMAIN_CONTROLLER_INFO domainControllerInfo; domainControllerInfo = (DOMAIN_CONTROLLER_INFO)Marshal.PtrToStructure(pDCInfo, typeof(DOMAIN_CONTROLLER_INFO)); if (domainControllerInfo.DomainControllerName.StartsWith(@"\\")) { //Remove the preceding "\\" dcName = domainControllerInfo.DomainControllerName.Substring(2); } else { dcName = domainControllerInfo.DomainControllerName; } } return dcName; } finally { NetApiBufferFree(pDCInfo); } } // //Function Description: // Return list of IP addresses and associated AD site names of the current host // //Arguments: // none // //Return Value: // List of IPAddrInfo objects on success // null otherwise // public static List GetIPAddrInfo() { IntPtr siteNames = IntPtr.Zero; List IPAddressesInfo = new List(); List addresses = new List(); List pinnedAddresses = null; int result = NERR_Success; try { string dcName = GetDc(); if (dcName == null) { return null; } NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface adapter in adapters) { IPInterfaceProperties adapterProperties = adapter.GetIPProperties(); UnicastIPAddressInformationCollection uniCast = adapterProperties.UnicastAddresses; if (uniCast.Count >0) { foreach (IPAddressInformation uni in uniCast) { if (uni.IsDnsEligible) { addresses.Add(uni.Address); } } } } SOCKET_ADDRESS[] socketAddr = new SOCKET_ADDRESS[addresses.Count]; pinnedAddresses = new List(addresses.Count); for (int i = 0; i < addresses.Count; i++) { IPAddress address = addresses[i]; GCHandle pinnedAddress; if (address.AddressFamily == AddressFamily.InterNetwork) { // // fill IPv4 unmanaged address structure (SOCKADDR_IN) // byte[] addrBytes = address.GetAddressBytes(); SOCKADDR_IN socketAddress = new SOCKADDR_IN(); socketAddress.sin_family = (short)AddressFamily.InterNetwork; socketAddress.sin_addr.s_b1 = addrBytes[0]; socketAddress.sin_addr.s_b2 = addrBytes[1]; socketAddress.sin_addr.s_b3 = addrBytes[2]; socketAddress.sin_addr.s_b4 = addrBytes[3]; pinnedAddress = GCHandle.Alloc(socketAddress, GCHandleType.Pinned); pinnedAddresses.Add(pinnedAddress); socketAddr[i].sockaddr = pinnedAddress.AddrOfPinnedObject(); socketAddr[i].sockaddrLength = Marshal.SizeOf(typeof(SOCKADDR_IN)); } else { byte[] addrBytes = address.GetAddressBytes(); SOCKADDR_IN6 socketAddress = new SOCKADDR_IN6(); socketAddress.sin6_family = (short)address.AddressFamily; // Copy the Address bytes socketAddress.s_b1 = addrBytes[0]; socketAddress.s_b2 = addrBytes[1]; socketAddress.s_b3 = addrBytes[2]; socketAddress.s_b4 = addrBytes[3]; socketAddress.s_b5 = addrBytes[4]; socketAddress.s_b6 = addrBytes[5]; socketAddress.s_b7 = addrBytes[6]; socketAddress.s_b8 = addrBytes[7]; socketAddress.s_b9 = addrBytes[8]; socketAddress.s_b10 = addrBytes[9]; socketAddress.s_b11 = addrBytes[10]; socketAddress.s_b12 = addrBytes[11]; socketAddress.s_b13 = addrBytes[12]; socketAddress.s_b14 = addrBytes[13]; socketAddress.s_b15 = addrBytes[14]; socketAddress.s_b16 = addrBytes[15]; pinnedAddress = GCHandle.Alloc(socketAddress, GCHandleType.Pinned); pinnedAddresses.Add(pinnedAddress); socketAddr[i].sockaddr = pinnedAddress.AddrOfPinnedObject(); socketAddr[i].sockaddrLength = Marshal.SizeOf(typeof(SOCKADDR_IN6)); } } result = DsAddressToSiteNames( dcName, addresses.Count, socketAddr, out siteNames); if (result != NERR_Success) { return null; } for (int i = 0; i < addresses.Count; i++) { IPAddress address = addresses[i]; IntPtr stringPtr = Marshal.ReadIntPtr( siteNames, i * Marshal.SizeOf(typeof(IntPtr))); IPAddrInfo ipAddr = new IPAddrInfo(); ipAddr.IpAddress = address.ToString(); ipAddr.ADSiteName = Marshal.PtrToStringAuto(stringPtr); IPAddressesInfo.Add(ipAddr); } return(IPAddressesInfo); } catch { return null; } finally { if (siteNames != IntPtr.Zero) { NetApiBufferFree(siteNames); siteNames = IntPtr.Zero; } for (int i = 0; i < addresses.Count; i++) { if (pinnedAddresses[i].IsAllocated) { pinnedAddresses[i].Free(); } } } } } } '@ Compile-CSharp($code) } # # Function Description: # # Check if the current host is a domain controller # # Arguments: # # none # # Return Value: # # $true - If the server is DC # $false - If the server is not a DC # function GetIsDomainController() { $IsDc = $false $operatingSystem = get-wmiobject -class Win32_OperatingSystem |select-object -property ProductType if ($operatingSystem.ProductType -eq 2) { $IsDc = $true } $IsDc } # # Function Description: # # Check if the domain functional level of a current host's domain # is Windows2008 or above # # Arguments: # # none # # Return Value: # # $true - If domain functional level is Windows2008 or above # $false - If domain functional level is below Windows2008 # function GetIsDFLAbove2008() { trap { write-debug $error[0] continue } $Dfl = $null $Dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() if ( $Dom -eq $null ) { $Dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain() } if ($Dom) { if ($Dom.DomainMode -ge [System.DirectoryServices.ActiveDirectory.domainmode]::Windows2008Domain) { $Dfl = $true } else { $Dfl = $false } } $Dfl } # # Function Description: # # Check if the forest functional level of a current host # is Windows2003 or above # # Arguments: # # none # # Return Value: # # $true - If forest functional level is Windows2003 or above # $false - If forest functional level is below Windows2003 # function GetIsFFLAbove2003() { trap { write-debug $error[0] continue } $Ffl = $null $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() if ($Forest) { if ($Forest.ForestMode -ge [System.DirectoryServices.ActiveDirectory.forestmode]::Windows2003Forest) { $Ffl = $true } else { $Ffl = $false } } $Ffl } # # Function Description: # # Get the DFS-N registry settings on a current host # # Arguments: # # $xmlDoc - XmlDocument manipulated # $ns - namespace used for the element # $dfsnNode - parent node # # Return Value: # # none # function GetDFSNRegistrySettings($xmlDoc, $ns, $dfsnNode) { trap { write-error $error[0] return } $IsSiteCostedReferralEnabled = $true $LdapTimeoutValueInSeconds = 30 $IsSysvolNetlogonTargetFailbackEnabled = $false $SyncIntervalInSeconds = 3600 $key = Get-Item HKLM:\System\CurrentControlSet\Services\Dfs\Parameters $values = Get-ItemProperty $key.PSPath if ($Values -ne $null) { if ($Values.SiteCostedReferrals -eq 0) { $IsSiteCostedReferralEnabled = $false } if ($Values.LdapTimeoutValueInSeconds -ne $null) { $LdapTimeoutValueInSeconds = $Values.LdapTimeoutValueInSeconds } if ($Values.SysvolNetlogonTargetFailback) { $IsSysvolNetlogonTargetFailbackEnabled = $true } if ($Values.SyncIntervalInSeconds -ne $null) { $SyncIntervalInSeconds = $Values.SyncIntervalInSeconds } } Append-XmlElement $xmlDoc $dfsnNode $ns "IsSysvolNetlogonTargetFailbackEnabled" (Formalize-BoolValue $IsSysvolNetlogonTargetFailbackEnabled) Append-XmlElement $xmlDoc $dfsnNode $ns "IsSiteCostedReferralEnabled" (Formalize-BoolValue $IsSiteCostedReferralEnabled) Append-XmlElement $xmlDoc $dfsnNode $ns "LdapTimeoutValueInSeconds" $LdapTimeoutValueInSeconds Append-XmlElement $xmlDoc $dfsnNode $ns "SyncIntervalInSeconds" $SyncIntervalInSeconds } # # Function Description: # # Enumerate Dfs Namespaces on a machine and get Dfs Namespace settings # # Arguments: # # $xmlDoc - XmlDocument manipulated # $ns - namespace used for the element # $dfsnNode - parent node # $computerName - the computer name to enumerate namespace on # # Return Value: # # none # function GetDfsNamespaceInfo($xmlDoc, $ns, $dfsnNode, $computerName, $isClusterRes) { trap { write-error $error[0] return } $dfsNamespaceList = [Dfsn.Bpa.NetDfsApis]::GetDfsNamespaceInfo($computerName) Foreach ($dfsNamespace in $dfsNamespaceList) { $dfsNamespaceNode = $xmlDoc.CreateElement("DFSNamespace", $ns) [void]$dfsnNode.AppendChild($dfsNamespaceNode) Append-XmlElement $xmlDoc $dfsNamespaceNode $ns "Name" ($dfsNamespace.Name) if ([String]$dfsNamespace.Type -ne [String]::Empty) { Append-XmlElement $xmlDoc $dfsNamespaceNode $ns "Type" ($dfsNamespace.Type) } Append-XmlElement $xmlDoc $dfsNamespaceNode $ns "RootTargetsCount" ($dfsNamespace.RootTargetsCount) Append-XmlElement $xmlDoc $dfsNamespaceNode $ns "IsSiteCostingEnabled" (Formalize-BoolValue $dfsNamespace.IsSitecostingEnabled) Append-XmlElement $xmlDoc $dfsNamespaceNode $ns "IsTargetFailbackEnabled" (Formalize-BoolValue $dfsNamespace.IsTargetFailbackEnabled) Append-XmlElement $xmlDoc $dfsNamespaceNode $ns "IsRootTargetOnline" (Formalize-BoolValue $dfsNamespace.IsRootTargetOnline) Append-XmlElement $xmlDoc $dfsNamespaceNode $ns "IsABEEnabled" (Formalize-BoolValue $dfsNamespace.IsABEEnabled) Append-XmlElement $xmlDoc $dfsNamespaceNode $ns "IsABEEnabledOnRootShare" (Formalize-BoolValue $dfsNamespace.IsABEEnabledOnRootShare) Append-XmlElement $xmlDoc $dfsNamespaceNode $ns "IsCluster" (Formalize-BoolValue $isClusterRes) } } # # Function Description: # # This function will update the XML document with DFS-N data # # Arguments: # # $xmlDoc - XmlDocument manipulated # $ns - namespace used for the element # # Return Value: # # none # function GetDFSNXml($xmlDoc, $ns) { #Compile C# code Call-NetAPIs #Create DFSN node $dfsnNode = $xmlDoc.CreateElement("DFSN", $ns) [void]$xmlDoc.DocumentElement.AppendChild($dfsnNode) $computerName = [System.Net.Dns]::GetHostName() #Check Dfs Namespace service status $checkDFSNServiceStatus = Check-ServiceStatus $computerName "dfs" Append-XmlElement $xmlDoc $dfsnNode $ns "IsServiceStarted" (Formalize-BoolValue $checkDFSNServiceStatus) #Check Dfs Namespace service start-up type $path = "HKLM:\SYSTEM\CurrentControlSet\Services\Dfs" $dfsServiceStartType = get-itemproperty -path $path -name 'Start' if ($dfsServiceStartType -ne $null) { if ($dfsServiceStartType.Start -eq [System.ServiceProcess.ServiceStartMode]::Automatic) { $IsServiceStartTypeAuto = $true } else { $IsServiceStartTypeAuto = $false } Append-XmlElement $xmlDoc $dfsnNode $ns "IsServiceStartTypeAuto" (Formalize-BoolValue $IsServiceStartTypeAuto) } #Get IP address to AD site mapping $ipAddresses = [Dfsn.Bpa.DsApis]::GetIPAddrInfo() if ($ipAddresses -ne $null) { $ipAddressListNode = $xmlDoc.CreateElement("IPAddressList", $ns) [void]$dfsnNode.AppendChild($ipAddressListNode) Foreach ($ipAddr in $ipAddresses) { $ipAddressNode = $xmlDoc.CreateElement("IPAddress", $ns) [void]$ipAddressListNode.AppendChild($ipAddressNode) Append-XmlElement $xmlDoc $ipAddressNode $ns "IPAddr" ($ipAddr.IpAddress) Append-XmlElement $xmlDoc $ipAddressNode $ns "ADSite" ($ipAddr.ADSiteName) } } #Check if the current host is a domain controller $isDC = GetIsDomainController Append-XmlElement $xmlDoc $dfsnNode $ns "IsDomainController" (Formalize-BoolValue $isDC) #Get DFS-N registry seetings GetDFSNRegistrySettings $xmlDoc $ns $dfsnNode #Check forest functional level $isFFLAbove2003 = GetIsFFLAbove2003 if ($isFFLAbove2003 -ne $null) { Append-XmlElement $xmlDoc $dfsnNode $ns "IsFFLAbove2003" (Formalize-BoolValue $isFFLAbove2003) } #Check domain functional level $isDFLAbove2008 = GetIsDFLAbove2008 if ($isDFLAbove2008 -ne $null) { Append-XmlElement $xmlDoc $dfsnNode $ns "IsDFLAbove2008" (Formalize-BoolValue $isDFLAbove2008) } #If Dfs Namespace service is not started then do not collect more information if ($checkDFSNServiceStatus -eq $true) { #Get Dfs namespaces information on the server GetDfsNamespaceInfo $xmlDoc $ns $dfsnNode $computerName $false #Check if this server is part of a cluster $isCluster = Get-ServerInClusterStatus if ($isCluster -eq $true) { $resources = get-clusterresource foreach($resource in $resources) { if(($resource.State -eq "Online") -and ($resource.ResourceType.Name -eq "Network Name")) { GetDfsNamespaceInfo $xmlDoc $ns $dfsnNode $resource.Name $true } } } } } # # ------------------ # FUNCTIONS - END # ------------------ # # # ------------------------ # SCRIPT MAIN BODY - START # ------------------------ # trap { write-error $error[0] continue } # # Initialize to perform querying Role information # Setup # # Set the Target Namespace to be used by XML # $tns="http://schemas.microsoft.com/bestpractices/models/FileServices/DFSN/2011/04" # # Create a new XmlDocument # $doc = Create-DocumentElement $tns "DFSNComposite" $dfsnInstalled = Check-ServiceInstallStatus "dfs" if($dfsnInstalled -eq $true) { GetDFSNXml $doc $tns } # # Role Information obtained. # TearDown $doc #$doc.Save([Environment]::SystemDirectory + "\BestPractices\v1.0\Models\Microsoft\Windows\FileServices\tempDFSN.xml")