What is the best way to create a wallet with a blank keypool then add keys to it? The scenario is this:

User has a cold storage wallet and will import their cold storage xpub into Bitcoin Core using getdescriptorinfo and importmulti. User wants to only use the keys associated with the imported xpub as a watch-only wallet and wants to exclude all existing keys in the wallet so as to avoid getting an address not associated with their xpub when calling getnewaddress or getrawchangeaddress.

Would the best way to go about this be to create a blank wallet first with:

createwallet "Cold Storage", disable_private_keys: true, blank: true?

Or is there a simpler way to specify which addresses are generated when calling getnewaddress?
Yes, you will want to create a new wallet and specify true for disable_private_keys. It is not necessary to specify blank as disable_private_keys will already ensure that no private keys will be generated or imported and as such, the newly created wallet will be blank anyways.

You can then import xpubs, public keys, and addresses into Bitcoin Core using importmulti. By setting "keypool":true for every public key, the keys will be added to Bitcoin Core's keypool and allow you to use getnewaddress to get addresses. Additionally, for change keys, specifying "internal":true will mark change keys as change and allow you to use getrawchangeaddress, fundrawtransaction, and walletcreatefundedpsbt more easily.

However, be careful when importing an xpub that you use the correct derivation paths. If the child keys that you will eventually be using are derived using hardened derivation, you cannot import an xpub as hardened child keys can only be derived from the master private key. In that case, you will have to import each key by itself, which can also be done using importmulti.