]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
netcmd: domain: move provision command to domain/provision.py
authorRob van der Linde <rob@catalyst.net.nz>
Fri, 31 Mar 2023 00:28:17 +0000 (13:28 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Fri, 31 Mar 2023 07:25:32 +0000 (07:25 +0000)
Signed-off-by: Rob van der Linde <rob@catalyst.net.nz>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
python/samba/netcmd/domain/__init__.py
python/samba/netcmd/domain/provision.py [new file with mode: 0644]

index 7cae91fc76adf4a72845ae937945f744c7073c7f..ae6936e6307c096e2d9a132b913b32dfef647597 100644 (file)
@@ -25,7 +25,6 @@
 import samba.getopt as options
 import ldb
 import os
-import sys
 import ctypes
 import tempfile
 import subprocess
@@ -38,7 +37,7 @@ from getpass import getpass
 from samba.net import Net, LIBNET_JOIN_AUTOMATIC
 import samba.ntacls
 from samba.auth import system_session
-from samba.samdb import SamDB, get_default_backend_store
+from samba.samdb import SamDB
 from samba.dcerpc import drsblobs
 from samba.dcerpc import lsa
 from samba.dcerpc import netlogon
@@ -52,38 +51,9 @@ from samba.netcmd import (
 )
 from samba.netcmd.fsmo import get_fsmo_roleowner
 from samba import string_to_byte_array
-from samba.auth_util import system_session_unix
 from samba import is_ad_dc_built
 
-from samba.dsdb import (
-    DS_DOMAIN_FUNCTION_2000,
-    DS_DOMAIN_FUNCTION_2003,
-    DS_DOMAIN_FUNCTION_2003_MIXED,
-    DS_DOMAIN_FUNCTION_2008,
-    DS_DOMAIN_FUNCTION_2008_R2,
-    DS_DOMAIN_FUNCTION_2012,
-    DS_DOMAIN_FUNCTION_2012_R2,
-    DS_DOMAIN_FUNCTION_2016,
-    DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL,
-    DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL,
-    UF_WORKSTATION_TRUST_ACCOUNT,
-    UF_SERVER_TRUST_ACCOUNT,
-    UF_TRUSTED_FOR_DELEGATION,
-    UF_PARTIAL_SECRETS_ACCOUNT
-)
-
-from samba.provision import (
-    provision,
-    ProvisioningError,
-    DEFAULT_MIN_PWD_LENGTH,
-    setup_path
-)
-
-from samba.provision.common import (
-    FILL_FULL,
-    FILL_NT4SYNC,
-    FILL_DRS
-)
+from samba.provision import setup_path
 
 from samba.trust_utils import CreateTrustedDomainRelax
 
@@ -100,366 +70,7 @@ from .keytab import cmd_domain_export_keytab
 from .leave import cmd_domain_leave
 from .level import cmd_domain_level
 from .passwordsettings import cmd_domain_passwordsettings
-
-
-class cmd_domain_provision(Command):
-    """Provision a domain."""
-
-    synopsis = "%prog [options]"
-
-    takes_optiongroups = {
-        "sambaopts": options.SambaOptions,
-        "versionopts": options.VersionOptions,
-    }
-
-    takes_options = [
-        Option("--interactive", help="Ask for names", action="store_true"),
-        Option("--domain", type="string", metavar="DOMAIN",
-               help="NetBIOS domain name to use"),
-        Option("--domain-guid", type="string", metavar="GUID",
-               help="set domainguid (otherwise random)"),
-        Option("--domain-sid", type="string", metavar="SID",
-               help="set domainsid (otherwise random)"),
-        Option("--ntds-guid", type="string", metavar="GUID",
-               help="set NTDS object GUID (otherwise random)"),
-        Option("--invocationid", type="string", metavar="GUID",
-               help="set invocationid (otherwise random)"),
-        Option("--host-name", type="string", metavar="HOSTNAME",
-               help="set hostname"),
-        Option("--host-ip", type="string", metavar="IPADDRESS",
-               help="set IPv4 ipaddress"),
-        Option("--host-ip6", type="string", metavar="IP6ADDRESS",
-               help="set IPv6 ipaddress"),
-        Option("--site", type="string", metavar="SITENAME",
-               help="set site name"),
-        Option("--adminpass", type="string", metavar="PASSWORD",
-               help="choose admin password (otherwise random)"),
-        Option("--krbtgtpass", type="string", metavar="PASSWORD",
-               help="choose krbtgt password (otherwise random)"),
-        Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
-               choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
-               help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
-               "BIND9_FLATFILE uses bind9 text database to store zone information, "
-               "BIND9_DLZ uses samba4 AD to store zone information, "
-               "NONE skips the DNS setup entirely (not recommended)",
-               default="SAMBA_INTERNAL"),
-        Option("--dnspass", type="string", metavar="PASSWORD",
-               help="choose dns password (otherwise random)"),
-        Option("--root", type="string", metavar="USERNAME",
-               help="choose 'root' unix username"),
-        Option("--nobody", type="string", metavar="USERNAME",
-               help="choose 'nobody' user"),
-        Option("--users", type="string", metavar="GROUPNAME",
-               help="choose 'users' group"),
-        Option("--blank", action="store_true",
-               help="do not add users or groups, just the structure"),
-        Option("--server-role", type="choice", metavar="ROLE",
-               choices=["domain controller", "dc", "member server", "member", "standalone"],
-               help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
-               default="domain controller"),
-        Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
-               choices=["2000", "2003", "2008", "2008_R2"],
-               help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
-               default="2008_R2"),
-        Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
-               choices=["2008_R2", "2008_R2_old", "2012", "2012_R2", "2016", "2019"],
-               help="The base schema files to use. Default is (Windows) 2019.",
-               default="2019"),
-        Option("--adprep-level", type="choice", metavar="FUNCTION_LEVEL",
-               choices=["SKIP", "2008_R2", "2012", "2012_R2", "2016"],
-               help="The highest functional level to prepare for. Default is based on --base-schema",
-               default=None),
-        Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
-               help="The initial nextRid value (only needed for upgrades).  Default is 1000."),
-        Option("--partitions-only",
-               help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
-        Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
-    ]
-
-    ntvfs_options = [
-        Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
-               metavar="[yes|no|auto]",
-               help="Define if we should use the native fs capabilities or a tdb file for "
-               "storing attributes likes ntacl when --use-ntvfs is set. "
-               "auto tries to make an inteligent guess based on the user rights and system capabilities",
-               default="auto")
-    ]
-
-    takes_options.extend(common_provision_join_options)
-
-    if samba.is_ntvfs_fileserver_built():
-        takes_options.extend(common_ntvfs_options)
-        takes_options.extend(ntvfs_options)
-
-    takes_args = []
-
-    def run(self, sambaopts=None, versionopts=None,
-            interactive=None,
-            domain=None,
-            domain_guid=None,
-            domain_sid=None,
-            ntds_guid=None,
-            invocationid=None,
-            host_name=None,
-            host_ip=None,
-            host_ip6=None,
-            adminpass=None,
-            site=None,
-            krbtgtpass=None,
-            machinepass=None,
-            dns_backend=None,
-            dns_forwarder=None,
-            dnspass=None,
-            ldapadminpass=None,
-            root=None,
-            nobody=None,
-            users=None,
-            quiet=None,
-            blank=None,
-            server_role=None,
-            function_level=None,
-            adprep_level=None,
-            next_rid=None,
-            partitions_only=None,
-            targetdir=None,
-            use_xattrs="auto",
-            use_ntvfs=False,
-            use_rfc2307=None,
-            base_schema=None,
-            plaintext_secrets=False,
-            backend_store=None,
-            backend_store_size=None):
-
-        self.logger = self.get_logger(name="provision", quiet=quiet)
-
-        lp = sambaopts.get_loadparm()
-        smbconf = lp.configfile
-
-        if dns_forwarder is not None:
-            suggested_forwarder = dns_forwarder
-        else:
-            suggested_forwarder = self._get_nameserver_ip()
-            if suggested_forwarder is None:
-                suggested_forwarder = "none"
-
-        if len(self.raw_argv) == 1:
-            interactive = True
-
-        if interactive:
-            from getpass import getpass
-            import socket
-
-            def ask(prompt, default=None):
-                if default is not None:
-                    print("%s [%s]: " % (prompt, default), end=' ')
-                else:
-                    print("%s: " % (prompt,), end=' ')
-                sys.stdout.flush()
-                return sys.stdin.readline().rstrip("\n") or default
-
-            try:
-                default = socket.getfqdn().split(".", 1)[1].upper()
-            except IndexError:
-                default = None
-            realm = ask("Realm", default)
-            if realm in (None, ""):
-                raise CommandError("No realm set!")
-
-            try:
-                default = realm.split(".")[0]
-            except IndexError:
-                default = None
-            domain = ask("Domain", default)
-            if domain is None:
-                raise CommandError("No domain set!")
-
-            server_role = ask("Server Role (dc, member, standalone)", "dc")
-
-            dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
-            if dns_backend in (None, ''):
-                raise CommandError("No DNS backend set!")
-
-            if dns_backend == "SAMBA_INTERNAL":
-                dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
-                if dns_forwarder.lower() in (None, 'none'):
-                    suggested_forwarder = None
-                    dns_forwarder = None
-
-            while True:
-                adminpassplain = getpass("Administrator password: ")
-                issue = self._adminpass_issue(adminpassplain)
-                if issue:
-                    self.errf.write("%s.\n" % issue)
-                else:
-                    adminpassverify = getpass("Retype password: ")
-                    if not adminpassplain == adminpassverify:
-                        self.errf.write("Sorry, passwords do not match.\n")
-                    else:
-                        adminpass = adminpassplain
-                        break
-
-        else:
-            realm = sambaopts._lp.get('realm')
-            if realm is None:
-                raise CommandError("No realm set!")
-            if domain is None:
-                raise CommandError("No domain set!")
-
-        if adminpass:
-            issue = self._adminpass_issue(adminpass)
-            if issue:
-                raise CommandError(issue)
-        else:
-            self.logger.info("Administrator password will be set randomly!")
-
-        if function_level == "2000":
-            dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
-        elif function_level == "2003":
-            dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
-        elif function_level == "2008":
-            dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
-        elif function_level == "2008_R2":
-            dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
-
-        if adprep_level is None:
-            # Select the adprep_level default based
-            # on what the base schema premits
-            if base_schema in ["2008_R2", "2008_R2_old"]:
-                # without explicit --adprep-level=2008_R2
-                # we will skip the adprep step on
-                # provision
-                adprep_level = "SKIP"
-            elif base_schema in ["2012"]:
-                adprep_level = "2012"
-            elif base_schema in ["2012_R2"]:
-                adprep_level = "2012_R2"
-            else:
-                adprep_level = "2016"
-
-        if adprep_level == "SKIP":
-            provision_adprep_level = None
-        elif adprep_level == "2008R2":
-            provision_adprep_level = DS_DOMAIN_FUNCTION_2008_R2
-        elif adprep_level == "2012":
-            provision_adprep_level = DS_DOMAIN_FUNCTION_2012
-        elif adprep_level == "2012_R2":
-            provision_adprep_level = DS_DOMAIN_FUNCTION_2012_R2
-        elif adprep_level == "2016":
-            provision_adprep_level = DS_DOMAIN_FUNCTION_2016
-
-        if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
-            dns_forwarder = suggested_forwarder
-
-        samdb_fill = FILL_FULL
-        if blank:
-            samdb_fill = FILL_NT4SYNC
-        elif partitions_only:
-            samdb_fill = FILL_DRS
-
-        if targetdir is not None:
-            if not os.path.isdir(targetdir):
-                os.makedirs(targetdir)
-
-        eadb = True
-
-        if use_xattrs == "yes":
-            eadb = False
-        elif use_xattrs == "auto" and use_ntvfs == False:
-            eadb = False
-        elif use_ntvfs == False:
-            raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use).  "
-                               "Please re-run with --use-xattrs omitted.")
-        elif use_xattrs == "auto" and not lp.get("posix:eadb"):
-            if targetdir:
-                file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
-            else:
-                file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
-            try:
-                try:
-                    samba.ntacls.setntacl(lp, file.name,
-                                          "O:S-1-5-32G:S-1-5-32",
-                                          "S-1-5-32",
-                                          system_session_unix(),
-                                          "native")
-                    eadb = False
-                except Exception:
-                    self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
-            finally:
-                file.close()
-
-        if eadb:
-            self.logger.info("not using extended attributes to store ACLs and other metadata. If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
-
-        if domain_sid is not None:
-            domain_sid = security.dom_sid(domain_sid)
-
-        session = system_session()
-        if backend_store is None:
-            backend_store = get_default_backend_store()
-        try:
-            result = provision(self.logger,
-                               session, smbconf=smbconf, targetdir=targetdir,
-                               samdb_fill=samdb_fill, realm=realm, domain=domain,
-                               domainguid=domain_guid, domainsid=domain_sid,
-                               hostname=host_name,
-                               hostip=host_ip, hostip6=host_ip6,
-                               sitename=site, ntdsguid=ntds_guid,
-                               invocationid=invocationid, adminpass=adminpass,
-                               krbtgtpass=krbtgtpass, machinepass=machinepass,
-                               dns_backend=dns_backend, dns_forwarder=dns_forwarder,
-                               dnspass=dnspass, root=root, nobody=nobody,
-                               users=users,
-                               serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
-                               useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
-                               use_rfc2307=use_rfc2307, skip_sysvolacl=False,
-                               base_schema=base_schema,
-                               adprep_level=provision_adprep_level,
-                               plaintext_secrets=plaintext_secrets,
-                               backend_store=backend_store,
-                               backend_store_size=backend_store_size)
-
-        except ProvisioningError as e:
-            raise CommandError("Provision failed", e)
-
-        result.report_logger(self.logger)
-
-    def _get_nameserver_ip(self):
-        """Grab the nameserver IP address from /etc/resolv.conf."""
-        from os import path
-        RESOLV_CONF = "/etc/resolv.conf"
-
-        if not path.isfile(RESOLV_CONF):
-            self.logger.warning("Failed to locate %s" % RESOLV_CONF)
-            return None
-
-        handle = None
-        try:
-            handle = open(RESOLV_CONF, 'r')
-            for line in handle:
-                if not line.startswith('nameserver'):
-                    continue
-                # we want the last non-space continuous string of the line
-                return line.strip().split()[-1]
-        finally:
-            if handle is not None:
-                handle.close()
-
-        self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
-
-    def _adminpass_issue(self, adminpass):
-        """Returns error string for a bad administrator password,
-        or None if acceptable"""
-        if isinstance(adminpass, bytes):
-            adminpass = adminpass.decode('utf8')
-        if len(adminpass) < DEFAULT_MIN_PWD_LENGTH:
-            return "Administrator password does not meet the default minimum" \
-                " password length requirement (%d characters)" \
-                % DEFAULT_MIN_PWD_LENGTH
-        elif not samba.check_password_quality(adminpass):
-            return "Administrator password does not meet the default" \
-                " quality standards"
-        else:
-            return None
+from .provision import cmd_domain_provision
 
 
 class cmd_domain_samba3upgrade(cmd_domain_classicupgrade):
diff --git a/python/samba/netcmd/domain/provision.py b/python/samba/netcmd/domain/provision.py
new file mode 100644 (file)
index 0000000..a503d1a
--- /dev/null
@@ -0,0 +1,408 @@
+# domain management - domain provision
+#
+# Copyright Matthias Dieter Wallnoefer 2009
+# Copyright Andrew Kroeger 2009
+# Copyright Jelmer Vernooij 2007-2012
+# Copyright Giampaolo Lauria 2011
+# Copyright Matthieu Patou <mat@matws.net> 2011
+# Copyright Andrew Bartlett 2008-2015
+# Copyright Stefan Metzmacher 2012
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import sys
+import tempfile
+
+import samba
+import samba.getopt as options
+from samba.auth import system_session
+from samba.auth_util import system_session_unix
+from samba.dcerpc import security
+from samba.dsdb import (
+    DS_DOMAIN_FUNCTION_2000,
+    DS_DOMAIN_FUNCTION_2003,
+    DS_DOMAIN_FUNCTION_2008,
+    DS_DOMAIN_FUNCTION_2008_R2,
+    DS_DOMAIN_FUNCTION_2012,
+    DS_DOMAIN_FUNCTION_2012_R2,
+    DS_DOMAIN_FUNCTION_2016
+)
+from samba.netcmd import Command, CommandError, Option
+from samba.provision import DEFAULT_MIN_PWD_LENGTH, ProvisioningError, provision
+from samba.provision.common import FILL_DRS, FILL_FULL, FILL_NT4SYNC
+from samba.samdb import get_default_backend_store
+
+from .common import common_ntvfs_options, common_provision_join_options
+
+
+class cmd_domain_provision(Command):
+    """Provision a domain."""
+
+    synopsis = "%prog [options]"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "versionopts": options.VersionOptions,
+    }
+
+    takes_options = [
+        Option("--interactive", help="Ask for names", action="store_true"),
+        Option("--domain", type="string", metavar="DOMAIN",
+               help="NetBIOS domain name to use"),
+        Option("--domain-guid", type="string", metavar="GUID",
+               help="set domainguid (otherwise random)"),
+        Option("--domain-sid", type="string", metavar="SID",
+               help="set domainsid (otherwise random)"),
+        Option("--ntds-guid", type="string", metavar="GUID",
+               help="set NTDS object GUID (otherwise random)"),
+        Option("--invocationid", type="string", metavar="GUID",
+               help="set invocationid (otherwise random)"),
+        Option("--host-name", type="string", metavar="HOSTNAME",
+               help="set hostname"),
+        Option("--host-ip", type="string", metavar="IPADDRESS",
+               help="set IPv4 ipaddress"),
+        Option("--host-ip6", type="string", metavar="IP6ADDRESS",
+               help="set IPv6 ipaddress"),
+        Option("--site", type="string", metavar="SITENAME",
+               help="set site name"),
+        Option("--adminpass", type="string", metavar="PASSWORD",
+               help="choose admin password (otherwise random)"),
+        Option("--krbtgtpass", type="string", metavar="PASSWORD",
+               help="choose krbtgt password (otherwise random)"),
+        Option("--dns-backend", type="choice", metavar="NAMESERVER-BACKEND",
+               choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
+               help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
+               "BIND9_FLATFILE uses bind9 text database to store zone information, "
+               "BIND9_DLZ uses samba4 AD to store zone information, "
+               "NONE skips the DNS setup entirely (not recommended)",
+               default="SAMBA_INTERNAL"),
+        Option("--dnspass", type="string", metavar="PASSWORD",
+               help="choose dns password (otherwise random)"),
+        Option("--root", type="string", metavar="USERNAME",
+               help="choose 'root' unix username"),
+        Option("--nobody", type="string", metavar="USERNAME",
+               help="choose 'nobody' user"),
+        Option("--users", type="string", metavar="GROUPNAME",
+               help="choose 'users' group"),
+        Option("--blank", action="store_true",
+               help="do not add users or groups, just the structure"),
+        Option("--server-role", type="choice", metavar="ROLE",
+               choices=["domain controller", "dc", "member server", "member", "standalone"],
+               help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
+               default="domain controller"),
+        Option("--function-level", type="choice", metavar="FOR-FUN-LEVEL",
+               choices=["2000", "2003", "2008", "2008_R2"],
+               help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native). Default is (Windows) 2008_R2 Native.",
+               default="2008_R2"),
+        Option("--base-schema", type="choice", metavar="BASE-SCHEMA",
+               choices=["2008_R2", "2008_R2_old", "2012", "2012_R2", "2016", "2019"],
+               help="The base schema files to use. Default is (Windows) 2019.",
+               default="2019"),
+        Option("--adprep-level", type="choice", metavar="FUNCTION_LEVEL",
+               choices=["SKIP", "2008_R2", "2012", "2012_R2", "2016"],
+               help="The highest functional level to prepare for. Default is based on --base-schema",
+               default=None),
+        Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
+               help="The initial nextRid value (only needed for upgrades).  Default is 1000."),
+        Option("--partitions-only",
+               help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true"),
+        Option("--use-rfc2307", action="store_true", help="Use AD to store posix attributes (default = no)"),
+    ]
+
+    ntvfs_options = [
+        Option("--use-xattrs", type="choice", choices=["yes", "no", "auto"],
+               metavar="[yes|no|auto]",
+               help="Define if we should use the native fs capabilities or a tdb file for "
+               "storing attributes likes ntacl when --use-ntvfs is set. "
+               "auto tries to make an inteligent guess based on the user rights and system capabilities",
+               default="auto")
+    ]
+
+    takes_options.extend(common_provision_join_options)
+
+    if samba.is_ntvfs_fileserver_built():
+        takes_options.extend(common_ntvfs_options)
+        takes_options.extend(ntvfs_options)
+
+    takes_args = []
+
+    def run(self, sambaopts=None, versionopts=None,
+            interactive=None,
+            domain=None,
+            domain_guid=None,
+            domain_sid=None,
+            ntds_guid=None,
+            invocationid=None,
+            host_name=None,
+            host_ip=None,
+            host_ip6=None,
+            adminpass=None,
+            site=None,
+            krbtgtpass=None,
+            machinepass=None,
+            dns_backend=None,
+            dns_forwarder=None,
+            dnspass=None,
+            ldapadminpass=None,
+            root=None,
+            nobody=None,
+            users=None,
+            quiet=None,
+            blank=None,
+            server_role=None,
+            function_level=None,
+            adprep_level=None,
+            next_rid=None,
+            partitions_only=None,
+            targetdir=None,
+            use_xattrs="auto",
+            use_ntvfs=False,
+            use_rfc2307=None,
+            base_schema=None,
+            plaintext_secrets=False,
+            backend_store=None,
+            backend_store_size=None):
+
+        self.logger = self.get_logger(name="provision", quiet=quiet)
+
+        lp = sambaopts.get_loadparm()
+        smbconf = lp.configfile
+
+        if dns_forwarder is not None:
+            suggested_forwarder = dns_forwarder
+        else:
+            suggested_forwarder = self._get_nameserver_ip()
+            if suggested_forwarder is None:
+                suggested_forwarder = "none"
+
+        if len(self.raw_argv) == 1:
+            interactive = True
+
+        if interactive:
+            from getpass import getpass
+            import socket
+
+            def ask(prompt, default=None):
+                if default is not None:
+                    print("%s [%s]: " % (prompt, default), end=' ')
+                else:
+                    print("%s: " % (prompt,), end=' ')
+                sys.stdout.flush()
+                return sys.stdin.readline().rstrip("\n") or default
+
+            try:
+                default = socket.getfqdn().split(".", 1)[1].upper()
+            except IndexError:
+                default = None
+            realm = ask("Realm", default)
+            if realm in (None, ""):
+                raise CommandError("No realm set!")
+
+            try:
+                default = realm.split(".")[0]
+            except IndexError:
+                default = None
+            domain = ask("Domain", default)
+            if domain is None:
+                raise CommandError("No domain set!")
+
+            server_role = ask("Server Role (dc, member, standalone)", "dc")
+
+            dns_backend = ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
+            if dns_backend in (None, ''):
+                raise CommandError("No DNS backend set!")
+
+            if dns_backend == "SAMBA_INTERNAL":
+                dns_forwarder = ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder)
+                if dns_forwarder.lower() in (None, 'none'):
+                    suggested_forwarder = None
+                    dns_forwarder = None
+
+            while True:
+                adminpassplain = getpass("Administrator password: ")
+                issue = self._adminpass_issue(adminpassplain)
+                if issue:
+                    self.errf.write("%s.\n" % issue)
+                else:
+                    adminpassverify = getpass("Retype password: ")
+                    if not adminpassplain == adminpassverify:
+                        self.errf.write("Sorry, passwords do not match.\n")
+                    else:
+                        adminpass = adminpassplain
+                        break
+
+        else:
+            realm = sambaopts._lp.get('realm')
+            if realm is None:
+                raise CommandError("No realm set!")
+            if domain is None:
+                raise CommandError("No domain set!")
+
+        if adminpass:
+            issue = self._adminpass_issue(adminpass)
+            if issue:
+                raise CommandError(issue)
+        else:
+            self.logger.info("Administrator password will be set randomly!")
+
+        if function_level == "2000":
+            dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
+        elif function_level == "2003":
+            dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
+        elif function_level == "2008":
+            dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
+        elif function_level == "2008_R2":
+            dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
+
+        if adprep_level is None:
+            # Select the adprep_level default based
+            # on what the base schema premits
+            if base_schema in ["2008_R2", "2008_R2_old"]:
+                # without explicit --adprep-level=2008_R2
+                # we will skip the adprep step on
+                # provision
+                adprep_level = "SKIP"
+            elif base_schema in ["2012"]:
+                adprep_level = "2012"
+            elif base_schema in ["2012_R2"]:
+                adprep_level = "2012_R2"
+            else:
+                adprep_level = "2016"
+
+        if adprep_level == "SKIP":
+            provision_adprep_level = None
+        elif adprep_level == "2008R2":
+            provision_adprep_level = DS_DOMAIN_FUNCTION_2008_R2
+        elif adprep_level == "2012":
+            provision_adprep_level = DS_DOMAIN_FUNCTION_2012
+        elif adprep_level == "2012_R2":
+            provision_adprep_level = DS_DOMAIN_FUNCTION_2012_R2
+        elif adprep_level == "2016":
+            provision_adprep_level = DS_DOMAIN_FUNCTION_2016
+
+        if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
+            dns_forwarder = suggested_forwarder
+
+        samdb_fill = FILL_FULL
+        if blank:
+            samdb_fill = FILL_NT4SYNC
+        elif partitions_only:
+            samdb_fill = FILL_DRS
+
+        if targetdir is not None:
+            if not os.path.isdir(targetdir):
+                os.makedirs(targetdir)
+
+        eadb = True
+
+        if use_xattrs == "yes":
+            eadb = False
+        elif use_xattrs == "auto" and use_ntvfs == False:
+            eadb = False
+        elif use_ntvfs == False:
+            raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use).  "
+                               "Please re-run with --use-xattrs omitted.")
+        elif use_xattrs == "auto" and not lp.get("posix:eadb"):
+            if targetdir:
+                file = tempfile.NamedTemporaryFile(dir=os.path.abspath(targetdir))
+            else:
+                file = tempfile.NamedTemporaryFile(dir=os.path.abspath(os.path.dirname(lp.get("private dir"))))
+            try:
+                try:
+                    samba.ntacls.setntacl(lp, file.name,
+                                          "O:S-1-5-32G:S-1-5-32",
+                                          "S-1-5-32",
+                                          system_session_unix(),
+                                          "native")
+                    eadb = False
+                except Exception:
+                    self.logger.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
+            finally:
+                file.close()
+
+        if eadb:
+            self.logger.info("not using extended attributes to store ACLs and other metadata. If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
+
+        if domain_sid is not None:
+            domain_sid = security.dom_sid(domain_sid)
+
+        session = system_session()
+        if backend_store is None:
+            backend_store = get_default_backend_store()
+        try:
+            result = provision(self.logger,
+                               session, smbconf=smbconf, targetdir=targetdir,
+                               samdb_fill=samdb_fill, realm=realm, domain=domain,
+                               domainguid=domain_guid, domainsid=domain_sid,
+                               hostname=host_name,
+                               hostip=host_ip, hostip6=host_ip6,
+                               sitename=site, ntdsguid=ntds_guid,
+                               invocationid=invocationid, adminpass=adminpass,
+                               krbtgtpass=krbtgtpass, machinepass=machinepass,
+                               dns_backend=dns_backend, dns_forwarder=dns_forwarder,
+                               dnspass=dnspass, root=root, nobody=nobody,
+                               users=users,
+                               serverrole=server_role, dom_for_fun_level=dom_for_fun_level,
+                               useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
+                               use_rfc2307=use_rfc2307, skip_sysvolacl=False,
+                               base_schema=base_schema,
+                               adprep_level=provision_adprep_level,
+                               plaintext_secrets=plaintext_secrets,
+                               backend_store=backend_store,
+                               backend_store_size=backend_store_size)
+
+        except ProvisioningError as e:
+            raise CommandError("Provision failed", e)
+
+        result.report_logger(self.logger)
+
+    def _get_nameserver_ip(self):
+        """Grab the nameserver IP address from /etc/resolv.conf."""
+        from os import path
+        RESOLV_CONF = "/etc/resolv.conf"
+
+        if not path.isfile(RESOLV_CONF):
+            self.logger.warning("Failed to locate %s" % RESOLV_CONF)
+            return None
+
+        handle = None
+        try:
+            handle = open(RESOLV_CONF, 'r')
+            for line in handle:
+                if not line.startswith('nameserver'):
+                    continue
+                # we want the last non-space continuous string of the line
+                return line.strip().split()[-1]
+        finally:
+            if handle is not None:
+                handle.close()
+
+        self.logger.warning("No nameserver found in %s" % RESOLV_CONF)
+
+    def _adminpass_issue(self, adminpass):
+        """Returns error string for a bad administrator password,
+        or None if acceptable"""
+        if isinstance(adminpass, bytes):
+            adminpass = adminpass.decode('utf8')
+        if len(adminpass) < DEFAULT_MIN_PWD_LENGTH:
+            return "Administrator password does not meet the default minimum" \
+                " password length requirement (%d characters)" \
+                % DEFAULT_MIN_PWD_LENGTH
+        elif not samba.check_password_quality(adminpass):
+            return "Administrator password does not meet the default" \
+                " quality standards"
+        else:
+            return None