Is this a good approach for temporarily changing the current thread's culture?

I like the using approach. I'd also create an extension method to make things read better:

var customerCulture = new CultureInfo(currentCustomer.Locale);  
using (customerCulture.AsCurrent()) {
  invoiceService.CreateInvoice(currentCustomer.CustomerId);
}

Something like this:

public static class CultureInfoExtensions {
  public static IDisposable AsCurrent(this CultureInfo culture) {
    return new CultureRunner(culture);
  }
}

CultureRunner example:

public class CultureRunner : IDisposable
{
    readonly CultureInfo originalCulture;
    readonly CultureInfo originalUICulture;

    public CultureRunner(CultureInfo culture)
    {
        if (culture == null)
            throw new ArgumentNullException(nameof(culture));

        originalCulture = Thread.CurrentThread.CurrentCulture;
        originalUICulture = Thread.CurrentThread.CurrentUICulture;

        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;
    }

    public void Dispose()
    {
        Thread.CurrentThread.CurrentCulture = originalCulture;
        Thread.CurrentThread.CurrentUICulture = originalUICulture;
    }
}

Or, if it's always your customer object who sets the culture, another extension method would raise the abstraction even further:

using (currentCustomer.CultureContext()) {
  invoiceService.CreateInvoice(currentCustomer.CustomerId);
}

Since you are asking if temporary changing Current Thread's Culture is good idea, I can only answer: no. It could be used if and only there is no other way to get things working. That is just because such switching is error prone. OK, you won't forget to change things back with the code Jordão (respect) gave you, but...
For now you have customers that want to create French invoices. I assuming that you want to use French date, number and currency formats. That's OK. But... What if in the future certain future would need to be printed out with other format, for example this originating German? Are you going to create some kind of ugly work-around?

I understand that it could be beyond your control (like Reporting Software could be 3rd party stand-alone solution and you could not control how it is handling ToString()) but if it is within your control, I would recommend feeding the data in right format in the first place. For example you could create some data transforming layer (DTO) and format data correctly (via ToString(IFormatProvider)). I know this is quite an effort but since you are asking about correct way to do things...

If we were in the same organization and I would do I18n code review, you could be sure that I would point out temporary changing culture as a defect. Usually there is a way to avoid this.