diff --git a/ee/app/graphql/types/members/customizable_admin_permissions_enum.rb b/ee/app/graphql/types/members/customizable_admin_permissions_enum.rb
index 7a4df2402024cc7c055cc70d533c1a45e87e1d6b..83c1a4ec5c4175892f758b139132081d29586829 100644
--- a/ee/app/graphql/types/members/customizable_admin_permissions_enum.rb
+++ b/ee/app/graphql/types/members/customizable_admin_permissions_enum.rb
@@ -9,7 +9,7 @@ class CustomizableAdminPermissionsEnum < BaseEnum
       include CustomizablePermission
 
       MemberRole.all_customizable_admin_permissions.each_pair do |key, value|
-        define_permission(key, value)
+        define_permission(key, value, feature_flag: :custom_admin_roles)
       end
     end
   end
diff --git a/ee/app/graphql/types/members/customizable_permission.rb b/ee/app/graphql/types/members/customizable_permission.rb
index d2a31b6e6b5f06d4b70df19799d13dbd3a1a2a13..e78359e2ac3745608a5abe3f2817873de61af9b1 100644
--- a/ee/app/graphql/types/members/customizable_permission.rb
+++ b/ee/app/graphql/types/members/customizable_permission.rb
@@ -6,11 +6,8 @@ module CustomizablePermission
       extend ActiveSupport::Concern
 
       included do
-        # As new custom abilities are created they are implemented behind a feature flag with a standard
-        # naming convention. Since these abilities depend on the feature flag being enabled, we want to mark
-        # any feature flagged abilities as experimental until they are generally released.
-        def self.define_permission(name, attrs)
-          if ::Feature::Definition.get("custom_ability_#{name}")
+        def self.define_permission(name, attrs, feature_flag: nil)
+          if CustomizablePermission.experimental?(name, feature_flag: feature_flag)
             value name.upcase, value: name, description: attrs[:description],
               experiment: { milestone: attrs[:milestone] }
           else
@@ -18,6 +15,18 @@ def self.define_permission(name, attrs)
           end
         end
       end
+
+      # As new custom abilities are created they are implemented behind a feature flag with a standard
+      # naming convention. Since these abilities depend on the feature flag being enabled, we want to mark
+      # any feature flagged abilities as experimental until they are generally released.
+      #
+      # Optionally, an additional feature flag parameter can be passed to check a feature flag that is meant
+      # to mark many custom permissions as experimental at once.
+      def self.experimental?(permission, feature_flag: nil)
+        ["custom_ability_#{permission}", feature_flag].compact.any? do |ff|
+          ::Feature::Definition.get(ff)
+        end
+      end
     end
   end
 end
diff --git a/ee/app/models/members/member_role.rb b/ee/app/models/members/member_role.rb
index ee91f1bfcd31909c45ce5da74347b7a7e2e65fe0..8fe9211bbd05f6f03cc2d8e58a1cacf2d6f31fef 100644
--- a/ee/app/models/members/member_role.rb
+++ b/ee/app/models/members/member_role.rb
@@ -119,6 +119,10 @@ 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)
+        return false
+      end
+
       return true unless ::Feature::Definition.get("custom_ability_#{permission}")
 
       ::Feature.enabled?("custom_ability_#{permission}", user)
