diff --git a/lib/puppet/provider/keystone_implied_role/openstack.rb b/lib/puppet/provider/keystone_implied_role/openstack.rb
new file mode 100644
index 000000000..36001ab1c
--- /dev/null
+++ b/lib/puppet/provider/keystone_implied_role/openstack.rb
@@ -0,0 +1,77 @@
+require File.join(File.dirname(__FILE__), '..','..','..', 'puppet/provider/keystone')
+require File.join(File.dirname(__FILE__), '..','..','..', 'puppet_x/keystone/composite_namevar')
+
+Puppet::Type.type(:keystone_implied_role).provide(
+  :openstack,
+  :parent => Puppet::Provider::Keystone
+) do
+
+  desc 'Provider for keystone implied roles.'
+
+  @credentials = Puppet::Provider::Openstack::CredentialsV3.new
+
+  include PuppetX::Keystone::CompositeNamevar::Helpers
+
+  def initialize(value={})
+    super(value)
+  end
+
+  def self.do_not_manage
+    @do_not_manage
+  end
+
+  def self.do_not_manage=(value)
+    @do_not_manage = value
+  end
+
+  def create
+    if self.class.do_not_manage
+      fail("Not managing Keystone_implied_role[#{@resource[:role]}@#{@resource[:implied_role]}] due to earlier Keystone API failures.")
+    end
+    self.class.system_request('implied role', 'create', [@resource[:role], '--implied-role', @resource[:implied_role]])
+    @property_hash[:ensure] = :present
+    @property_hash[:role] = @resource[:role]
+    @property_hash[:implied_role] = @resource[:implied_role]
+  end
+
+  def destroy
+    if self.class.do_not_manage
+      fail("Not managing Keystone_implied_role[#{@resource[:role]}@#{@resource[:implied_role]}] due to earlier Keystone API failures.")
+    end
+    self.class.system_request('implied role', 'delete', [@resource[:role], '--implied-role', @resource[:implied_role]])
+    @property_hash.clear
+  end
+
+  def exists?
+    @property_hash[:ensure] == :present
+  end
+
+  mk_resource_methods
+
+  [
+    :role,
+    :implied_role,
+  ].each do |attr|
+    define_method(attr.to_s + "=") do |value|
+      fail("Property #{attr.to_s} does not support being updated")
+    end
+  end
+
+  def self.instances
+    self.do_not_manage = true
+    list = system_request('implied role', 'list')
+    reallist = list.collect do |role|
+      new(
+        :ensure       => :present,
+        :role         => role[:prior_role_name].downcase,
+        :implied_role => role[:implied_role_name].downcase,
+      )
+    end
+    self.do_not_manage = false
+    reallist
+  end
+
+  def self.prefetch(resources)
+    prefetch_composite(resources)
+  end
+end
diff --git a/lib/puppet/provider/keystone_role/openstack.rb b/lib/puppet/provider/keystone_role/openstack.rb
index 7feca1792..41251af6e 100644
--- a/lib/puppet/provider/keystone_role/openstack.rb
+++ b/lib/puppet/provider/keystone_role/openstack.rb
@@ -11,7 +11,6 @@ Puppet::Type.type(:keystone_role).provide(
 
   def initialize(value={})
     super(value)
-    @property_flush = {}
   end
 
   def self.do_not_manage
@@ -63,7 +62,7 @@ Puppet::Type.type(:keystone_role).provide(
   def self.prefetch(resources)
     roles = instances
     resources.keys.each do |name|
-       if provider = roles.find{ |role| role.name == name.downcase }
+      if provider = roles.find{ |role| role.name == name.downcase }
         resources[name].provider = provider
       end
     end
diff --git a/lib/puppet/type/keystone_implied_role.rb b/lib/puppet/type/keystone_implied_role.rb
new file mode 100644
index 000000000..318bde30a
--- /dev/null
+++ b/lib/puppet/type/keystone_implied_role.rb
@@ -0,0 +1,44 @@
+# LP#1408531
+File.expand_path('../..', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir) }
+File.expand_path('../../../../openstacklib/lib', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir) }
+
+Puppet::Type.newtype(:keystone_implied_role) do
+
+  desc <<-EOT
+    This is currently used to model the creation of
+    keystone implied roles.
+  EOT
+
+  ensurable
+
+  newparam(:role) do
+    isnamevar
+    newvalues(/\S+/)
+  end
+
+  newparam(:implied_role) do
+    isnamevar
+    newvalues(/\S+/)
+  end
+
+  # we should not do anything until the keystone service is started
+  autorequire(:anchor) do
+    ['keystone::service::end']
+  end
+
+  autorequire(:keystone_role) do
+    [self[:role], self[:implied_role]]
+  end
+
+  def self.title_patterns
+    [
+      [
+        /^(\S+)@(\S+)$/,
+        [
+          [:role],
+          [:implied_role],
+        ]
+      ],
+    ]
+  end
+end
diff --git a/manifests/bootstrap.pp b/manifests/bootstrap.pp
index 11f614b2d..5c4af3960 100644
--- a/manifests/bootstrap.pp
+++ b/manifests/bootstrap.pp
@@ -116,7 +116,17 @@ class keystone::bootstrap (
     # use the below resources to make sure the current resources are
     # correct so if some value was updated we set that.
 
-    ensure_resource('keystone_role', $role_name, {
+    ensure_resource('keystone_role',
+      [$role_name, 'manager', 'member', 'reader', 'service'], {
+      'ensure' => 'present',
+    })
+
+    ensure_resource('keystone_implied_role',
+      [
+        "${role_name}@manager",
+        'manager@member',
+        'member@reader',
+      ], {
       'ensure' => 'present',
     })
 
diff --git a/releasenotes/notes/implied-role-894ec2595b94aed7.yaml b/releasenotes/notes/implied-role-894ec2595b94aed7.yaml
new file mode 100644
index 000000000..ec68b7f81
--- /dev/null
+++ b/releasenotes/notes/implied-role-894ec2595b94aed7.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    The new ``keystone_implied_role`` resource type has been added.
diff --git a/spec/classes/keystone_bootstrap_spec.rb b/spec/classes/keystone_bootstrap_spec.rb
index abf1c0123..f19c4882e 100644
--- a/spec/classes/keystone_bootstrap_spec.rb
+++ b/spec/classes/keystone_bootstrap_spec.rb
@@ -33,6 +33,14 @@ describe 'keystone::bootstrap' do
       )}
 
       it { is_expected.to contain_keystone_role('admin').with_ensure('present') }
+      it { is_expected.to contain_keystone_role('manager').with_ensure('present') }
+      it { is_expected.to contain_keystone_role('member').with_ensure('present') }
+      it { is_expected.to contain_keystone_role('reader').with_ensure('present') }
+      it { is_expected.to contain_keystone_role('service').with_ensure('present') }
+
+      it { is_expected.to contain_keystone_implied_role('admin@manager').with_ensure('present') }
+      it { is_expected.to contain_keystone_implied_role('manager@member').with_ensure('present') }
+      it { is_expected.to contain_keystone_implied_role('member@reader').with_ensure('present') }
 
       it { is_expected.to contain_keystone_user('admin').with(
         :ensure   => 'present',
@@ -137,6 +145,14 @@ describe 'keystone::bootstrap' do
       )}
 
       it { is_expected.to contain_keystone_role('adminrole').with_ensure('present') }
+      it { is_expected.to contain_keystone_role('manager').with_ensure('present') }
+      it { is_expected.to contain_keystone_role('member').with_ensure('present') }
+      it { is_expected.to contain_keystone_role('reader').with_ensure('present') }
+      it { is_expected.to contain_keystone_role('service').with_ensure('present') }
+
+      it { is_expected.to contain_keystone_implied_role('adminrole@manager').with_ensure('present') }
+      it { is_expected.to contain_keystone_implied_role('manager@member').with_ensure('present') }
+      it { is_expected.to contain_keystone_implied_role('member@reader').with_ensure('present') }
 
       it { is_expected.to contain_keystone_user('user').with(
         :ensure   => 'present',
diff --git a/spec/unit/provider/keystone_implied_role/openstack_spec.rb b/spec/unit/provider/keystone_implied_role/openstack_spec.rb
new file mode 100644
index 000000000..41696470f
--- /dev/null
+++ b/spec/unit/provider/keystone_implied_role/openstack_spec.rb
@@ -0,0 +1,89 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet/provider/keystone_implied_role/openstack'
+
+provider_class = Puppet::Type.type(:keystone_implied_role).provider(:openstack)
+
+describe provider_class do
+
+  let(:set_env) do
+    ENV['OS_USERNAME']     = 'test'
+    ENV['OS_PASSWORD']     = 'abc123'
+    ENV['OS_SYSTEM_SCOPE'] = 'all'
+    ENV['OS_AUTH_URL']     = 'http://127.0.0.1:5000'
+  end
+
+  before(:each) do
+    set_env
+  end
+
+  describe 'when creating an implied role' do
+    let(:implied_role_attrs) do
+      {
+        :title  => 'foo@bar',
+        :ensure => 'present',
+      }
+    end
+
+    let(:resource) do
+      Puppet::Type::Keystone_implied_role.new(implied_role_attrs)
+    end
+
+    let(:provider) do
+      provider_class.new(resource)
+    end
+
+    describe '#create' do
+      it 'creates an implied role' do
+        expect(provider.class).to receive(:openstack)
+          .with('implied role', 'create', '--format', 'shell',
+            ['foo', '--implied-role', 'bar'])
+          .and_return('implies="54d545116da64b68bb75244130ba51b2"
+prior_role="3553ab20c4dd497a867dd822913b6d30"
+')
+        provider.create
+        expect(provider.exists?).to be_truthy
+      end
+    end
+
+    describe '#destroy' do
+      it 'destroys an implied role' do
+        expect(provider.class).to receive(:openstack)
+          .with('implied role', 'delete',
+            ['foo', '--implied-role', 'bar'])
+        provider.destroy
+        expect(provider.exists?).to be_falsey
+      end
+
+    end
+
+    describe '#exists' do
+      context 'when implied role does not exist' do
+        subject(:response) do
+          response = provider.exists?
+        end
+        it { is_expected.to be_falsey }
+      end
+    end
+
+    describe '#instances' do
+      it 'finds every role' do
+        expect(provider.class).to receive(:openstack)
+          .with('implied role', 'list', '--quiet', '--format', 'csv', [])
+          .and_return('"Prior Role ID","Prior Role Name","Implied Role ID","Implied Role Name"
+"1d7f28c7d646463dba7b0c6c5851c59b","admin","da9eac51634e41fa902de65e4ec7f165","manager"
+"d00138e69f7c427693e437f33e3765af","member","906b88ee8a824e96aa93ea887337d8ac","reader"
+"da9eac51634e41fa902de65e4ec7f165","manager","d00138e69f7c427693e437f33e3765af","member"
+')
+        instances = Puppet::Type::Keystone_implied_role::ProviderOpenstack.instances
+        expect(instances.count).to eq(3)
+        expect(instances[0].role).to eq('admin')
+        expect(instances[0].implied_role).to eq('manager')
+        expect(instances[1].role).to eq('member')
+        expect(instances[1].implied_role).to eq('reader')
+        expect(instances[2].role).to eq('manager')
+        expect(instances[2].implied_role).to eq('member')
+      end
+    end
+  end
+end
diff --git a/spec/unit/type/keystone_implied_role_spec.rb b/spec/unit/type/keystone_implied_role_spec.rb
new file mode 100644
index 000000000..53062e8f4
--- /dev/null
+++ b/spec/unit/type/keystone_implied_role_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+require 'puppet'
+require 'puppet/type/keystone_implied_role'
+
+describe Puppet::Type.type(:keystone_implied_role) do
+
+  describe 'role@implied_role' do
+    include_examples 'parse title correctly',
+      :role         => 'role',
+      :implied_role => 'implied_role'
+  end
+
+  describe '#autorequire' do
+    let(:child) do
+      Puppet::Type.type(:keystone_role).new(:title => 'child')
+    end
+
+    let(:parent) do
+      Puppet::Type.type(:keystone_role).new(:title => 'parent')
+    end
+
+    let(:another) do
+      Puppet::Type.type(:keystone_role).new(:title => 'another')
+    end
+
+    context 'role autorequire from title' do
+      let(:implied_role) do
+        Puppet::Type.type(:keystone_implied_role).new(:title  => 'child@parent')
+      end
+      describe 'should require the correct domain' do
+        let(:resources) { [implied_role, child, parent, another] }
+        include_examples 'autorequire the correct resources'
+      end
+    end
+  end
+end