diff --git a/config/application.rb b/config/application.rb
index d23746fd7a09e0c9c423a20e56a7cdcf2da2d30b..c678a12fe8e0efa7b0e907b76880561868e31c5b 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -51,6 +51,10 @@ module Greenlight
     # The default callback url that bn launcher will redirect to
     config.gl_callback_url = ENV["GL_CALLBACK_URL"]
 
+    # Greenlight V3 variables:
+    config.v3_endpoint = ENV["V3_ENDPOINT"]
+    config.v3_secret_key_base = ENV["V3_SECRET_KEY_BASE"]
+
     # Default credentials (test-install.blindsidenetworks.com/bigbluebutton).
     config.bigbluebutton_endpoint_default = "http://test-install.blindsidenetworks.com/bigbluebutton/api/"
     config.bigbluebutton_secret_default = "8cd8ef52e8e101574e400365b55e11a6"
diff --git a/lib/tasks/migrations/migrations.rake b/lib/tasks/migrations/migrations.rake
new file mode 100644
index 0000000000000000000000000000000000000000..f54c37f48cc09606d841953e6df0aa2df608229a
--- /dev/null
+++ b/lib/tasks/migrations/migrations.rake
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+namespace :migrations do
+  COMMON = {
+    headers: { "Content-Type" => "application/json" }
+  }.freeze
+
+  desc "Migrates v2 resources to v3"
+  task :roles, [] => :environment do |_task, _args|
+    has_encountred_issue = 0
+
+    Role.select(:id, :name).where.not(name: Role::RESERVED_ROLE_NAMES).each do |r|
+      params = { role: { name: r.name } }
+      response = Net::HTTP.post(uri('roles'), payload(params), COMMON[:headers])
+
+      case response
+      when Net::HTTPCreated
+        puts green "Succesfully migrated Role:"
+        puts cyan "  Name: #{r.name}"
+      else
+        puts red "Unable to migrate Role:"
+        puts red "  Name: #{r.name}"
+        has_encountred_issue = 1 # At least one of the migrations failed.
+      end
+    end
+
+    puts
+    puts green "Roles migration complete."
+    puts yellow "In case of an error please retry the process to resolve." unless has_encountred_issue.zero?
+    exit has_encountred_issue
+  end
+
+  private
+
+  def encrypt_params(params)
+    unless Rails.configuration.v3_secret_key_base.present?
+      raise red 'Unable to migrate: No "V3_SECRET_KEY_BASE" provided, please check your .env file.'
+    end
+
+    unless Rails.configuration.v3_secret_key_base.size >= 32
+      raise red 'Unable to migrate: Provided "V3_SECRET_KEY_BASE" must be at least 32 charchters in length.'
+    end
+
+    key = Rails.configuration.v3_secret_key_base[0..31]
+    crypt = ActiveSupport::MessageEncryptor.new(key, cipher: 'aes-256-gcm', serializer: Marshal)
+    crypt.encrypt_and_sign(params, expires_in: 10.seconds)
+  end
+
+  def uri(path)
+    unless Rails.configuration.v3_endpoint.present?
+      raise red 'Unable to migrate: No "V3_ENDPOINT" provided, please check your .env file.'
+    end
+
+    res = URI(Rails.configuration.v3_endpoint)
+    res.path = "/api/v1/migrations/#{path}.json"
+    res
+  end
+
+  def payload(params)
+    encrypted_params = { "encrypted_params" => encrypt_params(params) }
+    res = { "v2" => encrypted_params }
+    res.to_json
+  end
+end
diff --git a/sample.env b/sample.env
index e8200fbcab412fd5a7309df183e1fe813b710725..601f59163ed0597ef261517cdcc477c35a44339f 100644
--- a/sample.env
+++ b/sample.env
@@ -16,6 +16,13 @@ SECRET_KEY_BASE=
 BIGBLUEBUTTON_ENDPOINT=
 BIGBLUEBUTTON_SECRET=
 
+# The endpoint and "SECRET_KEY_BASE" for your Greenlight v3 instance.
+# Set these if you are trying to migrate your resources to v3.
+# Example:
+#V3_ENDPOINT=https://v3.greenlight.test/
+#V3_SECRET_KEY_BASE=
+V3_ENDPOINT=
+V3_SECRET_KEY_BASE=
 # The hostname that the application is accessible from.
 #
 # Used to protect against various HTTP header attacks