diff --git a/.rubocop_todo/gitlab/bounded_contexts.yml b/.rubocop_todo/gitlab/bounded_contexts.yml
index 7f68b931863fbcb3a62654d05976b0e2cf7b62f2..d4b7c859060e36de9e58802ceef13edb132aa595 100644
--- a/.rubocop_todo/gitlab/bounded_contexts.yml
+++ b/.rubocop_todo/gitlab/bounded_contexts.yml
@@ -2283,7 +2283,9 @@ Gitlab/BoundedContexts:
     - 'ee/app/graphql/mutations/iterations/update.rb'
     - 'ee/app/graphql/mutations/member_roles/base.rb'
     - 'ee/app/graphql/mutations/member_roles/create.rb'
-    - 'ee/app/graphql/mutations/member_roles/create_admin.rb'
+    - 'ee/app/graphql/mutations/member_roles/admin/base.rb'
+    - 'ee/app/graphql/mutations/member_roles/admin/create.rb'
+    - 'ee/app/graphql/mutations/member_roles/admin/update.rb'
     - 'ee/app/graphql/mutations/member_roles/delete.rb'
     - 'ee/app/graphql/mutations/member_roles/update.rb'
     - 'ee/app/graphql/mutations/quality_management/test_cases/create.rb'
diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md
index 05a118d40d1fd63602b54cc8955f2c5e659ba34c..ed91f3ca4b2cf0432962645e65020fcb2fcab45b 100644
--- a/doc/api/graphql/reference/_index.md
+++ b/doc/api/graphql/reference/_index.md
@@ -7644,7 +7644,7 @@ Input type: `MemberRoleAdminCreateInput`
 | <a id="mutationmemberroleadmincreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
 | <a id="mutationmemberroleadmincreatedescription"></a>`description` | [`String`](#string) | Description of the member role. |
 | <a id="mutationmemberroleadmincreatename"></a>`name` | [`String`](#string) | Name of the member role. |
-| <a id="mutationmemberroleadmincreatepermissions"></a>`permissions` | [`[MemberRoleAdminPermission!]!`](#memberroleadminpermission) | List of all customizable admin permissions. |
+| <a id="mutationmemberroleadmincreatepermissions"></a>`permissions` | [`[MemberRoleAdminPermission!]`](#memberroleadminpermission) | List of all customizable admin permissions. |
 
 #### Fields
 
@@ -7652,7 +7652,34 @@ Input type: `MemberRoleAdminCreateInput`
 | ---- | ---- | ----------- |
 | <a id="mutationmemberroleadmincreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
 | <a id="mutationmemberroleadmincreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
-| <a id="mutationmemberroleadmincreatememberrole"></a>`memberRole` | [`AdminMemberRole`](#adminmemberrole) | Created member role. |
+| <a id="mutationmemberroleadmincreatememberrole"></a>`memberRole` | [`AdminMemberRole`](#adminmemberrole) | Member role. |
+
+### `Mutation.memberRoleAdminUpdate`
+
+{{< details >}}
+**Introduced** in GitLab 17.10.
+**Status**: Experiment.
+{{< /details >}}
+
+Input type: `MemberRoleAdminUpdateInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationmemberroleadminupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationmemberroleadminupdatedescription"></a>`description` | [`String`](#string) | Description of the member role. |
+| <a id="mutationmemberroleadminupdateid"></a>`id` | [`MemberRoleID!`](#memberroleid) | ID of the member role to mutate. |
+| <a id="mutationmemberroleadminupdatename"></a>`name` | [`String`](#string) | Name of the member role. |
+| <a id="mutationmemberroleadminupdatepermissions"></a>`permissions` | [`[MemberRoleAdminPermission!]`](#memberroleadminpermission) | List of all customizable admin permissions. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationmemberroleadminupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationmemberroleadminupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationmemberroleadminupdatememberrole"></a>`memberRole` | [`AdminMemberRole`](#adminmemberrole) | Member role. |
 
 ### `Mutation.memberRoleCreate`
 
@@ -7680,7 +7707,7 @@ Input type: `MemberRoleCreateInput`
 | ---- | ---- | ----------- |
 | <a id="mutationmemberrolecreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
 | <a id="mutationmemberrolecreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
-| <a id="mutationmemberrolecreatememberrole"></a>`memberRole` | [`MemberRole`](#memberrole) | Updated member role. |
+| <a id="mutationmemberrolecreatememberrole"></a>`memberRole` | [`MemberRole`](#memberrole) | Member role. |
 
 ### `Mutation.memberRoleDelete`
 
@@ -7751,7 +7778,7 @@ Input type: `MemberRoleUpdateInput`
 | ---- | ---- | ----------- |
 | <a id="mutationmemberroleupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
 | <a id="mutationmemberroleupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
-| <a id="mutationmemberroleupdatememberrole"></a>`memberRole` | [`MemberRole`](#memberrole) | Updated member role. |
+| <a id="mutationmemberroleupdatememberrole"></a>`memberRole` | [`MemberRole`](#memberrole) | Member role. |
 
 ### `Mutation.mergeRequestAccept`
 
diff --git a/ee/app/graphql/ee/types/mutation_type.rb b/ee/app/graphql/ee/types/mutation_type.rb
index 3b18eeae4faf50c5721b75a9e5b081a9c97fa258..32d5b2e4f79dd8101d435fe50dc73666e1a5c1a4 100644
--- a/ee/app/graphql/ee/types/mutation_type.rb
+++ b/ee/app/graphql/ee/types/mutation_type.rb
@@ -57,7 +57,8 @@ def self.authorization_scopes
         mount_mutation ::Mutations::Iterations::Cadences::Destroy
         mount_mutation ::Mutations::MemberRoles::Update
         mount_mutation ::Mutations::MemberRoles::Create, experiment: { milestone: '16.5' }
-        mount_mutation ::Mutations::MemberRoles::CreateAdmin, experiment: { milestone: '17.7' }
+        mount_mutation ::Mutations::MemberRoles::Admin::Create, experiment: { milestone: '17.7' }
+        mount_mutation ::Mutations::MemberRoles::Admin::Update, experiment: { milestone: '17.10' }
         mount_mutation ::Mutations::MemberRoles::Delete, experiment: { milestone: '16.7' }
         mount_mutation ::Mutations::RequirementsManagement::CreateRequirement
         mount_mutation ::Mutations::RequirementsManagement::ExportRequirements
diff --git a/ee/app/graphql/mutations/member_roles/admin/base.rb b/ee/app/graphql/mutations/member_roles/admin/base.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8ecc7d0bf6c4a1021137e5c2e04358e7baf32538
--- /dev/null
+++ b/ee/app/graphql/mutations/member_roles/admin/base.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Mutations
+  module MemberRoles
+    module Admin
+      # rubocop:disable GraphQL/GraphqlName -- This is a base mutation so name is not needed here
+      class Base < ::Mutations::MemberRoles::Base
+        argument :permissions,
+          [Types::Members::CustomizableAdminPermissionsEnum],
+          required: false,
+          description: 'List of all customizable admin permissions.'
+
+        field :member_role,
+          ::Types::Members::AdminMemberRoleType,
+          description: 'Member role.',
+          null: true
+
+        def ready?(**args)
+          if gitlab_com_subscription?
+            raise Gitlab::Graphql::Errors::ArgumentError, 'admin member roles are not available on SaaS instance.'
+          end
+
+          raise_resource_not_available_error! unless Feature.enabled?(:custom_admin_roles, :instance)
+
+          super
+        end
+      end
+      # rubocop:enable GraphQL/GraphqlName
+    end
+  end
+end
diff --git a/ee/app/graphql/mutations/member_roles/admin/create.rb b/ee/app/graphql/mutations/member_roles/admin/create.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8d2c7f9374ef760d0e898f93cf1e6b0506ed4ad1
--- /dev/null
+++ b/ee/app/graphql/mutations/member_roles/admin/create.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Mutations
+  module MemberRoles
+    module Admin
+      class Create < Base
+        graphql_name 'MemberRoleAdminCreate'
+
+        authorize :admin_member_role
+
+        def resolve(**args)
+          params = canonicalize_for_create(args)
+          response = ::MemberRoles::CreateService.new(current_user, params).execute
+
+          raise_resource_not_available_error! if response.error? && response.reason == :unauthorized
+
+          {
+            member_role: response.payload[:member_role],
+            errors: response.errors
+          }
+        end
+      end
+    end
+  end
+end
diff --git a/ee/app/graphql/mutations/member_roles/admin/update.rb b/ee/app/graphql/mutations/member_roles/admin/update.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8120750200d5e6b8b23dd7c641b3bb3284c1a969
--- /dev/null
+++ b/ee/app/graphql/mutations/member_roles/admin/update.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Mutations
+  module MemberRoles
+    module Admin
+      class Update < Base
+        graphql_name 'MemberRoleAdminUpdate'
+
+        authorize :admin_member_role
+
+        argument :id, ::Types::GlobalIDType[::MemberRole],
+          required: true,
+          description: 'ID of the member role to mutate.'
+
+        def ready?(**args)
+          if args.except(:id).blank?
+            raise Gitlab::Graphql::Errors::ArgumentError, 'The list of member_role attributes is empty'
+          end
+
+          super
+        end
+
+        def resolve(**args)
+          member_role = authorized_find!(id: args.delete(:id))
+
+          unless member_role.admin_related_role?
+            raise Gitlab::Graphql::Errors::ArgumentError, 'This mutation can only be used to update admin member roles'
+          end
+
+          params = canonicalize_for_update(args,
+            available_permissions: MemberRole.all_customizable_admin_permission_keys)
+
+          response = ::MemberRoles::UpdateService.new(current_user, params).execute(member_role)
+
+          {
+            member_role: response.payload[:member_role],
+            errors: response.errors
+          }
+        end
+      end
+    end
+  end
+end
diff --git a/ee/app/graphql/mutations/member_roles/base.rb b/ee/app/graphql/mutations/member_roles/base.rb
index cda54d8c0e4288ead4b64da41a33f95c480e2c20..c1a1772a99ef9a00a2f998aca3857c83b47871d3 100644
--- a/ee/app/graphql/mutations/member_roles/base.rb
+++ b/ee/app/graphql/mutations/member_roles/base.rb
@@ -3,23 +3,47 @@
 module Mutations
   module MemberRoles
     class Base < ::Mutations::BaseMutation
-      authorize :admin_member_role
+      include ::GitlabSubscriptions::SubscriptionHelper
 
-      field :member_role, ::Types::MemberRoles::MemberRoleType,
-        description: 'Updated member role.', null: true
+      field :member_role,
+        ::Types::MemberRoles::MemberRoleType,
+        description: 'Member role.',
+        null: true
 
       argument :description,
         GraphQL::Types::String,
         required: false,
         description: 'Description of the member role.'
+
       argument :name,
         GraphQL::Types::String,
         required: false,
         description: 'Name of the member role.'
+
       argument :permissions,
         [Types::MemberRoles::PermissionsEnum],
         required: false,
         description: 'List of all customizable permissions.'
+
+      private
+
+      def canonicalize_for_create(args)
+        permissions = args.delete(:permissions) || []
+        permissions.each_with_object(args) do |permission, new_args|
+          new_args[permission.downcase] = true
+        end
+      end
+
+      def canonicalize_for_update(args, available_permissions: MemberRole.all_customizable_standard_permissions.keys)
+        permissions = args.delete(:permissions) || []
+        permissions.each_with_object(args) do |permission, new_args|
+          new_args[permission.downcase] = true
+        end
+
+        (available_permissions - permissions).each_with_object(args) do |permission, new_args|
+          new_args[permission.downcase] = false
+        end
+      end
     end
   end
 end
diff --git a/ee/app/graphql/mutations/member_roles/create.rb b/ee/app/graphql/mutations/member_roles/create.rb
index fcc6bfb5452da4de12864157f3b3aaa79a3c283a..c41b5b8cd437b99f247caf07f1d696146df7ba8a 100644
--- a/ee/app/graphql/mutations/member_roles/create.rb
+++ b/ee/app/graphql/mutations/member_roles/create.rb
@@ -5,9 +5,10 @@ module MemberRoles
     class Create < Base
       graphql_name 'MemberRoleCreate'
 
-      include ::GitlabSubscriptions::SubscriptionHelper
       include Mutations::ResolvesNamespace
 
+      authorize :admin_member_role
+
       argument :base_access_level,
         ::Types::Members::MemberRoles::AccessLevelEnum,
         required: true,
@@ -30,7 +31,7 @@ def ready?(**args)
       def resolve(**args)
         group = find_group(args.delete(:group_path))
 
-        params = canonicalize(args.merge(namespace: group))
+        params = canonicalize_for_create(args.merge(namespace: group))
         response = ::MemberRoles::CreateService.new(current_user, params).execute
 
         raise_resource_not_available_error! if response.error? && response.reason == :unauthorized
@@ -68,13 +69,6 @@ def extra_group_path?(args)
 
         args[:group_path].present?
       end
-
-      def canonicalize(args)
-        permissions = args.delete(:permissions) || []
-        permissions.each_with_object(args) do |permission, new_args|
-          new_args[permission.downcase] = true
-        end
-      end
     end
   end
 end
diff --git a/ee/app/graphql/mutations/member_roles/create_admin.rb b/ee/app/graphql/mutations/member_roles/create_admin.rb
deleted file mode 100644
index bcf00df594e0a95f349efc47567dcd031a0f8f24..0000000000000000000000000000000000000000
--- a/ee/app/graphql/mutations/member_roles/create_admin.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
-  module MemberRoles
-    class CreateAdmin < Base
-      graphql_name 'MemberRoleAdminCreate'
-
-      include ::GitlabSubscriptions::SubscriptionHelper
-
-      argument :permissions,
-        [Types::Members::CustomizableAdminPermissionsEnum],
-        required: true,
-        description: 'List of all customizable admin permissions.'
-
-      field :member_role, ::Types::Members::AdminMemberRoleType,
-        description: 'Created member role.', null: true
-
-      def ready?(**args)
-        if gitlab_com_subscription?
-          raise Gitlab::Graphql::Errors::ArgumentError, 'admin member roles are not available on SaaS instance.'
-        end
-
-        raise_resource_not_available_error! unless Feature.enabled?(:custom_ability_read_admin_dashboard, current_user)
-
-        super
-      end
-
-      def resolve(**args)
-        response = ::MemberRoles::CreateService.new(current_user, canonicalize(args)).execute
-
-        raise_resource_not_available_error! if response.error? && response.reason == :unauthorized
-
-        {
-          member_role: response.payload[:member_role],
-          errors: response.errors
-        }
-      end
-
-      private
-
-      def canonicalize(args)
-        permissions = args.delete(:permissions) || []
-        permissions.each_with_object(args) do |permission, new_args|
-          new_args[permission.downcase] = true
-        end
-      end
-    end
-  end
-end
diff --git a/ee/app/graphql/mutations/member_roles/update.rb b/ee/app/graphql/mutations/member_roles/update.rb
index 4541c16c7f93a8721837acb9500d5cd661780700..0b990fedb2e7b418c3c4314b59068d190b473239 100644
--- a/ee/app/graphql/mutations/member_roles/update.rb
+++ b/ee/app/graphql/mutations/member_roles/update.rb
@@ -5,6 +5,8 @@ module MemberRoles
     class Update < Base
       graphql_name 'MemberRoleUpdate'
 
+      authorize :admin_member_role
+
       argument :id, ::Types::GlobalIDType[::MemberRole],
         required: true,
         description: 'ID of the member role to mutate.'
@@ -20,7 +22,7 @@ def ready?(**args)
       def resolve(**args)
         member_role = authorized_find!(id: args.delete(:id))
 
-        params = canonicalize(args)
+        params = canonicalize_for_update(args)
         response = ::MemberRoles::UpdateService.new(current_user, params).execute(member_role)
 
         {
@@ -28,24 +30,6 @@ def resolve(**args)
           errors: response.errors
         }
       end
-
-      private
-
-      def canonicalize(args)
-        permissions = args.delete(:permissions)
-        return args if permissions.nil?
-
-        available_permissions = MemberRole.all_customizable_permissions.keys
-
-        set_permissions(args, permissions & available_permissions, true)
-        set_permissions(args, available_permissions - permissions, false)
-      end
-
-      def set_permissions(args, permissions, value)
-        permissions.each_with_object(args) do |permission, new_args|
-          new_args[permission] = value
-        end
-      end
     end
   end
 end
diff --git a/ee/app/graphql/mutations/users/member_roles/assign.rb b/ee/app/graphql/mutations/users/member_roles/assign.rb
index 8813c219b1c147629655a461418213bba66ded23..49308a759ce316b459583019bdc204530c8e1bd5 100644
--- a/ee/app/graphql/mutations/users/member_roles/assign.rb
+++ b/ee/app/graphql/mutations/users/member_roles/assign.rb
@@ -25,8 +25,7 @@ class Assign < BaseMutation
         def ready?(**args)
           raise_resource_not_available_error! if gitlab_com_subscription?
 
-          raise_resource_not_available_error! unless Feature.enabled?(:custom_ability_read_admin_dashboard,
-            current_user)
+          raise_resource_not_available_error! unless Feature.enabled?(:custom_admin_roles, :instance)
 
           raise_resource_not_available_error! unless current_user.can?(:admin_member_role)
 
diff --git a/ee/app/models/members/member_role.rb b/ee/app/models/members/member_role.rb
index 8fe9211bbd05f6f03cc2d8e58a1cacf2d6f31fef..7c8e392c0c8aab8ae0aa45c1b3922cf346fc9e81 100644
--- a/ee/app/models/members/member_role.rb
+++ b/ee/app/models/members/member_role.rb
@@ -119,7 +119,8 @@ def customizable_permissions_exempt_from_consuming_seat
     end
 
     def permission_enabled?(permission, user = nil)
-      if ::Feature.disabled?(:custom_admin_roles, user) && all_customizable_admin_permission_keys.include?(permission)
+      if ::Feature.disabled?(:custom_admin_roles, :instance) &&
+          all_customizable_admin_permission_keys.include?(permission)
         return false
       end
 
diff --git a/ee/app/services/users/member_roles/assign_service.rb b/ee/app/services/users/member_roles/assign_service.rb
index c80165b13ee7bfb2b089770b8b90253dffe5707a..d16593b90dcae9c2fb49031b2d71ae83aa462850 100644
--- a/ee/app/services/users/member_roles/assign_service.rb
+++ b/ee/app/services/users/member_roles/assign_service.rb
@@ -22,7 +22,7 @@ def execute
           )
         end
 
-        unless Feature.enabled?(:custom_ability_read_admin_dashboard, current_user)
+        unless Feature.enabled?(:custom_admin_roles, :instance)
           return ServiceResponse.error(message: 'Not yet available', reason: :forbidden)
         end
 
diff --git a/ee/spec/requests/api/graphql/mutations/member_role/create_admin_member_role_spec.rb b/ee/spec/requests/api/graphql/mutations/member_roles/admin/create_spec.rb
similarity index 84%
rename from ee/spec/requests/api/graphql/mutations/member_role/create_admin_member_role_spec.rb
rename to ee/spec/requests/api/graphql/mutations/member_roles/admin/create_spec.rb
index 70e5e8fc1f97cbcf1eaa914ec8e9f850cbdfffe6..0a3e3f1b90ad8bff5e1d8784320304482e5db6df 100644
--- a/ee/spec/requests/api/graphql/mutations/member_role/create_admin_member_role_spec.rb
+++ b/ee/spec/requests/api/graphql/mutations/member_roles/admin/create_spec.rb
@@ -2,7 +2,7 @@
 
 require 'spec_helper'
 
-RSpec.describe 'creating member role', feature_category: :permissions do
+RSpec.describe 'creating admin member role', :enable_admin_mode, feature_category: :permissions do
   include GraphqlHelpers
 
   let_it_be_with_reload(:current_user) { create(:admin) }
@@ -33,7 +33,7 @@
 
   subject(:create_member_role) { graphql_mutation_response(:member_role_admin_create) }
 
-  context 'without the custom roles feature', :enable_admin_mode do
+  context 'without the custom roles feature' do
     before do
       stub_licensed_features(custom_roles: false)
     end
@@ -41,7 +41,7 @@
     it_behaves_like 'a mutation that returns a top-level access error'
   end
 
-  context 'with the custom roles feature', :enable_admin_mode do
+  context 'with the custom roles feature' do
     before do
       stub_licensed_features(custom_roles: true)
     end
@@ -58,9 +58,9 @@
     context 'when on self-managed' do
       it_behaves_like 'a mutation that creates a member role'
 
-      context 'when custom_ability_read_admin_dashboard FF is disabled' do
+      context 'when custom_admin_roles FF is disabled' do
         before do
-          stub_feature_flags(custom_ability_read_admin_dashboard: false)
+          stub_feature_flags(custom_admin_roles: false)
         end
 
         it_behaves_like 'a mutation that returns a top-level access error',
diff --git a/ee/spec/requests/api/graphql/mutations/member_roles/admin/update_spec.rb b/ee/spec/requests/api/graphql/mutations/member_roles/admin/update_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9e33a4da7cdae625972fd390aa50c8f65b0b28a1
--- /dev/null
+++ b/ee/spec/requests/api/graphql/mutations/member_roles/admin/update_spec.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'updating admin member role', :enable_admin_mode, feature_category: :permissions do
+  include GraphqlHelpers
+
+  let_it_be(:member_role) { create(:member_role, :read_admin_dashboard) }
+  let_it_be_with_reload(:current_user) { create(:admin) }
+
+  let(:name) { 'new name' }
+  let(:description) { 'new description' }
+  let(:permissions) { %w[READ_ADMIN_MONITORING READ_ADMIN_CICD] }
+
+  let(:input) do
+    { 'id' => member_role.to_global_id.to_s, 'name' => name, 'description' => description,
+      'permissions' => permissions }
+  end
+
+  let(:fields) do
+    <<~FIELDS
+      errors
+      memberRole {
+        id
+        name
+        description
+        enabledPermissions {
+          nodes {
+            value
+          }
+        }
+      }
+    FIELDS
+  end
+
+  let(:mutation) { graphql_mutation(:member_role_admin_update, input, fields) }
+
+  subject(:update_admin_member_role) { graphql_mutation_response(:member_role_admin_update) }
+
+  context 'without the custom roles feature' do
+    before do
+      stub_licensed_features(custom_roles: false)
+    end
+
+    it_behaves_like 'a mutation that returns a top-level access error'
+  end
+
+  context 'with the custom roles feature' do
+    before do
+      stub_licensed_features(custom_roles: true)
+    end
+
+    context 'when on SaaS' do
+      before do
+        stub_saas_features(gitlab_com_subscriptions: true)
+      end
+
+      it_behaves_like 'a mutation that returns top-level errors',
+        errors: ['admin member roles are not available on SaaS instance.']
+    end
+
+    context 'when on self-managed' do
+      it 'returns success' do
+        post_graphql_mutation(mutation, current_user: current_user)
+
+        expect(graphql_errors).to be_nil
+
+        expect(update_admin_member_role['memberRole']).to include('name' => 'new name')
+
+        expect(update_admin_member_role['memberRole']).to include('description' => 'new description')
+
+        expect(update_admin_member_role['memberRole']['enabledPermissions']['nodes'].flat_map(&:values))
+          .to match_array(permissions)
+      end
+
+      it 'updates the member role' do
+        post_graphql_mutation(mutation, current_user: current_user)
+
+        member_role.reload
+
+        expect(member_role.name).to eq('new name')
+        expect(member_role.description).to eq('new description')
+        expect(member_role.read_admin_monitoring).to be(true)
+        expect(member_role.read_admin_cicd).to be(true)
+        expect(member_role.read_admin_dashboard).to be(false)
+      end
+
+      context 'when member role is not an admin role' do
+        let(:member_role) { create(:member_role, :guest, :read_code, :instance) }
+
+        it_behaves_like  'a mutation that returns top-level errors',
+          errors: ['This mutation can only be used to update admin member roles']
+      end
+
+      context 'when `custom_admin_roles` feature-flag is disabled' do
+        before do
+          stub_feature_flags(custom_admin_roles: false)
+        end
+
+        it_behaves_like 'a mutation that returns a top-level access error'
+      end
+
+      context 'when current user is not an admin' do
+        before do
+          current_user.update!(admin: false)
+        end
+
+        it_behaves_like 'a mutation that returns a top-level access error'
+      end
+
+      context 'with missing arguments' do
+        let(:input) { { 'id' => member_role.to_global_id.to_s } }
+
+        it_behaves_like 'a mutation that returns top-level errors',
+          errors: ['The list of member_role attributes is empty']
+      end
+    end
+  end
+end
diff --git a/ee/spec/requests/api/graphql/mutations/member_role/create_member_role_spec.rb b/ee/spec/requests/api/graphql/mutations/member_roles/create_spec.rb
similarity index 96%
rename from ee/spec/requests/api/graphql/mutations/member_role/create_member_role_spec.rb
rename to ee/spec/requests/api/graphql/mutations/member_roles/create_spec.rb
index 496ff21b0485fe3275a71a60099c2b2ab90836af..ea2ebebc800ba9d2e203e034a21d9fc8d303d5c5 100644
--- a/ee/spec/requests/api/graphql/mutations/member_role/create_member_role_spec.rb
+++ b/ee/spec/requests/api/graphql/mutations/member_roles/create_spec.rb
@@ -99,7 +99,7 @@
 
             it_behaves_like 'a mutation that returns top-level errors',
               errors: ["The resource that you are attempting to access does not exist or " \
-                       "you don't have permission to perform this action"]
+                "you don't have permission to perform this action"]
           end
 
           context 'with missing arguments' do
@@ -123,7 +123,7 @@
       context 'when the current user is not an instance admin' do
         it_behaves_like 'a mutation that returns a top-level access error',
           errors: ["The resource that you are attempting to access does not exist or " \
-                   "you don't have permission to perform this action"]
+            "you don't have permission to perform this action"]
       end
 
       context 'when the current user is an instance admin' do
diff --git a/ee/spec/requests/api/graphql/mutations/member_role/delete_member_role_spec.rb b/ee/spec/requests/api/graphql/mutations/member_roles/delete_spec.rb
similarity index 100%
rename from ee/spec/requests/api/graphql/mutations/member_role/delete_member_role_spec.rb
rename to ee/spec/requests/api/graphql/mutations/member_roles/delete_spec.rb
diff --git a/ee/spec/requests/api/graphql/mutations/member_role/update_member_role_spec.rb b/ee/spec/requests/api/graphql/mutations/member_roles/update_spec.rb
similarity index 62%
rename from ee/spec/requests/api/graphql/mutations/member_role/update_member_role_spec.rb
rename to ee/spec/requests/api/graphql/mutations/member_roles/update_spec.rb
index d25354cd133e90fa35f6d222c291907928887a09..15c04422f1eced49cfa65a5c80dd5540bf45ce4f 100644
--- a/ee/spec/requests/api/graphql/mutations/member_role/update_member_role_spec.rb
+++ b/ee/spec/requests/api/graphql/mutations/member_roles/update_spec.rb
@@ -6,13 +6,14 @@
   include GraphqlHelpers
 
   let_it_be(:group) { create(:group) }
-  let_it_be(:member_role) { create(:member_role, namespace: group) }
+  let_it_be(:member_role) { create(:member_role, :read_code, namespace: group) }
   let_it_be(:current_user) { create(:user) }
 
   let(:name) { 'new name' }
-  let(:permissions) { MemberRole.all_customizable_standard_permissions.keys.map(&:to_s).map(&:upcase) }
+  let(:description) { 'new description' }
+  let(:permissions) { %w[READ_VULNERABILITY ADMIN_VULNERABILITY] }
 
-  let(:input) { { 'name' => name, 'permissions' => permissions } }
+  let(:input) { { 'name' => name, 'description' => description, 'permissions' => permissions } }
   let(:mutation) { graphql_mutation(:memberRoleUpdate, input.merge('id' => member_role.to_global_id.to_s), fields) }
   let(:fields) do
     <<~FIELDS
@@ -72,14 +73,22 @@
 
           expect(update_member_role['memberRole']).to include('name' => 'new name')
 
+          expect(update_member_role['memberRole']).to include('description' => 'new description')
+
           expect(update_member_role['memberRole']['enabledPermissions']['nodes'].flat_map(&:values))
             .to match_array(permissions)
         end
 
         it 'updates the member role' do
-          expect { post_graphql_mutation(mutation, current_user: current_user) }
-            .to change { member_role.reload.name }.to('new name')
-            .and change { member_role.read_vulnerability }.to(true)
+          post_graphql_mutation(mutation, current_user: current_user)
+
+          member_role.reload
+
+          expect(member_role.name).to eq('new name')
+          expect(member_role.description).to eq('new description')
+          expect(member_role.read_vulnerability).to be(true)
+          expect(member_role.admin_vulnerability).to be(true)
+          expect(member_role.read_code).to be(false)
         end
       end
 
@@ -105,6 +114,31 @@
             .to include("The list of member_role attributes is empty")
         end
       end
+
+      context 'when ability is disabled' do
+        before do
+          stub_feature_flag_definition("custom_ability_admin_vulnerability")
+          stub_feature_flags(custom_ability_admin_vulnerability: false)
+        end
+
+        it 'returns success without the ability' do
+          post_graphql_mutation(mutation, current_user: current_user)
+
+          expect(graphql_errors).to be_nil
+
+          expect(update_member_role['memberRole']['enabledPermissions']['nodes'].flat_map(&:values))
+            .to eq(['READ_VULNERABILITY'])
+        end
+
+        it 'updates member role' do
+          post_graphql_mutation(mutation, current_user: current_user)
+
+          member_role.reload
+
+          expect(member_role.read_vulnerability).to be(true)
+          expect(member_role.admin_vulnerability).to be(true)
+        end
+      end
     end
   end
 end
diff --git a/ee/spec/requests/api/graphql/mutations/users/member_roles/assign_spec.rb b/ee/spec/requests/api/graphql/mutations/users/member_roles/assign_spec.rb
index feeb1ddfa9dff8aa6fb5c059e1e2eaf61319823a..8c689249f884a78dbe3e1c46e97f2493c48d4937 100644
--- a/ee/spec/requests/api/graphql/mutations/users/member_roles/assign_spec.rb
+++ b/ee/spec/requests/api/graphql/mutations/users/member_roles/assign_spec.rb
@@ -61,9 +61,9 @@ def mutation_response
   end
 
   context 'when current user is an admin', :enable_admin_mode do
-    context 'when custom_ability_read_admin_dashboard FF is disabled' do
+    context 'when custom_admin_roles FF is disabled' do
       before do
-        stub_feature_flags(custom_ability_read_admin_dashboard: false)
+        stub_feature_flags(custom_admin_roles: false)
       end
 
       it_behaves_like 'a mutation that returns a top-level access error',
@@ -71,7 +71,7 @@ def mutation_response
           "you don't have permission to perform this action"]
     end
 
-    context 'when custom_ability_read_admin_dashboard FF is enabled' do
+    context 'when custom_admin_roles FF is enabled' do
       context 'when on SaaS' do
         before do
           stub_saas_features(gitlab_com_subscriptions: true)
diff --git a/ee/spec/services/users/member_roles/assign_service_spec.rb b/ee/spec/services/users/member_roles/assign_service_spec.rb
index f101465fedbf1b89587024d3e55ac573ee4befc9..2db4078e8fce4fa8032ef6787a6b9576ee32ee23 100644
--- a/ee/spec/services/users/member_roles/assign_service_spec.rb
+++ b/ee/spec/services/users/member_roles/assign_service_spec.rb
@@ -28,9 +28,9 @@
   end
 
   context 'when current user is an admin', :enable_admin_mode do
-    context 'when custom_ability_read_admin_dashboard FF is disabled' do
+    context 'when custom_admin_roles FF is disabled' do
       before do
-        stub_feature_flags(custom_ability_read_admin_dashboard: false)
+        stub_feature_flags(custom_admin_roles: false)
       end
 
       it 'returns an error' do
@@ -38,7 +38,7 @@
       end
     end
 
-    context 'when custom_ability_read_admin_dashboard FF is enabled' do
+    context 'when custom_admin_roles FF is enabled' do
       context 'when member_role param is present' do
         it 'creates a new user member role relation' do
           expect { assign_member_role }.to change { Users::UserMemberRole.count }.by(1)