If I wanted to make a few services (like a website and imap) only available via a .onion address, can someone scan the .onion address to see all the open services that point to it? If I don't want them discoverable, but still only reachable via Tor, should I make something like a VPN connection listen on the .onion address and then connect to that? In essence, if I make a hidden service available to a .onion address, is it the same as opening a port on my firewall and pointing it to the server (albeit only seen on the Tor network)?
Yes it can be scanned. People can scan for services as they do on clearnet, by routing their scanner through tor pointing it at the domain. A onion domain is not to be considered a secret.
Im not sure about your vpn question, but you can look at this answer here for some hints on how to make the domain private:
If you don't enable client authentication on the Tor hidden service, you only have the existence of the .onion name to protect your services from scanning.
It is generally difficult for someone to guess the .onion address, however, once they've found it there's no way to stop them having it unless you move to a different one (which inconveniences your real users).
If you're unhappy with the strength of the authentication on your services, you can require client authentication in Tor. This does impose an additional admin overhead on legitimate users (key generation, distribution etc).
Your analogy with a tunnel is not too bad, but you are not routing all e.g. foobar123foobar.onion traffic to your machine, only the specific ports you choose. If you expose an ssh server on port 22, that doesn't mean someone can scan foobar123foobar.onion and find out that you also have a web server on port 80.
Making a VPN connection adds no value, TOR is your VPN. If you have an adversary that can find out that it's your node connecting to TOR and listening to that .onion address, they can probably find out where your VPN is going too.
Also, you don't need to have the services accessible on any public IP number, only on localhost. So if you expose an ssh server on port 22, you can configure sshd to only listen on localhost. People scanning your machine's public IP number (if it has one) will see a closed port 22.
The machine doesn't even need to have a public IP number, it can be deep behind a NAT somewhere (or be connection through a VPN!), as long as it is able to create a connection to some TOR node. And as mirimir says, if you have several services on one machine, they can all get a .onion address each if you wish.
I hope this clears things up.