If you prefer to encode the “last day” logic in the schedule itself, register three timer triggers (one per month-length group):
[FunctionName("MonthEnd30")]
public void Run30([TimerTrigger("0 0 0 30 4,6,9,11 *")] TimerInfo t, ILogger log) => RunMonthEnd(log);
[FunctionName("MonthEnd31")]
public void Run31([TimerTrigger("0 0 0 31 1,3,5,7,8,10,12 *")] TimerInfo t, ILogger log) => RunMonthEnd(log);
[FunctionName("MonthEnd28")]
public void Run28([TimerTrigger("0 0 0 28 2 *")] TimerInfo t, ILogger log) {
if (DateTime.UtcNow.AddDays(1).Day == 1) RunMonthEnd(log);
}
[FunctionName("MonthEnd29")]
public void Run29([TimerTrigger("0 0 0 29 2 *")] TimerInfo t, ILogger log) {
if (DateTime.UtcNow.AddDays(1).Day == 1) RunMonthEnd(log);
}Three or four functions for one logical job is a lot of boilerplate. The daily-with-runtime-check approach is usually cleaner.