Initialization Vector Properties This is a multi-layer problem, and I want to take you through the whole thing rather than just asking about IVs. Bear with me. Also, all code below is in Common Lisp because that's what I'm using, but if there are any complaints, I'll edit to put up pseudocode. 

I'm trying to put together a procedure which will produce hard-to-guess session tokens for use in a server implementation. What I've got so far is

(ql:quickload (list :ironclad :cl-base64))
(setf *random-state* (make-random-state t))

(defmethod sha256 ((message integer))
  (ironclad:digest-sequence
   :sha256 (ironclad:integer-to-octets message)))

(let ((cipher (ironclad:make-cipher :aes :mode :ecb :key (sha256 (random (expt 2 1024)))))
      (counter (random (expt 2 512))))
  (defun new-session-token ()
    (let ((raw (ironclad:integer-to-octets (incf counter))))
      (ironclad:encrypt-in-place cipher raw)
      (cl-base64:usb8-array-to-base64-string raw :uri t))))


A side question here: what vulnerabilities does this approach have over using AES in counter mode? 

The way to use this in counter-mode would be to change the line that defines cipherto :

...
(let ((cipher (ironclad:make-cipher 
           :aes :key (sha256 (random (expt 2 1024))) 
           :mode :ctr :initialization-vector [16-element vector goes here])
...


My questions are as follows:


What properties does that IV need? The only one I've seen mentioned is uniqueness, which implies that it doesn't have to be secret. 
Do I need to generate a new one each time I generate a session token?
What's a good way of generating one?


EDIT:

As per comment by Stephen Touset, Ironclad supports CSPRNG out of the box (more specifically, it supports Fortuna. For my purposes, the above functions can be replaced with

(let ((prng (ironclad:make-prng :fortuna)))
  (defun new-session-token ()
    (cl-base64:usb8-array-to-base64-string
     (ironclad:random-data 32 prng) :uri t)))


ironclad:make-prng seems inconsistent in runtime; I've timed between 8 and 70 seconds.
Don't use ECB mode. ECB mode is fundamentally unsound except in very specific use-cases.
IVs have different requirements depending on the mode. In CBC mode, the requirement is "unpredictability", which is typically construed to mean "cryptographically random". CTR mode only requires that the IV be a nonce (e.g., is guaranteed to be unique).
A new IV must be used for each and every use of a key for an encryption operation.
IVs do not need to be kept secret. They are considered public information.
SHA-256(rand) is not a cryptographically strong random number generator. Hash functions evenly distribute existing entropy. If your underlying random source is predictable (e.g., has low entropy), hashing it does not improve that. Use a real CSPRNG.
In almost all contexts, you want to use authenticated encryption. GCM mode, EAX mode, and CCM mode satisfy this requirement. Non-authenticated encryption modes can allow attackers to modify ciphertexts in ways that have well-defined effects on the underlying plaintext.


If you want to generate hard-to-guess symmetric keys, just use a cryptographically-strong source of random numbers. If you need to establish these keys over an insecure channel, use something like the Diffie-Hellman key exchange.

You appear to be very new to cryptography. In general, I would strongly caution against working with ciphers at this low a level – the details are extremely important, and they are very difficult for amateurs to get right. The fact that you have already made multiple serious mistakes in fewer than 20 lines of code should give you an indication of just how difficult it actually is.

Unfortunately, I am not personally familiar with any higher-level abstractions for this sort of stuff for Common Lisp.