If you want to store the eventual exit code in a cmd.exe variable for later use with exit, do not try to set ERRORLEVEL; instead, use a custom variable name; e.g., ec (for exit code):
# Execute in PowerShell
PS> cmd /v /c 'set "ec=3" & echo ... & exit /b !ec!'; $LASTEXITCODE
...
3
On the cmd.exe side:
- set "ec=3"sets variable- ecto value- 3
 - 
- Enclosing the name-value pair in - "..."isn't strictly necessary, but clearly delineates the end of the value, and allows use of values with embedded special characters such as- &.
 
- The variable's value can later be referenced as - %ec%(up-front expansion) or- !ec!(delayed expansion, if enabled via- setlocal enabledelayedexpansionor via command-line switch- /v- see- help setlocal)
 
 
- echo ...is a sample command representing further commands
 
- exit /b !ec!exits with the value of variable- ecas the exit code; note how the variable is of necessity referenced as- !ec!rather than- %ec%, via delayed expansion, because the variable was set as part of the same statement.
 - 
- Whether you use /b(exit the current batch file only) or not doesn't make a difference here; either way, theexitstatement in this case determines the process exit code of thecmdinstance.
 
The commands are sequenced (unconditionally executed one after the other), with the & operator, as part of a single statement.
On the PowerShell side:
- PowerShell's analog to - cmd.exe's- &is- ;, the statement separator - it allows you to place multiple statements on a single line.
 
- $LASTEXITCODEis the automatic PowerShell variable that contains the process exit code of the most recently executed external program, which in this case is- cmd.exe's, with value- 3.
 
Does anyone have any idea why the command SET %ERRORLEVEL% = 4 does not work?
To summarize the helpful information from the comments on the question:
- Fundamentally, do no try to set the dynamic (pseudo) environment variable - %ERRORLEVEL%- it is automatically maintained by- cmd.exeto reflect the most recent command's exit code - see the bottom section.
 
- Do not put spaces around - =in a- cmd.exevariable assignment:
 - 
- The space(s) before the =become part of the variable name.
- The space(s) after become part of the value.
 
- Do not enclose the target variable name in - %...%(unless you want to indirectly set a variable, via another variable whose value contains the name of the variable to assign to).
 
set %ERRORLEVEL% = 4
With %ERRORLEVEL% reflecting 0 at the start of your command sequence, the above assignment creates a variable literally named 0  (that is, the value of %ERRORLEVEL% followed by a single space), whose value is  4 (that is, a single space followed by 4).
Variables in cmd.exe:
Fundamentally, with the exceptions discussed below, variables in cmd.exe are all environment variables. 
Unlike in shells such as Bash and PowerShell, there is no separate namespace for shell-local variables that are not seen by child processes.
This has the following implications:
- Predefined persistent environment variables such as - %Path%share the same namespace as custom variables you define with the- SETcommand, so you have to be mindful of name collisions.
 
- Similarly, child processes you run from your - cmd.exesession / batch file inherit custom variables you've created in the session.
 - 
- Note that the use of setlocaldoes not change that;setlocalis acmd-internal scoping mechanism that allows you to control the lifetime of custom environment variables, by localizing them to the scope (batch file) in whichsetlocalwas called, and removing them via a subsequentendlocalcall or, implicitly, at the end of the enclosing batch file.
 
Custom variables are process-only environment variables that go out of scope with the cmd.exe process; persistent environment variable definitions must be created and modified via the registry, such as with the setx.exe utility.
In addition to the predefined (persistent) environment variables and custom (session-only) environment variables, cmd.exe maintains dynamic pseudo environment variables such as %ERRORLEVEL% and %RANDOM% (see list below):
Note: Strictly speaking, these dynamic variables are only enabled with the so-called command extensions turned on, but that is true by default (you can disable them with /E:OFF, but that is ill-advised).
Because these dynamic variables are not strictly part of the process environment that child processes inherit a copy of, they are not environment variables, even though help SET somewhat confusingly calls them dynamic environment variables.
You shouldn't (and cannot) modify these variables.
If you try, what really happens is that you shadow (override) these pseudo variables with real, custom environment variables, which by definition have static values.
Later code that relies on the variables by that name to have their usual, dynamic behavior can therefore malfunction.
The list of dynamic variables, as retrieved on Windows 10 via help SET (emphasis added):
If Command Extensions are enabled, then there are several dynamic
  environment variables that can be expanded but which don't show up in
  the list of variables displayed by SET.  These variable values are
  computed dynamically each time the value of the variable is expanded.
  If the user explicitly defines a variable with one of these names, then
  that definition will override the dynamic one described below:
- %CD%- expands to the current directory string.
- %DATE%- expands to current date using same format as DATE command.
- %TIME%- expands to current time using same format as TIME command.
- %RANDOM%- expands to a random decimal number between 0 and 32767.
- %ERRORLEVEL%- expands to the current ERRORLEVEL value
- %CMDEXTVERSION%- expands to the current Command Processor Extensions
  version number.
- %CMDCMDLINE%- expands to the original command line that invoked the
  Command Processor.
- %HIGHESTNUMANODENUMBER%- expands to the highest NUMA node number
  on this machine.