diff --git a/ee/config/feature_flags/wip/custom_ability_read_admin_cicd.yml b/ee/config/feature_flags/wip/custom_ability_read_admin_cicd.yml
deleted file mode 100644
index bb8fa312742de58f2fde33d5ca907740bdcdc21a..0000000000000000000000000000000000000000
--- a/ee/config/feature_flags/wip/custom_ability_read_admin_cicd.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-name: custom_ability_read_admin_cicd
-feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/507960
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/177233
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/512831
-milestone: '17.9'
-group: group::authorization
-type: wip
-default_enabled: false
diff --git a/ee/config/feature_flags/wip/custom_ability_read_admin_monitoring.yml b/ee/config/feature_flags/wip/custom_ability_read_admin_monitoring.yml
deleted file mode 100644
index a18039b0eef87d8c2921f80cb6ad1e651f9d7c17..0000000000000000000000000000000000000000
--- a/ee/config/feature_flags/wip/custom_ability_read_admin_monitoring.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-name: custom_ability_read_admin_monitoring
-feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/507960
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/179439
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/515665
-milestone: '17.9'
-group: group::authorization
-type: wip
-default_enabled: false
diff --git a/ee/config/feature_flags/wip/custom_ability_read_admin_subscription.yml b/ee/config/feature_flags/wip/custom_ability_read_admin_subscription.yml
deleted file mode 100644
index f3a494188e0819c896e2653dada5eadcafb17238..0000000000000000000000000000000000000000
--- a/ee/config/feature_flags/wip/custom_ability_read_admin_subscription.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-name: custom_ability_read_admin_subscription
-feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/507961
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/178230
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/514810
-milestone: '17.9'
-group: group::authorization
-type: wip
-default_enabled: false
diff --git a/ee/config/feature_flags/wip/custom_admin_roles.yml b/ee/config/feature_flags/wip/custom_admin_roles.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0105f5eb61174e851a03bd683c7d21ec1b7d5bcc
--- /dev/null
+++ b/ee/config/feature_flags/wip/custom_admin_roles.yml
@@ -0,0 +1,9 @@
+---
+name: custom_admin_roles
+feature_issue_url: https://gitlab.com/groups/gitlab-org/-/epics/15956
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/181346
+rollout_issue_url:
+milestone: '17.9'
+group: group::authorization
+type: wip
+default_enabled: false
diff --git a/ee/spec/models/members/member_role_spec.rb b/ee/spec/models/members/member_role_spec.rb
index a3dffb31345b5c8956b00ff64f4ac5b8a6eb2fb2..068d2a17ce1d8d098666fc4a46bee3f49c0ed2eb 100644
--- a/ee/spec/models/members/member_role_spec.rb
+++ b/ee/spec/models/members/member_role_spec.rb
@@ -440,6 +440,30 @@
 
       it { is_expected.to eq(expected_result) }
     end
+
+    context 'with an admin permission' do
+      let(:ability) { :read_admin_dashboard }
+
+      before do
+        allow(described_class).to receive_messages(
+          all_customizable_permissions: {
+            read_code: { description: 'Test permission' },
+            read_admin_dashboard: { description: 'Test admin permission' }
+          },
+          all_customizable_admin_permissions: { read_admin_dashboard: { description: 'Test admin permission' } }
+        )
+      end
+
+      it { is_expected.to be true }
+
+      context 'when the custom_admin_roles feature flag is disabled' do
+        before do
+          stub_feature_flags(custom_admin_roles: false)
+        end
+
+        it { is_expected.to be false }
+      end
+    end
   end
 
   describe '#enabled_permission_items' do
diff --git a/ee/spec/support/shared_examples/graphql/types/members/customizable_permissions_shared_examples.rb b/ee/spec/support/shared_examples/graphql/types/members/customizable_permissions_shared_examples.rb
index dace73cef4d0e5140369137a6d9e834cd4262712..fdc64a1bc9160cb9f3237fc2149ea5af39dd53ec 100644
--- a/ee/spec/support/shared_examples/graphql/types/members/customizable_permissions_shared_examples.rb
+++ b/ee/spec/support/shared_examples/graphql/types/members/customizable_permissions_shared_examples.rb
@@ -7,15 +7,34 @@
   let(:permission_attrs) { { description: 'read code', milestone: '17.10' } }
 
   describe '.define_permission' do
-    subject(:define_permission) { described_class.define_permission(permission_name, permission_attrs) }
+    let(:feature_flag) { nil }
+
+    subject(:define_permission) do
+      described_class.define_permission(permission_name, permission_attrs, feature_flag: feature_flag)
+    end
 
     context 'for feature flagged permissions' do
