You can partition the table using only metadata operations by creating a new partitioned table that maps all the existing rows to a single partition, and SWITCHing the table into the partition on the new table. Then you can rename/drop and you've got a partitioned table without having to rewrite any of the rows, e.g.
use master
go
create database part_test
go
use part_test
go
drop table if exists Foo
drop table if exists Foo_New
go
create table Foo(id int identity primary key, a int, b int)
insert into Foo(a,b)
select value%5, value%17
from generate_series(1,1000*1000)
go
drop partition scheme ps_foo
drop partition function pf_Foo
go
create partition function pf_Foo(int)
as range right for values (0)
CREATE PARTITION SCHEME ps_Foo
AS PARTITION pf_Foo
ALL TO ([Primary]);
go
create table Foo_new(id int identity primary key, a int, b int)
on ps_Foo(id)
go
begin transaction
alter table Foo add constraint ck_id_gte_0 check (id >= 0)
alter table Foo switch to Foo_new partition 2
exec sp_rename 'Foo','Foo_old', 'object'
exec sp_rename 'Foo_new','Foo', 'object'
exec('drop table Foo_old')
dbcc checkident('Foo')
declare @split_point int = ident_current('foo') + 1
alter partition scheme ps_foo next used [Primary]
alter partition function pf_Foo() split range (@split_point)
commit
All new rows will go into a new partition at the end. You can then add more partitions on the hot end over time by inserting a split point after the last value. And you can go back and split the large partition as time allows. Note since this is RANGE RIGHT you'll want to split from "right" to "left" as the split will move all the rows with values equal or greater than the split point, e.g.
alter partition scheme ps_foo next used [Primary]
alter partition function pf_Foo() split range (900000)
alter partition scheme ps_foo next used [Primary]
alter partition function pf_Foo() split range (800000)
alter partition scheme ps_foo next used [Primary]
alter partition function pf_Foo() split range (700000)