Minimal permissions required to read index size in SQL Server I'm involved with managing an application with a large number of SQL Server 2014 databases and am extracting table and index sizes in order to track capacity. I'm using the following query:

select
    db_name() as DBName,
    object_schema_name(i.object_id) as SchemaName,
    object_name(i.object_id) as TableName,
    isnull(i.name,'(HEAP)') as IndexName,
    ps.SizeInPages
from
    sys.indexes i
        inner join 
        (
            select ps.object_id,ps.index_id,sum(ps.row_count) as IndexRows, sum(ps.used_page_count) as SizeInPages, count(*) as PartitionCount
            from sys.dm_db_partition_stats ps
            group by ps.object_id,ps.index_id
        ) ps on 
            ps.object_id = i.object_id and
            ps.index_id = i.index_id
where
    object_schema_name(i.object_id) not in ('cdc','sys')


Ideally I'd like to run this query from a SQL Login with the minimum required permissions, but so far it seems like the user needs VIEW SERVER STATE and SELECT permissions on all the tables. I've also tried putting the above query in a stored procedure and giving the user account EXECUTE permissions on that, but sys.indexes is still filtered based on what the user has select rights to.

Is there another way of doing this that I'm missing?
There are couple ways to achieve what you are after.

I will show you two, but there are other ways as well.

The first one is signing a procedure, and allowing other users to use it with specifying grant execute on object::yourprocedure only.

First of all you need a master key, which i assume you already have, and you can check it using sys.symmetric_keys DMV, and looking for record that has '##' as a prefix. If not simply create a DB master key 

'CREATE MASTER KEY
ENCRYPTION BY PASSWORD = 'P@$$W0RD'


This is very importaint key, and make sure you back it up using this guide

Now you are going to create a certificate based on which you will create module signing user. Note it is only for singing modules and you cannot use it to test privileges etc. So first things first - Certificate :

CREATE CERTIFICATE SignModules
WITH SUBJECT = 'Certificate for signing modules'


Just like the master key, you need to back up this certificate as well. guide

Now we are creating a user with certificate which will have all the rights:

CREATE USER UnlockCertifiedProcedures FOR CERTIFICATE SignModules


There are two ways of providing necessary rights:


becoming a db_reader as exec sp_addrolemember 'db_datareader',UnlockCertifiedProcedures
giving appropriate rights GRANT VIEW DEFINITION TO UnlockCertifiedProcedures, GRANT VIEW DATABASE STATE TO  UnlockCertifiedProcedures


Once this is done, the next step is to actually sign this procedure, so whoever has rights to 'execute' the procedure will inherit rights from the certificate user.

ADD SIGNATURE TO Schema.YourProcedure
BY CERTIFICATE SignModules


To test: 

create user TestUser WITHOUT LOGIN 
grant execute on OBJECT::YourProcedure to TestUser
execute as user = 'TestUser'
exec Schema.YourProcedure

The second option is creating a user and executing procedure as that specified user.

CREATE USER AnotherTest WITHOUT LOGIN
GRANT VIEW DEFINITION TO AnotherTest
GRANT VIEW DATABASE STATE To AnotherTest
GRANT EXECUTE ON OBJECT::YourProcedure To AnotherTest


Create procedure executing as that user:

CREATE proc  Schema.YourProcedure WITH EXECUTE AS 'AnotherTest'
AS
--your code--


Now simply give execute rights on a procedure to a specified user :

GRANT EXECUTE ON OBJECT::YourProcedure To SomeUser


Like i said there are other ways, including roles but these two could get you a job done. Just remember you cannot track a user that executed procedure by specifying 'execute as'(login is possible tho). Also creating a certificate/database keys can be a headache when you`re migrating DB, or simply restoring it somewhere else.