-      before do
-        allow(::Feature::Definition).to receive(:get).with("custom_ability_#{permission_name}").and_return(true)
+      context 'for a default feature flag' do
+        before do
+          allow(::Feature::Definition).to receive(:get).with("custom_ability_#{permission_name}").and_return(true)
+        end
+
+        it 'is experimental' do
+          expect(define_permission.deprecation_reason).to include('Experiment')
+        end
       end
 
-      it 'is experimental' do
-        expect(define_permission.deprecation_reason).to include('Experiment')
+      context 'for a custom feature flag' do
+        let(:feature_flag) { :custom_permission_feature_in_dev }
+
+        before do
+          allow(::Feature::Definition).to receive(:get).and_call_original
+          allow(::Feature::Definition).to receive(:get).with(feature_flag).and_return(true)
+        end
+
+        it 'is experimental' do
+          expect(define_permission.deprecation_reason).to include('Experiment')
+        end
       end
     end
 
diff --git a/ee/spec/tasks/gitlab/custom_roles/check_docs_task_spec.rb b/ee/spec/tasks/gitlab/custom_roles/check_docs_task_spec.rb
index 37b0413910820dfbe2ab66bc15bbc7bb7ff80417..0b7b41cdce16f23aed10e50846f3de6d9ff1b875 100644
--- a/ee/spec/tasks/gitlab/custom_roles/check_docs_task_spec.rb
+++ b/ee/spec/tasks/gitlab/custom_roles/check_docs_task_spec.rb
@@ -9,8 +9,9 @@
   let(:docs_path) { Rails.root.join(docs_dir, 'abilities.md') }
   let(:template_erb_path) { Rails.root.join("tooling/custom_roles/docs/templates/custom_abilities.md.erb") }
 
+  # TODO convert to all_customizable_permissions when custom_admin_roles feature flag is removed
   let(:stub_definitions) do
-    expect(::MemberRole).to receive(:all_customizable_permissions).and_return(updated_definitions)
+    expect(::MemberRole).to receive(:all_customizable_standard_permissions).and_return(updated_definitions)
   end
 
   describe '#run' do
@@ -26,10 +27,10 @@
       } }
     end
 
-    let(:added_definition) { MemberRole.all_customizable_permissions.merge(new_ability) }
-    let(:removed_definition) { MemberRole.all_customizable_permissions.except(:admin_terraform_state) }
+    let(:added_definition) { MemberRole.all_customizable_standard_permissions.merge(new_ability) }
+    let(:removed_definition) { MemberRole.all_customizable_standard_permissions.except(:admin_terraform_state) }
     let(:updated_definition) do
-      definitions = MemberRole.all_customizable_permissions
+      definitions = MemberRole.all_customizable_standard_permissions
       definitions[:read_code][:milestone] = '12.0'
 
       definitions
diff --git a/tooling/custom_roles/docs/templates/custom_abilities.md.erb b/tooling/custom_roles/docs/templates/custom_abilities.md.erb
index c5775e6eb6ca02f9ec523c17999b958b4e948148..225e30557effd2d7f5fbf3f74548c7facc6d562c 100644
--- a/tooling/custom_roles/docs/templates/custom_abilities.md.erb
+++ b/tooling/custom_roles/docs/templates/custom_abilities.md.erb
@@ -1,4 +1,5 @@
-<% active_custom_abilities = MemberRole.all_customizable_permissions.filter { |p| !::Feature::Definition.get("custom_ability_#{p}") } %>
+<% # change all_customizable_standard_permissions to all_customizable_permissions when custom_admin_roles feature flag is removed%>
+<% active_custom_abilities = MemberRole.all_customizable_standard_permissions.filter { |p| !::Feature::Definition.get("custom_ability_#{p}") } %>
 <% custom_abilities_by_feature_category = active_custom_abilities.group_by { |_k, definition| definition[:feature_category] } %>
 <% def humanize(feature_category) %>
 <%   case feature_category %>