A way without capture groups or lookarounds, use a word-boundary:
^(?:\b:?[A-Z]{1,3}[0-9]{1,10}){1,2}$
demo
The word-boundary can't succeed between the start of the string and a colon nor between a digit and a letter, but it does between a digit and a colon or between the start of the string and a letter.
Obviously, it's also possible to do it like that for the same kind of reasons:
^(?:[A-Z]{1,3}[0-9]{1,10}:?\b){1,2}$
(You win one step more with this one, YAY!)
test cases (first pattern):
with :A2
It fails because \b
fails between the start of the string and a non-word character (the colon).
with A2:
It fails because there's no colon at the end of the sub-pattern (that is not repeated in this case).
with A2:A2
The pattern succeeds. \b
succeeds because the first time it is between the start of the string and a letter (a word character), and the second time because it is between a digit (a word character too) and a colon (a non-word character).