Can I assume the following code will always pass my assertions? I'm a worried about the index value. I'm not sure if the scoped value will be passed along to the Task.Run lambda expression. I think it will be scoped just like the attributesChunked value seems to be. But I'd like some confirmation.
var tasks = new List<Task>();
var attributes = new string[4]{ "att1", "att2", "att3", "att4" };
var chunkIndex = -1;
foreach (var attributesChunked in attributes.Chunk(2))
{
    var index = Interlocked.Increment(ref chunkIndex);
    tasks.Add(Task.Run(() =>
    {
        if (index == 0)
             Assert.Equal("attr1", attributesChunked.First());
        if (index == 2)
             Assert.Equal("attr3", attributesChunked.First());
    }
}
await Task.WhenAll(tasks);


Can I be guaranteed that a variable value will be passed along to a new running Task?
For each task you create, you are introducing a closure on the index variable declared in that iteration. So yes your Task will use the right value, as can be tested using following code:
static async Task Main(string[] args)
{
    var tasks = new List<Task>();
    var attributes = new string[4] { "att1", "att2", "att3", "att4" };
    var chunkIndex = -1;
    foreach (var attributesChunked in attributes.Chunk(2))
    {
        var index = Interlocked.Increment(ref chunkIndex);
        tasks.Add(Task.Run(async () =>
        {
            //simulate some work...
            await Task.Delay(50);
            Console.WriteLine($"index: {index}");
        }));
    }
    await Task.WhenAll(tasks);
}

The result:

index: 0
index: 1

Note that the sequence of the output is entirely dependent on the scheduling of the individual Tasks and cannot be predicted...
Useful reference: What are 'closures' in .NET?
