anchor.diff:        '';
       };
   
  -    generalConfig = {
  -      mutable = mkOption {
  -        type = bool;
  -        default = false;
  -        description = ''
  -          Make config.ini mutable (e.g. via the web interface).
  -          Not that you need to update module_path manually.
  -        '';
  -      };
  -
  -      showStacktraces = mkOption {
  -        type = bool;
  -        default = true;
  -        description = "Enable stack traces in the Web UI";
  -      };
  -
  -      showApplicationStateMessages = mkOption {
  -        type = bool;
  -        default = true;
  -        description = "Enable application state messages in the Web UI";
  -      };
  -
  -      modulePath = mkOption {
  -        type = listOf str;
  -        default = [];
  -        description = "List of additional module search paths";
  -      };
  -
  -      configBackend = mkOption {
  -        type = enum [ "ini" "db" "none" ];
  -        default = "db";
  -        description = "Where to store user preferences";
  -      };
  -
  -      configResource = mkOption {
  -        type = nullOr str;
  -        default = null;
  -        description = "Database resource where user preferences are stored (if they are stored in a database)";
  -      };
  -
  -      log = mkOption {
  -        type = enum [ "syslog" "php" "file" "none" ];
  -        default = "syslog";
  -        description = "Logging target";
  -      };
  -
  -      logLevel = mkOption {
  -        type = enum [ "ERROR" "WARNING" "INFO" "DEBUG" ];
  -        default = "ERROR";
  -        description = "Maximum logging level to emit";
  -      };
  -
  -      logApplication = mkOption {
  -        type = str;
  -        default = "icingaweb2";
  -        description = "Application name to log under (syslog and php log)";
  -      };
  -
  -      logFacility = mkOption {
  -        type = enum [ "user" "local0" "local1" "local2" "local3" "local4" "local5" "local6" "local7" ];
  -        default = "user";
  -        description = "Syslog facility to log to";
  -      };
  -
  -      logFile = mkOption {
  -        type = str;
  -        default = "/var/log/icingaweb2/icingaweb2.log";
  -        description = "File to log to";
  -      };
  -
  -      themeDefault = mkOption {
  -        type = str;
  -        default = "Icinga";
  -        description = "Name of the default theme";
  -      };
  -
  -      themeDisabled = mkOption {
  -        type = bool;
  -        default = false;
  -        description = "Disallow users to change the theme";
  -      };
  -
  -      authDefaultDomain = mkOption {
  -        type = nullOr str;
  -        default = null;
  -        description = "Domain for users logging in without a qualified domain";
  -      };
  -    };
  -
  -    mutableResources = mkOption {
  -      type = bool;
  -      default = false;
  -      description = "Make resources.ini mutable (e.g. via the web interface)";
  +    generalConfig = mkOption {
  +      type = nullOr attrs;
  +      default = null;
  +      example = {
  +        general = {
  +          showStacktraces = 1;
  +          config_resource = "icingaweb_db";
  +        };
  +        logging = {
  +          log = "syslog";
  +          level = "CRITICAL";
  +        };
  +      };
  +      description = ''
  +        config.ini contents.
  +        Will automatically be converted to a .ini file.
  +        If you don't set global.module_path, the module will take care of it.
  +
  +        If the value is null, no config.ini is created and you can
  +        modify it manually (e.g. via the web interface).
  +        Note that you need to update module_path manually.
  +      '';
       };
   
       resources = mkOption {
  -      default = {};
  -      description = "Icingaweb 2 resources to define";
  -      type = attrsOf (submodule ({ name, ... }: {
  -        options = {
  -          name = mkOption {
  -            visible = false;
  -            default = name;
  -            type = str;
  -            description = "Name of this resource";
  -          };
  -
  -          type = mkOption {
  -            type = enum [ "db" "ldap" "ssh" ];
  -            default = "db";
  -            description = "Type of this resouce";
  -          };
  -
  -          db = mkOption {
  -            type = enum [ "mysql" "pgsql" ];
  -            default = "mysql";
  -            description = "Type of this database resource";
  -          };
  -
  -          host = mkOption {
  -            type = str;
  -            description = "Host to connect to";
  -          };
  -
  -          port = mkOption {
  -            type = nullOr port;
  -            default = null;
  -            description = "Port to connect on";
  -          };
  -
  -          username = mkOption {
  -            type = str;
  -            description = "Database or SSH user or LDAP bind DN to connect with";
  -          };
  -
  -          password = mkOption {
  -            type = str;
  -            description = "Password for the database user or LDAP bind DN";
  -          };
  -
  -          dbname = mkOption {
  -            type = str;
  -            description = "Name of the database to connect to";
  -          };
  -
  -          charset = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            example = "utf8";
  -            description = "Database character set to connect with";
  -          };
  -
  -          useSSL = mkOption {
  -            type = nullOr bool;
  -            default = false;
  -            description = "Whether to connect to the database using SSL";
  -          };
  -
  -          sslCert = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "The file path to the SSL certificate. Only available for the mysql database.";
  -          };
  -
  -          sslKey = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "The file path to the SSL key. Only available for the mysql database.";
  -          };
  -
  -          sslCA = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "The file path to the SSL certificate authority. Only available for the mysql database.";
  -          };
  -
  -          sslCApath = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "The file path to the directory that contains the trusted SSL CA certificates in PEM format. Only available for the mysql database.";
  -          };
  -
  -          sslCipher = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "A list of one or more permissible ciphers to use for SSL encryption, in a format understood by OpenSSL. Only available for the mysql database.";
  -          };
  -
  -          rootDN = mkOption {
  -            type = str;
  -            description = "Root object of the LDAP tree";
  -          };
  -
  -          ldapEncryption = mkOption {
  -            type = enum [ "none" "starttls" "ldaps" ];
  -            default = "none";
  -            description = "LDAP encryption to use";
  -          };
  -
  -          ldapTimeout = mkOption {
  -            type = ints.positive;
  -            default = 5;
  -            description = "Connection timeout for every LDAP connection";
  -          };
  -
  -          sshPrivateKey = mkOption {
  -            type = str;
  -            description = "The path to the private key of the user";
  -          };
  -        };
  -      }));
  -    };
  -
  -    mutableAuthConfig = mkOption {
  -      type = bool;
  -      default = true;
  -      description = "Make authentication.ini mutable (e.g. via the web interface)";
  +      type = nullOr attrs;
  +      default = null;
  +      example = {
  +        icingaweb_db = {
  +          type = "db";
  +          db = "mysql";
  +          host = "localhost";
  +          username = "icingaweb2";
  +          password = "icingaweb2";
  +          dbname = "icingaweb2";
  +        };
  +      };
  +      description = ''
  +        resources.ini contents.
  +        Will automatically be converted to a .ini file.
  +
  +        If the value is null, no resources.ini is created and you can
  +        modify it manually (e.g. via the web interface).
  +        Note that if you set passwords here, they will go into the nix store.
  +      '';
       };
   
       authentications = mkOption {
  -      default = {};
  -      description = "Icingaweb 2 authentications to define";
  -      type = attrsOf (submodule ({ name, ... }: {
  -        options = {
  -          name = mkOption {
  -            visible = false;
  -            default = name;
  -            type = str;
  -            description = "Name of this authentication";
  -          };
  -
  -          backend = mkOption {
  -            type = enum [ "external" "ldap" "msldap" "db" ];
  -            default = "db";
  -            description = "The type of this authentication backend";
  -          };
  -
  -          domain = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "Domain for domain-aware authentication";
  -          };
  -
  -          externalStripRegex = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "Regular expression to strip off specific user name parts";
  -          };
  -
  -          resource = mkOption {
  -            type = str;
  -            description = "Name of the database/LDAP resource";
  -          };
  -
  -          ldapUserClass = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "LDAP user class";
  -          };
  -
  -          ldapUserNameAttr = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "LDAP attribute which contains the username";
  -          };
  -
  -          ldapFilter = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "LDAP search filter";
  -          };
  -        };
  -      }));
  -    };
  -
  -    mutableGroupsConfig = mkOption {
  -      type = bool;
  -      default = true;
  -      description = "Make groups.ini mutable (e.g. via the web interface)";
  +      type = nullOr attrs;
  +      default = null;
  +      example = {
  +        icingaweb = {
  +          backend = "db";
  +          resource = "icingaweb_db";
  +        };
  +      };
  +      description = ''
  +        authentication.ini contents.
  +        Will automatically be converted to a .ini file.
  +
  +        If the value is null, no authentication.ini is created and you can
  +        modify it manually (e.g. via the web interface).
  +      '';
       };
   
       groupBackends = mkOption {
  -      default = {};
  -      description = "Icingaweb 2 group backends to define";
  -      type = attrsOf (submodule ({ name, ... }: {
  -        options = {
  -          name = mkOption {
  -            visible = false;
  -            default = name;
  -            type = str;
  -            description = "Name of this group backend";
  -          };
  -
  -          backend = mkOption {
  -            type = enum [ "ldap" "msldap" "db" ];
  -            default = "db";
  -            description = "The type of this group backend";
  -          };
  -
  -          resource = mkOption {
  -            type = str;
  -            description = "Name of the database/LDAP resource";
  -          };
  -
  -          ldapUserClass = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "LDAP user class";
  -          };
  -
  -          ldapUserNameAttr = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "LDAP attribute which contains the username";
  -          };
  -
  -          ldapGroupClass = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "LDAP group class";
  -          };
  -
  -          ldapGroupNameAttr = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "LDAP attribute which contains the groupname";
  -          };
  -
  -          ldapGroupFilter = mkOption {
  -            type = nullOr str;
  -            default = null;
  -            description = "LDAP group search filter";
  -          };
  -
  -          ldapNestedSearch = mkOption {
  -            type = bool;
  -            default = false;
  -            description = "Enable nested group search in Active Directory based on the user";
  -          };
  -        };
  -      }));
  -    };
  -
  -    mutableRolesConfig = mkOption {
  -      type = bool;
  -      default = true;
  -      description = "Make roles.ini mutable (e.g. via the web interface)";
  +      type = nullOr attrs;
  +      default = null;
  +      example = {
  +        icingaweb = {
  +          backend = "db";
  +          resource = "icingaweb_db";
  +        };
  +      };
  +      description = ''
  +        groups.ini contents.
  +        Will automatically be converted to a .ini file.
  +
  +        If the value is null, no groups.ini is created and you can
  +        modify it manually (e.g. via the web interface).
  +      '';
       };
   
       roles = mkOption {
  -      default = {};
  -      description = "Icingaweb 2 roles to define";
  -      type = attrsOf (submodule ({ name, ... }: {
  -        options = {
  -          name = mkOption {
  -            visible = false;
  -            default = name;
  -            type = str;
  -            description = "Name of this role";
  -          };
  -
  -          users = mkOption {
  -            type = listOf str;
  -            default = [];
  -            description = "List of users that are assigned to the role";
  -          };
  -
  -          groups = mkOption {
  -            type = listOf str;
  -            default = [];
  -            description = "List of groups that are assigned to the role";
  -          };
  -
  -          permissions = mkOption {
  -            type = listOf str;
  -            default = [];
  -            example = [ "application/share/navigation" "config/*" ];
  -            description = "The permissions to grant";
  -          };
  -
  -          extraAssignments = mkOption {
  -            type = attrsOf (listOf str);
  -            default = {};
  -            example = { "monitoring/blacklist/properties" = [ "sla" "customer"]; };
  -            description = "Additional assignments of this role";
  -          };
  -        };
  -      }));
  +      type = nullOr attrs;
  +      default = null;
  +      example = {
  +        Administrators = {
  +          users = "admin";
  +          permissions = "*";
  +        };
  +      };
  +      description = ''
  +        roles.ini contents.
  +        Will automatically be converted to a .ini file.
  +
  +        If the value is null, no roles.ini is created and you can
  +        modify it manually (e.g. via the web interface).
  +      '';
       };
     };
   
     config = mkIf cfg.enable {
       services.phpfpm.poolConfigs = mkIf (cfg.pool == "${poolName}") {
  -      "${poolName}" = {
  -        listen = phpfpmSocketName;
  -        phpOptions = ''
  -          extension = ${pkgs.phpPackages.imagick}/lib/php/extensions/imagick.so
  -          date.timezone = "${cfg.timezone}"
  -        '';
  -        extraConfig = ''
  -          listen.owner = nginx
  -          listen.group = nginx
  -          listen.mode = 0600
  -          user = icingaweb2
  -          pm = dynamic
  -          pm.max_children = 75
  -          pm.start_servers = 2
  -          pm.min_spare_servers = 2
  -          pm.max_spare_servers = 10
  -        '';
  -      };
  -    };
  +      "${poolName}" = ''
  +        listen = "${phpfpmSocketName}"
  +        listen.owner = nginx
  +        listen.group = nginx
  +        listen.mode = 0600
  +        user = icingaweb2
  +        pm = dynamic
  +        pm.max_children = 75
  +        pm.start_servers = 2
  +        pm.min_spare_servers = 2
  +        pm.max_spare_servers = 10
  +      '';
  +    };
  +
  +    services.phpfpm.phpOptions = mkIf (cfg.pool == "${poolName}")
  +      ''
  +        extension = ${pkgs.phpPackages.imagick}/lib/php/extensions/imagick.so
  +        date.timezone = "${cfg.timezone}"
  +      '';
   
       systemd.services."phpfpm-${poolName}".serviceConfig.ReadWritePaths = [ "/etc/icingaweb2" ];
   
  
positive.code:       default = {};
        example = literalExample ''
          {
            "snow" = pkgs.icingaweb2Modules.theme-snow;
          }
        '';
        description = ''
  
negative.code:       // doModule "test"
        // doModule "translation"
        # Configs
        // optionalAttrs (cfg.generalConfig != null) { "icingaweb2/config.ini".text = generators.toINI {} (defaultConfig // cfg.generalConfig); }
        // optionalAttrs (cfg.resources != null) { "icingaweb2/resources.ini".text = generators.toINI {} cfg.resources; }
        // optionalAttrs (cfg.authentications != null) { "icingaweb2/authentication.ini".text = generators.toINI {} cfg.authentications; }
        // optionalAttrs (cfg.groupBackends != null) { "icingaweb2/groups.ini".text = generators.toINI {} cfg.groupBackends; }
        // optionalAttrs (cfg.roles != null) { "icingaweb2/roles.ini".text = generators.toINI {} cfg.roles; };
  
      # User and group
      users.groups.icingaweb2 = {};
  