diff --git a/modules/puppet-fm/PKG_INFO b/modules/puppet-fm/PKG_INFO
new file mode 100644
index 000000000..285a5ae04
--- /dev/null
+++ b/modules/puppet-fm/PKG_INFO
@@ -0,0 +1,2 @@
+Name: puppet-fm
+Version: 1.0.0
diff --git a/modules/puppet-fm/centos/build_srpm.data b/modules/puppet-fm/centos/build_srpm.data
new file mode 100644
index 000000000..29c4710a7
--- /dev/null
+++ b/modules/puppet-fm/centos/build_srpm.data
@@ -0,0 +1,3 @@
+SRC_DIR="src"
+COPY_LIST="$SRC_DIR/LICENSE"
+TIS_PATCH_VER=1
diff --git a/modules/puppet-fm/centos/puppet-fm.spec b/modules/puppet-fm/centos/puppet-fm.spec
new file mode 100644
index 000000000..47322e77a
--- /dev/null
+++ b/modules/puppet-fm/centos/puppet-fm.spec
@@ -0,0 +1,34 @@
+%global module_dir fm
+
+Name:           puppet-%{module_dir}
+Version:        1.0.0
+Release:        %{tis_patch_ver}%{?_tis_dist}
+Summary:        Puppet FM module
+License:        Apache-2.0
+Packager:       Wind River <info@windriver.com>
+
+URL:            unknown
+
+Source0:        %{name}-%{version}.tar.gz
+Source1:        LICENSE
+
+BuildArch:      noarch
+
+BuildRequires: python2-devel
+
+%description
+A puppet module for Fault Management
+
+%prep
+%autosetup -c %{module_dir}
+
+#
+# The src for this puppet module needs to be staged to puppet/modules
+#
+%install
+install -d -m 0755 %{buildroot}%{_datadir}/puppet/modules/%{module_dir}
+cp -R %{name}-%{version}/%{module_dir} %{buildroot}%{_datadir}/puppet/modules
+
+%files
+%license  %{name}-%{version}/LICENSE
+%{_datadir}/puppet/modules/%{module_dir}
diff --git a/modules/puppet-fm/src/LICENSE b/modules/puppet-fm/src/LICENSE
new file mode 100644
index 000000000..8d968b6cb
--- /dev/null
+++ b/modules/puppet-fm/src/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!) The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/modules/puppet-fm/src/fm/lib/puppet/provider/fm_api_paste_ini/ini_setting.rb b/modules/puppet-fm/src/fm/lib/puppet/provider/fm_api_paste_ini/ini_setting.rb
new file mode 100644
index 000000000..7fd50aeb3
--- /dev/null
+++ b/modules/puppet-fm/src/fm/lib/puppet/provider/fm_api_paste_ini/ini_setting.rb
@@ -0,0 +1,27 @@
+Puppet::Type.type(:fm_api_paste_ini).provide(
+  :ini_setting,
+  :parent => Puppet::Type.type(:ini_setting).provider(:ruby)
+) do
+
+  def section
+    resource[:name].split('/', 2).first
+  end
+
+  def setting
+    resource[:name].split('/', 2).last
+  end
+
+  def separator
+    '='
+  end
+
+  def self.file_path
+    '/etc/fm/api-paste.ini'
+  end
+
+  # this needs to be removed. This has been replaced with the class method
+  def file_path
+     self.class.file_path
+  end
+ 
+end
diff --git a/modules/puppet-fm/src/fm/lib/puppet/provider/fm_config/ini_setting.rb b/modules/puppet-fm/src/fm/lib/puppet/provider/fm_config/ini_setting.rb
new file mode 100644
index 000000000..f00b4851d
--- /dev/null
+++ b/modules/puppet-fm/src/fm/lib/puppet/provider/fm_config/ini_setting.rb
@@ -0,0 +1,10 @@
+Puppet::Type.type(:fm_config).provide(
+  :ini_setting,
+  :parent => Puppet::Type.type(:openstack_config).provider(:ini_setting)
+) do
+
+  def self.file_path
+    '/etc/fm/fm.conf'
+  end
+
+end
diff --git a/modules/puppet-fm/src/fm/lib/puppet/type/fm_api_paste_ini.rb b/modules/puppet-fm/src/fm/lib/puppet/type/fm_api_paste_ini.rb
new file mode 100644
index 000000000..0df981e0c
--- /dev/null
+++ b/modules/puppet-fm/src/fm/lib/puppet/type/fm_api_paste_ini.rb
@@ -0,0 +1,43 @@
+Puppet::Type.newtype(:fm_api_paste_ini) do
+
+  ensurable
+
+  newparam(:name, :namevar => true) do
+    desc 'Section/setting name to manage from /etc/fm/api_paste.ini'
+    newvalues(/\S+\/\S+/)
+  end
+
+  newproperty(:value) do
+    desc 'The value of the setting to be defined.'
+    munge do |value|
+      value = value.to_s.strip
+      value.capitalize! if value =~ /^(true|false)$/i
+      value
+    end
+
+    def is_to_s( currentvalue )
+      if resource.secret?
+        return '[old secret redacted]'
+      else
+        return currentvalue
+      end
+    end
+
+    def should_to_s( newvalue )
+      if resource.secret?
+        return '[new secret redacted]'
+      else
+        return newvalue
+      end
+    end
+  end
+
+  newparam(:secret, :boolean => true) do
+    desc 'Whether to hide the value from Puppet logs. Defaults to `false`.'
+
+    newvalues(:true, :false)
+
+    defaultto false
+  end
+end
+
diff --git a/modules/puppet-fm/src/fm/lib/puppet/type/fm_config.rb b/modules/puppet-fm/src/fm/lib/puppet/type/fm_config.rb
new file mode 100644
index 000000000..e4ad3a832
--- /dev/null
+++ b/modules/puppet-fm/src/fm/lib/puppet/type/fm_config.rb
@@ -0,0 +1,51 @@
+Puppet::Type.newtype(:fm_config) do
+
+  ensurable
+
+  newparam(:name, :namevar => true) do
+    desc 'Section/setting name to manage from fm.conf'
+    newvalues(/\S+\/\S+/)
+  end
+
+  newproperty(:value) do
+    desc 'The value of the setting to be defined.'
+    munge do |value|
+      value = value.to_s.strip
+      value.capitalize! if value =~ /^(true|false)$/i
+      value
+    end
+    newvalues(/^[\S ]*$/)
+
+    def is_to_s( currentvalue )
+      if resource.secret?
+        return '[old secret redacted]'
+      else
+        return currentvalue
+      end
+    end
+
+    def should_to_s( newvalue )
+      if resource.secret?
+        return '[new secret redacted]'
+      else
+        return newvalue
+      end
+    end
+  end
+
+  newparam(:secret, :boolean => true) do
+    desc 'Whether to hide the value from Puppet logs. Defaults to `false`.'
+    newvalues(:true, :false)
+    defaultto false
+  end
+
+  newparam(:ensure_absent_val) do
+    desc 'A value that is specified as the value property will behave as if ensure => absent was specified'
+    defaultto('<SERVICE DEFAULT>')
+  end
+
+  autorequire(:package) do
+    'fm-rest-api'
+  end
+
+end
diff --git a/modules/puppet-fm/src/fm/manifests/api.pp b/modules/puppet-fm/src/fm/manifests/api.pp
new file mode 100644
index 000000000..f5dcacf81
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/api.pp
@@ -0,0 +1,109 @@
+# Installs & configure the fm api service
+#
+# == Parameters
+#
+# [*enabled*]
+#   (optional) Should the service be enabled.
+#   Defaults to true
+#
+# [*manage_service*]
+#   (optional) Whether the service should be managed by Puppet.
+#   Defaults to true.
+#
+# [*host*]
+#   (optional) The fm api bind address.
+#   Defaults to 0.0.0.0
+#
+# [*port*]
+#   (optional) The fm api port.
+#   Defaults to 18002
+#
+# [*package_ensure*]
+#   (optional) ensure state for package.
+#   Defaults to 'present'
+#
+# [*service_name*]
+#   (optional) Name of the service that will be providing the
+#   server functionality of fm-api.
+#
+# [*sync_db*]
+#   (optional) Run gnocchi-upgrade db sync on api nodes after installing the package.
+#   Defaults to false
+#
+# [*auth_strategy*]
+#   (optional) Type of authentication to be used.
+#   Defaults to 'keystone'
+#
+# [*enable_proxy_headers_parsing*]
+#   (Optional) Enable paste middleware to handle SSL requests through
+#   HTTPProxyToWSGI middleware.
+#   Defaults to $::os_service_default.
+#
+# [*paste_config*]
+#   (Optional) Configuration file for WSGI definition of API
+#   Defaults to $::os_service_default.
+#
+class fm::api (
+  $manage_service                 = true,
+  $enabled                        = true,
+  $package_ensure                 = 'present',
+  $host                           = '0.0.0.0',
+  $port                           = '18002',
+  $workers                        = 1,
+  $service_name                   = $::fm::params::api_service,
+  $sync_db                        = false,
+  $auth_strategy                  = 'keystone',
+  $enable_proxy_headers_parsing   = $::os_service_default,
+  $paste_config                   = '/etc/fm/api-paste.ini',
+) inherits fm::params {
+
+
+  include ::fm::deps
+  include ::fm::params
+
+  if $auth_strategy == 'keystone' {
+    include ::fm::keystone::authtoken
+  }
+
+  package { 'fm-api':
+    ensure => $package_ensure,
+    name   => $::fm::params::api_package,
+    tag    => 'fm-package',
+  }
+
+  if $manage_service {
+    if $enabled {
+      $service_ensure = 'running'
+    } else {
+      $service_ensure = 'stopped'
+    }
+  }
+
+  if $sync_db {
+    include ::fm::db::sync
+  }
+
+  if $service_name == $::fm::params::api_service {
+    service { 'fm-api':
+      ensure     => $service_ensure,
+      name       => $::fm::params::api_service,
+      enable     => $enabled,
+      hasstatus  => true,
+      hasrestart => true,
+      tag        => 'fm-service',
+    }
+  } else {
+    fail("Invalid service_name. fm-api for running as a standalone service")
+  }
+
+  fm_config {
+    'api/bind_host':                  value => $host;
+    'api/bind_port':                  value => $port;
+    'api/api_workers':                value => $workers;
+    'api/api_paste_config':           value => $paste_config;
+  }
+
+  oslo::middleware { 'fm_config':
+    enable_proxy_headers_parsing => $enable_proxy_headers_parsing,
+  }
+}
diff --git a/modules/puppet-fm/src/fm/manifests/client.pp b/modules/puppet-fm/src/fm/manifests/client.pp
new file mode 100644
index 000000000..1267012be
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/client.pp
@@ -0,0 +1,22 @@
+#
+# Installs the fm python client.
+#
+# == parameters
+#  [*ensure*]
+#   (optional) Ensure state of the package.
+#   Defaults to 'present'.
+#
+class fm::client (
+  $ensure = 'present'
+) {
+
+  include ::fm::deps
+  include ::fm::params
+
+  package { 'fmclient':
+    ensure => $ensure,
+    name   => $::fm::params::client_package,
+    tag    => 'fmclient',
+  }
+}
+
diff --git a/modules/puppet-fm/src/fm/manifests/db.pp b/modules/puppet-fm/src/fm/manifests/db.pp
new file mode 100644
index 000000000..f82d7c1df
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/db.pp
@@ -0,0 +1,78 @@
+# == Class: fm::db
+#
+#  Configure the fm database
+#
+# === Parameters
+#
+# [*database_db_max_retries*]
+#   (optional) Maximum retries in case of connection error or deadlock error
+#   before error is raised. Set to -1 to specify an infinite retry count.
+#   Defaults to $::os_service_default
+#
+# [*database_connection*]
+#   Url used to connect to database.
+#   (Optional) Defaults to "sqlite:////var/lib/fm/fm.sqlite".
+#
+# [*database_idle_timeout*]
+#   Timeout when db connections should be reaped.
+#   (Optional) Defaults to $::os_service_default.
+#
+# [*database_min_pool_size*]
+#   Minimum number of SQL connections to keep open in a pool.
+#   (Optional) Defaults to $::os_service_default.
+#
+# [*database_max_pool_size*]
+#   Maximum number of SQL connections to keep open in a pool.
+#   (Optional) Defaults to $::os_service_default.
+#
+# [*database_max_retries*]
+#   Maximum number of database connection retries during startup.
+#   Setting -1 implies an infinite retry count.
+#   (Optional) Defaults to $::os_service_default.
+#
+# [*database_retry_interval*]
+#   Interval between retries of opening a database connection.
+#   (Optional) Defaults to $::os_service_default.
+#
+# [*database_max_overflow*]
+#   If set, use this value for max_overflow with sqlalchemy.
+#   (Optional) Defaults to $::os_service_default.
+#
+class fm::db (
+  $database_db_max_retries = $::os_service_default,
+  $database_connection     = 'sqlite:////var/lib/fm/fm.sqlite',
+  $database_idle_timeout   = $::os_service_default,
+  $database_min_pool_size  = $::os_service_default,
+  $database_max_pool_size  = $::os_service_default,
+  $database_max_retries    = $::os_service_default,
+  $database_retry_interval = $::os_service_default,
+  $database_max_overflow   = $::os_service_default,
+) {
+
+  include ::fm::deps
+
+  $database_connection_real = pick($::fm::database_connection, $database_connection)
+  $database_idle_timeout_real = pick($::fm::database_idle_timeout, $database_idle_timeout)
+  $database_min_pool_size_real = pick($::fm::database_min_pool_size, $database_min_pool_size)
+  $database_max_pool_size_real = pick($::fm::database_max_pool_size, $database_max_pool_size)
+  $database_max_retries_real = pick($::fm::database_max_retries, $database_max_retries)
+  $database_retry_interval_real = pick($::fm::database_retry_interval, $database_retry_interval)
+  $database_max_overflow_real = pick($::fm::database_max_overflow, $database_max_overflow)
+
+  oslo::db { 'fm_config':
+    db_max_retries => $database_db_max_retries,
+    connection     => $database_connection_real,
+    idle_timeout   => $database_idle_timeout_real,
+    min_pool_size  => $database_min_pool_size_real,
+    max_pool_size  => $database_max_pool_size_real,
+    max_retries    => $database_max_retries_real,
+    retry_interval => $database_retry_interval_real,
+    max_overflow   => $database_max_overflow_real,
+  }
+
+  # set up the connection string for FM Manager
+  $sql_connection = regsubst($database_connection_real,'^postgresql+psycopg2:','postgresql:')
+  fm_config {
+    'DEFAULT/sql_connection':    value => $sql_connection;
+  }
+}
diff --git a/modules/puppet-fm/src/fm/manifests/db/mysql.pp b/modules/puppet-fm/src/fm/manifests/db/mysql.pp
new file mode 100644
index 000000000..cbe034975
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/db/mysql.pp
@@ -0,0 +1,75 @@
+# The fm::db::mysql class implements mysql backend for fm
+#
+# This class can be used to create tables, users and grant
+# privileges for a mysql fm database.
+#
+# == parameters
+#
+# [*password*]
+#   (Mandatory) Password to connect to the database.
+#   Defaults to 'false'.
+#
+# [*dbname*]
+#   (Optional) Name of the database.
+#   Defaults to 'fm'.
+#
+# [*user*]
+#   (Optional) User to connect to the database.
+#   Defaults to 'fm'.
+#
+# [*host*]
+#   (Optional) The default source host user is allowed to connect from.
+#   Defaults to '127.0.0.1'
+#
+# [*allowed_hosts*]
+#   (Optional) Other hosts the user is allowed to connect from.
+#   Defaults to 'undef'.
+#
+# [*charset*]
+#   (Optional) The database charset.
+#   Defaults to 'utf8'
+#
+# [*collate*]
+#   (Optional) The database collate.
+#   Only used with mysql modules >= 2.2.
+#   Defaults to 'utf8_general_ci'
+#
+# == Dependencies
+#   Class['mysql::server']
+#
+# == Examples
+#
+# == Authors
+#
+# == Copyright
+#
+class fm::db::mysql(
+  $password,
+  $dbname        = 'fm',
+  $user          = 'fm',
+  $host          = '127.0.0.1',
+  $charset       = 'utf8',
+  $collate       = 'utf8_general_ci',
+  $allowed_hosts = undef
+) {
+
+  #include ::fm::deps
+
+  validate_string($password)
+
+  ::openstacklib::db::mysql { 'fm':
+    user          => $user,
+    password_hash => mysql_password($password),
+    dbname        => $dbname,
+    host          => $host,
+    charset       => $charset,
+    collate       => $collate,
+    allowed_hosts => $allowed_hosts,
+  }
+
+  Anchor['fm::db::begin']
+  ~> Class['fm::db::mysql']
+  ~> Anchor['fm::db::end']
+
+}
+
diff --git a/modules/puppet-fm/src/fm/manifests/db/postgresql.pp b/modules/puppet-fm/src/fm/manifests/db/postgresql.pp
new file mode 100644
index 000000000..26c272620
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/db/postgresql.pp
@@ -0,0 +1,57 @@
+# == Class: fm::db::postgresql
+#
+# Class that configures postgresql for fm
+# Requires the Puppetlabs postgresql module.
+#
+# === Parameters
+#
+# [*password*]
+#   (Required) Password to connect to the database.
+#
+# [*dbname*]
+#   (Optional) Name of the database.
+#   Defaults to 'fm'.
+#
+# [*user*]
+#   (Optional) User to connect to the database.
+#   Defaults to 'fm'.
+#
+#  [*encoding*]
+#    (Optional) The charset to use for the database.
+#    Default to undef.
+#
+#  [*privileges*]
+#    (Optional) Privileges given to the database user.
+#    Default to 'ALL'
+#
+# == Dependencies
+#
+# == Examples
+#
+# == Authors
+#
+# == Copyright
+#
+class fm::db::postgresql(
+  $password,
+  $dbname     = 'fm',
+  $user       = 'fm',
+  $encoding   = undef,
+  $privileges = 'ALL',
+) {
+
+  include ::fm::deps
+
+  ::openstacklib::db::postgresql { 'fm':
+    password_hash => postgresql_password($user, $password),
+    dbname        => $dbname,
+    user          => $user,
+    encoding      => $encoding,
+    privileges    => $privileges,
+  }
+
+  Anchor['fm::db::begin']
+  ~> Class['fm::db::postgresql']
+  ~> Anchor['fm::db::end']
+
+}
diff --git a/modules/puppet-fm/src/fm/manifests/db/sync.pp b/modules/puppet-fm/src/fm/manifests/db/sync.pp
new file mode 100644
index 000000000..e2fdbcd4b
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/db/sync.pp
@@ -0,0 +1,30 @@
+#
+# Class to execute "fm-dbsync"
+#
+# [*user*]
+#   (optional) User to run dbsync command.
+#   Defaults to 'fm'
+#
+class fm::db::sync (
+  $user = 'fm',
+){
+
+  include ::fm::deps
+
+  exec { 'fm-db-sync':
+    command     => 'fm-dbsync --config-file /etc/fm/fm.conf',
+    path        => '/usr/bin',
+    refreshonly => true,
+    user        => $user,
+    try_sleep   => 5,
+    tries       => 10,
+    logoutput   => on_failure,
+    subscribe   => [
+      Anchor['fm::install::end'],
+      Anchor['fm::config::end'],
+      Anchor['fm::dbsync::begin']
+    ],
+    notify      => Anchor['fm::dbsync::end'],
+  }
+
+}
diff --git a/modules/puppet-fm/src/fm/manifests/deps.pp b/modules/puppet-fm/src/fm/manifests/deps.pp
new file mode 100644
index 000000000..ea7af7f13
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/deps.pp
@@ -0,0 +1,40 @@
+# == Class: fm::deps
+#
+#  FM anchors and dependency management
+#
+class fm::deps {
+  # Setup anchors for install, config and service phases of the module.  These
+  # anchors allow external modules to hook the begin and end of any of these
+  # phases.  Package or service management can also be replaced by ensuring the
+  # package is absent or turning off service management and having the
+  # replacement depend on the appropriate anchors.  When applicable, end tags
+  # should be notified so that subscribers can determine if installation,
+  # config or service state changed and act on that if needed.
+  anchor { 'fm::install::begin': }
+  -> Package<| tag == 'fm-package'|>
+  ~> anchor { 'fm::install::end': }
+  -> anchor { 'fm::config::begin': }
+  -> Fm_config<||>
+  ~> anchor { 'fm::config::end': }
+  -> anchor { 'fm::db::begin': }
+  -> anchor { 'fm::db::end': }
+  ~> anchor { 'fm::dbsync::begin': }
+  -> anchor { 'fm::dbsync::end': }
+  ~> anchor { 'fm::service::begin': }
+  ~> Service<| tag == 'fm-service' |>
+  ~> anchor { 'fm::service::end': }
+
+  # api paste ini config should occur in the config block also.
+  Anchor['fm::config::begin']
+  -> Fm_api_paste_ini<||>
+  ~> Anchor['fm::config::end']
+
+  # all db settings should be applied and all packages should be installed
+  # before dbsync starts
+  Oslo::Db<||> -> Anchor['fm::dbsync::begin']
+
+  # Installation or config changes will always restart services.
+  Anchor['fm::install::end'] ~> Anchor['fm::service::begin']
+  Anchor['fm::config::end']  ~> Anchor['fm::service::begin']
+}
+
diff --git a/modules/puppet-fm/src/fm/manifests/init.pp b/modules/puppet-fm/src/fm/manifests/init.pp
new file mode 100644
index 000000000..b2958c933
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/init.pp
@@ -0,0 +1,116 @@
+# == Class: fm
+#
+# Full description of class fm here.
+#
+# === Parameters
+#
+# [*package_ensure*]
+#   (optional) The state of fm packages
+#   Defaults to 'present'
+#
+# [*log_dir*]
+#   (optional) Directory where logs should be stored.
+#   If set to boolean false or the $::os_service_default, it will not log to
+#   any directory.
+#   Defaults to undef.
+#
+# [*debug*]
+#   (optional) Set log output to debug output.
+#   Defaults to undef
+#
+# [*use_syslog*]
+#   (optional) Use syslog for logging
+#   Defaults to undef
+#
+# [*use_stderr*]
+#   (optional) Use stderr for logging
+#   Defaults to undef
+#
+# [*log_facility*]
+#   (optional) Syslog facility to receive log lines.
+#   Defaults to undef
+#
+# [*database_connection*]
+#   (optional) Connection url for the fm database.
+#   Defaults to undef.
+#
+# [*database_max_retries*]
+#   (optional) Maximum database connection retries during startup.
+#   Defaults to undef.
+#
+# [*database_idle_timeout*]
+#   (optional) Timeout before idle database connections are reaped.
+#   Defaults to undef.
+#
+# [*database_retry_interval*]
+#   (optional) Interval between retries of opening a database connection.
+#   Defaults to undef.
+#
+# [*database_min_pool_size*]
+#   (optional) Minimum number of SQL connections to keep open in a pool.
+#   Defaults to undef.
+#
+# [*database_max_pool_size*]
+#   (optional) Maximum number of SQL connections to keep open in a pool.
+#   Defaults to undef.
+#
+# [*database_max_overflow*]
+#   (optional) If set, use this value for max_overflow with sqlalchemy.
+#   Defaults to: undef.
+#
+class fm (
+  $package_ensure                     = 'present',
+  $debug                              = undef,
+  $use_syslog                         = undef,
+  $use_stderr                         = undef,
+  $log_facility                       = undef,
+  $log_dir                            = undef,
+  $database_connection                = undef,
+  $database_idle_timeout              = undef,
+  $database_min_pool_size             = undef,
+  $database_max_pool_size             = undef,
+  $database_max_retries               = undef,
+  $database_retry_interval            = undef,
+  $database_max_overflow              = undef,
+  $event_log_max_size                 = 4000,
+  $system_name                        = undef,
+  $region_name                        = undef,
+  $trap_destinations                  = undef,
+  $sysinv_catalog_info                = undef,
+) inherits fm::params {
+
+  include ::fm::deps
+  include ::fm::logging
+
+  # set up the connection string for FM Manager, remove psycopg2 if it exists
+  $sql_connection = regsubst($database_connection,'^postgresql+psycopg2:','postgresql:')
+  fm_config {
+    'DEFAULT/sql_connection':       value => $sql_connection, secret => true;
+    'DEFAULT/event_log_max_size':   value => $event_log_max_size;
+    'DEFAULT/system_name':          value => $system_name;
+    'DEFAULT/region_name':          value => $region_name;
+    'DEFAULT/trap_destinations':    value => $trap_destinations;
+  }
+
+  # Automatically add psycopg2 driver to postgresql (only does this if it is missing)
+  $real_connection = regsubst($database_connection,'^postgresql:','postgresql+psycopg2:')
+  fm_config {
+    'database/connection':    value => $real_connection, secret => true;
+    'database/idle_timeout':  value => $database_idle_timeout;
+    'database/max_pool_size': value => $database_max_pool_size;
+    'database/max_overflow':  value => $database_max_overflow;
+  }
+
+  fm_config {
+    'sysinv/catalog_info':    value => $sysinv_catalog_info;
+    'sysinv/os_region_name':  value => $region_name;
+  }
+
+  fm_api_paste_ini {
+    'pipeline:fm-api/pipeline': value => 'request_id authtoken api_v1';
+    'filter:request_id/paste.filter_factory': value => 'oslo_middleware:RequestId.factory';
+    'filter:authtoken/acl_public_routes': value => '/, /v1';
+    'filter:authtoken/paste.filter_factory': value => 'fm.api.middleware.auth_token:AuthTokenMiddleware.factory';
+    'app:api_v1/paste.app_factory': value => 'fm.api.app:app_factory';
+  }
+}
diff --git a/modules/puppet-fm/src/fm/manifests/keystone/auth.pp b/modules/puppet-fm/src/fm/manifests/keystone/auth.pp
new file mode 100644
index 000000000..ddbf31970
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/keystone/auth.pp
@@ -0,0 +1,87 @@
+# == Class: fm::keystone::auth
+#
+# Configures fault management user, service and endpoint in Keystone.
+#
+# === Parameters
+#
+# [*password*]
+#   (required) Password for fm user.
+#
+# [*auth_name*]
+#   Username for fm service. Defaults to 'fm'.
+#
+# [*email*]
+#   Email for fm user. Defaults to 'fm@localhost'.
+#
+# [*tenant*]
+#   Tenant for fm user. Defaults to 'services'.
+#
+# [*configure_endpoint*]
+#   Should fm endpoint be configured? Defaults to 'true'.
+#
+# [*configure_user*]
+#   (Optional) Should the service user be configured?
+#   Defaults to 'true'.
+#
+# [*configure_user_role*]
+#   (Optional) Should the admin role be configured for the service user?
+#   Defaults to 'true'.
+#
+# [*service_type*]
+#   Type of service. Defaults to 'faultmanagement'.
+#
+# [*region*]
+#   Region for endpoint. Defaults to 'RegionOne'.
+#
+# [*service_name*]
+#   (optional) Name of the service.
+#   Defaults to 'fm'.
+#
+# [*public_url*]
+#   (optional) The endpoint's public url. (Defaults to 'http://127.0.0.1:18002')
+#   This url should *not* contain any trailing '/'.
+#
+# [*admin_url*]
+#   (optional) The endpoint's admin url. (Defaults to 'http://127.0.0.1:18002')
+#   This url should *not* contain any trailing '/'.
+#
+# [*internal_url*]
+#   (optional) The endpoint's internal url. (Defaults to 'http://127.0.0.1:18002')
+#   This url should *not* contain any trailing '/'.
+#
+class fm::keystone::auth (
+  $password,
+  $auth_name           = 'fm',
+  $email               = 'fm@localhost',
+  $tenant              = 'services',
+  $configure_endpoint  = true,
+  $configure_user      = true,
+  $configure_user_role = true,
+  $service_name        = 'fm',
+  $service_type        = 'faultmanagement',
+  $region              = 'RegionOne',
+  $public_url          = 'http://127.0.0.1:18002',
+  $internal_url        = 'http://127.0.0.1:18002',
+  $admin_url           = 'http://127.0.0.1:18002',
+) {
+
+  include ::fm::deps
+
+  keystone::resource::service_identity { 'fm':
+    configure_user      => $configure_user,
+    configure_user_role => $configure_user_role,
+    configure_endpoint  => $configure_endpoint,
+    service_name        => $service_name,
+    service_type        => $service_type,
+    service_description => 'Fault Management Service',
+    region              => $region,
+    auth_name           => $auth_name,
+    password            => $password,
+    email               => $email,
+    tenant              => $tenant,
+    public_url          => $public_url,
+    internal_url        => $internal_url,
+    admin_url           => $admin_url,
+  }
+
+}
diff --git a/modules/puppet-fm/src/fm/manifests/keystone/authtoken.pp b/modules/puppet-fm/src/fm/manifests/keystone/authtoken.pp
new file mode 100644
index 000000000..2b2755249
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/keystone/authtoken.pp
@@ -0,0 +1,243 @@
+# class: fm::keystone::authtoken
+#
+# Configure the keystone_authtoken section in the configuration file
+#
+# === Parameters
+#
+# [*username*]
+#   (Optional) The name of the service user
+#   Defaults to 'fm'
+#
+# [*password*]
+#   (Optional) Password to create for the service user
+#   Defaults to $::os_service_default
+#
+# [*auth_url*]
+#   (Optional) The URL to use for authentication.
+#   Defaults to 'http://localhost:35357'
+#
+# [*project_name*]
+#   (Optional) Service project name
+#   Defaults to 'services'
+#
+# [*user_domain_name*]
+#   (Optional) Name of domain for $username
+#   Defaults to 'Default'
+#
+# [*project_domain_name*]
+#   (Optional) Name of domain for $project_name
+#   Defaults to 'Default'
+#
+# [*insecure*]
+#   (Optional) If true, explicitly allow TLS without checking server cert
+#   against any certificate authorities.  WARNING: not recommended.  Use with
+#   caution.
+#   Defaults to $::os_service_default
+#
+# [*auth_section*]
+#   (Optional) Config Section from which to load plugin specific options
+#   Defaults to $::os_service_default.
+#
+# [*auth_type*]
+#   (Optional) Authentication type to load
+#   Defaults to 'password'
+#
+# [*auth_uri*]
+#   (Optional) Complete public Identity API endpoint.
+#   Defaults to 'http://localhost:5000'
+#
+# [*auth_version*]
+#   (Optional) API version of the admin Identity API endpoint.
+#   Defaults to $::os_service_default.
+#
+# [*cache*]
+#   (Optional) Env key for the swift cache.
+#   Defaults to $::os_service_default.
+#
+# [*cafile*]
+#   (Optional) A PEM encoded Certificate Authority to use when verifying HTTPs
+#   connections.
+#   Defaults to $::os_service_default.
+#
+# [*certfile*]
+#   (Optional) Required if identity server requires client certificate
+#   Defaults to $::os_service_default.
+#
+# [*check_revocations_for_cached*]
+#   (Optional) If true, the revocation list will be checked for cached tokens.
+#   This requires that PKI tokens are configured on the identity server.
+#   boolean value.
+#   Defaults to $::os_service_default.
+#
+# [*delay_auth_decision*]
+#   (Optional) Do not handle authorization requests within the middleware, but
+#   delegate the authorization decision to downstream WSGI components. Boolean
+#   value
+#   Defaults to $::os_service_default.
+#
+# [*enforce_token_bind*]
+#   (Optional) Used to control the use and type of token binding. Can be set
+#   to: "disabled" to not check token binding. "permissive" (default) to
+#   validate binding information if the bind type is of a form known to the
+#   server and ignore it if not. "strict" like "permissive" but if the bind
+#   type is unknown the token will be rejected. "required" any form of token
+#   binding is needed to be allowed. Finally the name of a binding method that
+#   must be present in tokens. String value.
+#   Defaults to $::os_service_default.
+#
+# [*hash_algorithms*]
+#   (Optional) Hash algorithms to use for hashing PKI tokens. This may be a
+#   single algorithm or multiple. The algorithms are those supported by Python
+#   standard hashlib.new(). The hashes will be tried in the order given, so put
+#   the preferred one first for performance. The result of the first hash will
+#  be stored in the cache. This will typically be set to multiple values only
+#   while migrating from a less secure algorithm to a more secure one. Once all
+#   the old tokens are expired this option should be set to a single value for
+#   better performance. List value.
+#   Defaults to $::os_service_default.
+#
+# [*http_connect_timeout*]
+#   (Optional) Request timeout value for communicating with Identity API
+#   server.
+#   Defaults to $::os_service_default.
+#
+# [*http_request_max_retries*]
+#   (Optional) How many times are we trying to reconnect when communicating
+#   with Identity API Server. Integer value
+#   Defaults to $::os_service_default.
+#
+# [*include_service_catalog*]
+#   (Optional) Indicate whether to set the X-Service-Catalog header. If False,
+#   middleware will not ask for service catalog on token validation and will
+#   not set the X-Service-Catalog header. Boolean value.
+#   Defaults to $::os_service_default.
+#
+# [*keyfile*]
+#   (Optional) Required if identity server requires client certificate
+#   Defaults to $::os_service_default.
+#
+# [*memcache_pool_conn_get_timeout*]
+#   (Optional) Number of seconds that an operation will wait to get a memcached
+#   client connection from the pool. Integer value
+#   Defaults to $::os_service_default.
+#
+# [*memcache_pool_dead_retry*]
+#   (Optional) Number of seconds memcached server is considered dead before it
+#   is tried again. Integer value
+#   Defaults to $::os_service_default.
+#
+# [*memcache_pool_maxsize*]
+#   (Optional) Maximum total number of open connections to every memcached
+#   server. Integer value
+#   Defaults to $::os_service_default.
+#
+# [*memcache_pool_socket_timeout*]
+#   (Optional) Number of seconds a connection to memcached is held unused in
+#   the pool before it is closed. Integer value
+#   Defaults to $::os_service_default.
+#
+# [*memcache_pool_unused_timeout*]
+#   (Optional) Number of seconds a connection to memcached is held unused in
+#   the pool before it is closed. Integer value
+#   Defaults to $::os_service_default.
+#
+# [*memcache_secret_key*]
+#   (Optional, mandatory if memcache_security_strategy is defined) This string
+#   is used for key derivation.
+#   Defaults to $::os_service_default.
+#
+# [*memcache_security_strategy*]
+#   (Optional) If defined, indicate whether token data should be authenticated
+#   or authenticated and encrypted. If MAC, token data is authenticated (with
+#   HMAC) in the cache. If ENCRYPT, token data is encrypted and authenticated in the
+#   cache. If the value is not one of these options or empty, auth_token will
+#   raise an exception on initialization.
+#   Defaults to $::os_service_default.
+#
+# [*memcache_use_advanced_pool*]
+#   (Optional)  Use the advanced (eventlet safe) memcached client pool. The
+#   advanced pool will only work under python 2.x Boolean value
+#   Defaults to $::os_service_default.
+#
+# [*memcached_servers*]
+#   (Optional) Optionally specify a list of memcached server(s) to use for
+#   caching. If left undefined, tokens will instead be cached in-process.
+#   Defaults to $::os_service_default.
+#
+# [*manage_memcache_package*]
+#  (Optional) Whether to install the python-memcache package.
+#  Defaults to false.
+#
+# [*region_name*]
+#   (Optional) The region in which the identity server can be found.
+#   Defaults to $::os_service_default.
+#
+# [*revocation_cache_time*]
+#   (Optional) Determines the frequency at which the list of revoked tokens is
+#   retrieved from the Identity service (in seconds). A high number of
+#   revocation events combined with a low cache duration may significantly
+#   reduce performance. Only valid for PKI tokens. Integer value
+#   Defaults to $::os_service_default.
+#
+# [*token_cache_time*]
+#   (Optional) In order to prevent excessive effort spent validating tokens,
+#   the middleware caches previously-seen tokens for a configurable duration
+#   (in seconds). Set to -1 to disable caching completely. Integer value
+#   Defaults to $::os_service_default.
+#
+class fm::keystone::authtoken(
+  $username                       = 'fm',
+  $password                       = $::os_service_default,
+  $auth_url                       = 'http://localhost:35357',
+  $project_name                   = 'services',
+  $user_domain_name               = 'Default',
+  $project_domain_name            = 'Default',
+  $insecure                       = $::os_service_default,
+  $auth_section                   = $::os_service_default,
+  $auth_type                      = 'password',
+  $auth_uri                       = 'http://localhost:5000',
+  $auth_version                   = $::os_service_default,
+  $cache                          = $::os_service_default,
+  $cafile                         = $::os_service_default,
+  $certfile                       = $::os_service_default,
+  $check_revocations_for_cached   = $::os_service_default,
+  $delay_auth_decision            = $::os_service_default,
+  $enforce_token_bind             = $::os_service_default,
+  $hash_algorithms                = $::os_service_default,
+  $http_connect_timeout           = $::os_service_default,
+  $http_request_max_retries       = $::os_service_default,
+  $include_service_catalog        = $::os_service_default,
+  $keyfile                        = $::os_service_default,
+  $memcache_pool_conn_get_timeout = $::os_service_default,
+  $memcache_pool_dead_retry       = $::os_service_default,
+  $memcache_pool_maxsize          = $::os_service_default,
+  $memcache_pool_socket_timeout   = $::os_service_default,
+  $memcache_pool_unused_timeout   = $::os_service_default,
+  $memcache_secret_key            = $::os_service_default,
+  $memcache_security_strategy     = $::os_service_default,
+  $memcache_use_advanced_pool     = $::os_service_default,
+  $memcached_servers              = $::os_service_default,
+  $manage_memcache_package        = false,
+  $region_name                    = $::os_service_default,
+  $revocation_cache_time          = $::os_service_default,
+  $token_cache_time               = $::os_service_default,
+) {
+
+  include ::fm::deps
+
+  if is_service_default($password) {
+    fail('Please set password for FM service user')
+  }
+
+  keystone::resource::authtoken { 'fm_config':
+    username                       => $username,
+    password                       => $password,
+    project_name                   => $project_name,
+    auth_url                       => $auth_url,
+    auth_uri                       => $auth_uri,
+    auth_type                      => $auth_type,
+    user_domain_name               => $user_domain_name,
+    project_domain_name            => $project_domain_name,
+    region_name                    => $region_name,
+  }
+}
diff --git a/modules/puppet-fm/src/fm/manifests/logging.pp b/modules/puppet-fm/src/fm/manifests/logging.pp
new file mode 100644
index 000000000..62a2f4a33
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/logging.pp
@@ -0,0 +1,134 @@
+# Class fm::logging
+#
+#  fm logging configuration
+#
+# == parameters
+#
+#  [*debug*]
+#    (Optional) Should the daemons log debug messages
+#    Defaults to $::os_service_default
+#
+#  [*use_syslog*]
+#    (Optional) Use syslog for logging.
+#    Defaults to $::os_service_default
+#
+#  [*use_stderr*]
+#    (optional) Use stderr for logging
+#    Defaults to $::os_service_default
+#
+#  [*log_facility*]
+#    (Optional) Syslog facility to receive log lines.
+#    Defaults to $::os_service_default
+#
+#  [*log_dir*]
+#    (optional) Directory where logs should be stored.
+#    If set to boolean false or the $::os_service_default, it will not log to
+#    any directory.
+#    Defaults to '/var/log/fm'.
+#
+#  [*logging_context_format_string*]
+#    (optional) Format string to use for log messages with context.
+#    Defaults to $::os_service_default
+#    Example: '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s\
+#              [%(request_id)s %(user_identity)s] %(instance)s%(message)s'
+#
+#  [*logging_default_format_string*]
+#    (optional) Format string to use for log messages without context.
+#    Defaults to $::os_service_default
+#    Example: '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s\
+#              [-] %(instance)s%(message)s'
+#
+#  [*logging_debug_format_suffix*]
+#    (optional) Formatted data to append to log format when level is DEBUG.
+#    Defaults to $::os_service_default
+#    Example: '%(funcName)s %(pathname)s:%(lineno)d'
+#
+#  [*logging_exception_prefix*]
+#    (optional) Prefix each line of exception output with this format.
+#    Defaults to $::os_service_default
+#    Example: '%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s'
+#
+#  [*log_config_append*]
+#    The name of an additional logging configuration file.
+#    Defaults to $::os_service_default
+#    See https://docs.python.org/2/howto/logging.html
+#
+#  [*default_log_levels*]
+#    (optional) Hash of logger (keys) and level (values) pairs.
+#    Defaults to $::os_service_default
+#    Example:
+#      { 'amqp' => 'WARN', 'amqplib' => 'WARN', 'boto' => 'WARN',
+#        'sqlalchemy' => 'WARN', 'suds' => 'INFO', 'iso8601' => 'WARN',
+#        'requests.packages.urllib3.connectionpool' => 'WARN' }
+#
+#  [*publish_errors*]
+#    (optional) Publish error events (boolean value).
+#    Defaults to $::os_service_default
+#
+#  [*fatal_deprecations*]
+#    (optional) Make deprecations fatal (boolean value)
+#    Defaults to $::os_service_default
+#
+#  [*instance_format*]
+#    (optional) If an instance is passed with the log message, format it
+#               like this (string value).
+#    Defaults to $::os_service_default
+#    Example: '[instance: %(uuid)s] '
+#
+#  [*instance_uuid_format*]
+#    (optional) If an instance UUID is passed with the log message, format
+#               it like this (string value).
+#    Defaults to $::os_service_default
+#    Example: instance_uuid_format='[instance: %(uuid)s] '
+#
+#  [*log_date_format*]
+#    (optional) Format string for %%(asctime)s in log records.
+#    Defaults to $::os_service_default
+#    Example: 'Y-%m-%d %H:%M:%S'
+#
+class fm::logging(
+  $use_syslog                    = $::os_service_default,
+  $use_stderr                    = $::os_service_default,
+  $log_facility                  = $::os_service_default,
+  $log_dir                       = '/var/log/fm',
+  $debug                         = $::os_service_default,
+  $logging_context_format_string = $::os_service_default,
+  $logging_default_format_string = $::os_service_default,
+  $logging_debug_format_suffix   = $::os_service_default,
+  $logging_exception_prefix      = $::os_service_default,
+  $log_config_append             = $::os_service_default,
+  $default_log_levels            = $::os_service_default,
+  $publish_errors                = $::os_service_default,
+  $fatal_deprecations            = $::os_service_default,
+  $instance_format               = $::os_service_default,
+  $instance_uuid_format          = $::os_service_default,
+  $log_date_format               = $::os_service_default,
+) {
+
+  include ::fm::deps
+
+  $use_syslog_real   = pick($::fm::use_syslog,$use_syslog)
+  $use_stderr_real   = pick($::fm::use_stderr,$use_stderr)
+  $log_facility_real = pick($::fm::log_facility,$log_facility)
+  $log_dir_real      = pick($::fm::log_dir,$log_dir)
+  $debug_real        = pick($::fm::debug,$debug)
+
+  oslo::log { 'fm_config':
+    debug                         => $debug_real,
+    use_syslog                    => $use_syslog_real,
+    use_stderr                    => $use_stderr_real,
+    log_dir                       => $log_dir_real,
+    syslog_log_facility           => $log_facility_real,
+    logging_context_format_string => $logging_context_format_string,
+    logging_default_format_string => $logging_default_format_string,
+    logging_debug_format_suffix   => $logging_debug_format_suffix,
+    logging_exception_prefix      => $logging_exception_prefix,
+    log_config_append             => $log_config_append,
+    default_log_levels            => $default_log_levels,
+    publish_errors                => $publish_errors,
+    fatal_deprecations            => $fatal_deprecations,
+    log_date_format               => $log_date_format,
+    instance_format               => $instance_format,
+    instance_uuid_format          => $instance_uuid_format,
+  }
+}
diff --git a/modules/puppet-fm/src/fm/manifests/params.pp b/modules/puppet-fm/src/fm/manifests/params.pp
new file mode 100644
index 000000000..d96ec2821
--- /dev/null
+++ b/modules/puppet-fm/src/fm/manifests/params.pp
@@ -0,0 +1,20 @@
+class fm::params {
+
+  case $::osfamily {
+    'RedHat': {
+      $client_package = 'python-fmclient'
+      $api_package    = 'fm-rest-api'
+      $api_service    = 'fm-api'
+    }
+    'Debian': {
+      $client_package = 'python-fmclient'
+      $api_package    = 'fm-rest-api'
+      $api_service    = 'fm-api'
+    }
+    default: {
+      fail("Unsupported osfamily: ${::osfamily} operatingsystem")
+    }
+
+  } # Case $::osfamily
+
+}
diff --git a/modules/puppet-nfv/src/nfv/manifests/nfvi.pp b/modules/puppet-nfv/src/nfv/manifests/nfvi.pp
index 96315c70f..c99ccae43 100644
--- a/modules/puppet-nfv/src/nfv/manifests/nfvi.pp
+++ b/modules/puppet-nfv/src/nfv/manifests/nfvi.pp
@@ -52,6 +52,10 @@ class nfv::nfvi (
   $patching_service_name         = 'patching',
   $patching_service_type         = 'patching',
   $patching_endpoint_type        = 'admin',
+  $fm_region_name                = 'RegionOne',
+  $fm_service_name               = 'fm',
+  $fm_service_type               = 'faultmanagement',
+  $fm_endpoint_type              = 'admin',
   $rabbit_host                   = '127.0.0.1',
   $rabbit_port                   =  5672,
   $rabbit_userid                 = 'guest',
@@ -133,6 +137,11 @@ class nfv::nfvi (
     'patching/service_type': value => $patching_service_type;
     'patching/endpoint_type': value => $patching_endpoint_type;
 
+    'fm/region_name': value => $fm_region_name;
+    'fm/service_name': value => $fm_service_name;
+    'fm/service_type': value => $fm_service_type;
+    'fm/endpoint_type': value => $fm_endpoint_type;
+
     /* AMQP */
     'amqp/host': value => $rabbit_host;
     'amqp/port': value => $rabbit_port;
diff --git a/puppet-manifests/centos/puppet-manifests.spec b/puppet-manifests/centos/puppet-manifests.spec
index d3f690476..7717aa38d 100644
--- a/puppet-manifests/centos/puppet-manifests.spec
+++ b/puppet-manifests/centos/puppet-manifests.spec
@@ -21,6 +21,7 @@ Requires: puppet-patching
 Requires: puppet-sysinv
 Requires: puppet-sshd
 Requires: puppet-smapi
+Requires: puppet-fm
 
 # Openstack puppet modules
 Requires: puppet-aodh
diff --git a/puppet-manifests/src/hieradata/controller.yaml b/puppet-manifests/src/hieradata/controller.yaml
index c06f32f37..e54cd853c 100644
--- a/puppet-manifests/src/hieradata/controller.yaml
+++ b/puppet-manifests/src/hieradata/controller.yaml
@@ -524,3 +524,13 @@ dcorch::debug: false
 dcmanager::use_syslog: true
 dcmanager::log_facility: 'local2'
 dcmanager::debug: false
+
+#FM
+fm::use_syslog: true
+fm::log_facility: 'local2'
+fm::api::enable_proxy_headers_parsing: true
+fm::db::sync::user: 'root'
+fm::database_idle_timeout: 60
+fm::database_max_overflow: 20
+fm::database_max_pool_size: 1
+
diff --git a/puppet-manifests/src/manifests/controller.pp b/puppet-manifests/src/manifests/controller.pp
index 89e733c41..ec0bfe1df 100644
--- a/puppet-manifests/src/manifests/controller.pp
+++ b/puppet-manifests/src/manifests/controller.pp
@@ -62,6 +62,9 @@ include ::platform::influxdb
 include ::platform::influxdb::logrotate
 include ::platform::collectd
 
+include ::platform::fm
+include ::platform::fm::api
+
 include ::openstack::client
 include ::openstack::keystone
 include ::openstack::keystone::api
diff --git a/puppet-manifests/src/modules/openstack/manifests/keystone.pp b/puppet-manifests/src/modules/openstack/manifests/keystone.pp
index 500e29b14..e2c4d3b13 100644
--- a/puppet-manifests/src/modules/openstack/manifests/keystone.pp
+++ b/puppet-manifests/src/modules/openstack/manifests/keystone.pp
@@ -325,6 +325,7 @@ class openstack::keystone::endpoint::runtime {
     include ::sysinv::keystone::auth
     include ::patching::keystone::auth
     include ::nfv::keystone::auth
+    include ::fm::keystone::auth
 
     include ::openstack::aodh::params
     if $::openstack::aodh::params::service_enabled {
diff --git a/puppet-manifests/src/modules/platform/manifests/fm.pp b/puppet-manifests/src/modules/platform/manifests/fm.pp
new file mode 100644
index 000000000..75052d476
--- /dev/null
+++ b/puppet-manifests/src/modules/platform/manifests/fm.pp
@@ -0,0 +1,101 @@
+class platform::fm::params (
+  $api_port = 18002,
+  $api_host = undef,
+  $region_name = undef,
+  $system_name = undef,
+  $service_create = false,
+  $service_enabled = true,
+  $trap_destinations = [],
+  $sysinv_catalog_info = 'platform:sysinv:internalURL',
+) { }
+
+
+class platform::fm::config
+  inherits ::platform::fm::params {
+
+  $trap_dest_str = join($trap_destinations,',')
+  class { '::fm':
+    region_name => $region_name,
+    system_name => $system_name,
+    trap_destinations => $trap_dest_str,
+    sysinv_catalog_info => $sysinv_catalog_info,
+  }
+}
+
+class platform::fm
+  inherits ::platform::fm::params {
+
+  include ::fm::client
+  include ::fm::keystone::authtoken
+  include ::platform::fm::config
+
+  include ::platform::params
+  if $::platform::params::init_database {
+    include ::fm::db::postgresql
+  }
+}
+
+class platform::fm::firewall
+  inherits ::platform::fm::params {
+
+  platform::firewall::rule { 'fm-api':
+    service_name => 'fm',
+    ports        => $api_port,
+  }
+}
+
+class platform::fm::haproxy
+  inherits ::platform::fm::params {
+
+  include ::platform::haproxy::params
+
+  platform::haproxy::proxy { 'fm-api-internal':
+    server_name => 's-fm-api-internal',
+    public_ip_address => $::platform::haproxy::params::private_ip_address,
+    public_port => $api_port,
+    private_ip_address => $api_host,
+    private_port => $api_port,
+    public_api => false,
+  }
+
+  platform::haproxy::proxy { 'fm-api-public':
+    server_name => 's-fm-api-public',
+    public_port => $api_port,
+    private_port => $api_port,
+  }
+}
+
+class platform::fm::api
+  inherits ::platform::fm::params {
+
+  include ::platform::params
+
+  if $service_enabled {
+    if ($::platform::fm::service_create and
+        $::platform::params::init_keystone) {
+      include ::fm::keystone::auth
+    }
+
+    include ::platform::params
+
+    class { '::fm::api':
+      host      => $api_host,
+      workers   => $::platform::params::eng_workers,
+      sync_db   => $::platform::params::init_database,
+    }
+
+    include ::platform::fm::firewall
+    include ::platform::fm::haproxy
+  }
+}
+
+class platform::fm::runtime {
+
+  require ::platform::fm::config
+
+  exec { 'notify-fm-mgr':
+    command => "/usr/bin/pkill -HUP fmManager",
+    onlyif => "pgrep fmManager"
+  }
+}
+
diff --git a/puppet-manifests/src/modules/platform/manifests/haproxy.pp b/puppet-manifests/src/modules/platform/manifests/haproxy.pp
index 0430a566c..7191f800c 100644
--- a/puppet-manifests/src/modules/platform/manifests/haproxy.pp
+++ b/puppet-manifests/src/modules/platform/manifests/haproxy.pp
@@ -19,6 +19,7 @@ define platform::haproxy::proxy (
   $client_timeout = undef,
   $x_forwarded_proto = true,
   $enable_https = undef,
+  $public_api = true,
 ) {
   include ::platform::haproxy::params
   
@@ -29,7 +30,7 @@ define platform::haproxy::proxy (
   }  
 
   if $x_forwarded_proto {
-    if $https_enabled {
+    if $https_enabled and $public_api {
         $ssl_option = 'ssl crt /etc/ssl/private/server-cert.pem'
         $proto = 'X-Forwarded-Proto:\ https'
         # The value of max-age matches lighttpd.conf, and should be
@@ -135,6 +136,7 @@ class platform::haproxy::runtime {
   include ::platform::sysinv::haproxy
   include ::platform::nfv::haproxy
   include ::platform::ceph::haproxy
+  include ::platform::fm::haproxy
   if $::platform::params::distributed_cloud_role =='systemcontroller' {
     include ::platform::dcmanager::haproxy
     include ::platform::dcorch::haproxy
diff --git a/puppet-manifests/src/modules/platform/manifests/postgresql.pp b/puppet-manifests/src/modules/platform/manifests/postgresql.pp
index e283c117f..60a0d9e79 100644
--- a/puppet-manifests/src/modules/platform/manifests/postgresql.pp
+++ b/puppet-manifests/src/modules/platform/manifests/postgresql.pp
@@ -211,6 +211,7 @@ class platform::postgresql::upgrade
   include ::sysinv::db::postgresql
   include ::keystone::db::postgresql
   include ::ironic::db::postgresql
+  include ::fm::db::postgresql
 
 }
 
diff --git a/puppet-manifests/src/modules/platform/manifests/sysinv.pp b/puppet-manifests/src/modules/platform/manifests/sysinv.pp
index c50b3f0f8..0b2afef96 100644
--- a/puppet-manifests/src/modules/platform/manifests/sysinv.pp
+++ b/puppet-manifests/src/modules/platform/manifests/sysinv.pp
@@ -60,12 +60,6 @@ class platform::sysinv
       'sysinv %(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s';
   }
 
-  $sysinv_db_connection = $::sysinv::database_connection
-  file { "/etc/fm.conf":
-    ensure => 'present',
-    content => template('platform/fm.conf.erb'),
-  }
-
   if str2bool($::is_initial_config_primary) {
     $software_version = $::platform::params::software_version
 
diff --git a/puppet-manifests/src/modules/platform/templates/fm.conf.erb b/puppet-manifests/src/modules/platform/templates/fm.conf.erb
deleted file mode 100644
index f6f418da4..000000000
--- a/puppet-manifests/src/modules/platform/templates/fm.conf.erb
+++ /dev/null
@@ -1,9 +0,0 @@
-###################################################
-#
-# fm.conf
-#
-# The configuration file for the fmManager process.
-#
-###################################################
-event_log_max_size=4000
-sql_connection=<%= @sysinv_db_connection %>
diff --git a/puppet-manifests/src/modules/platform/templates/fm.snmp.conf.erb b/puppet-manifests/src/modules/platform/templates/fm.snmp.conf.erb
new file mode 100644
index 000000000..6822b9aff
--- /dev/null
+++ b/puppet-manifests/src/modules/platform/templates/fm.snmp.conf.erb
@@ -0,0 +1,6 @@
+[snmp]
+<%- @trap_destinations.each do |destination| -%>
+trap2sink=<%= destination %>
+<%- end -%>
+
+