\foreach cannot straddle table cells.
Here's a version that works.
\documentclass[letterpaper,oneside]{article}
\usepackage{array}
\ExplSyntaxOn
\NewDocumentCommand{\makerows}{mm}
{% #1 = number of lines, #2 = template
\cs_gset_protected:Nn __lownds_makerow:n { #2 }
\int_step_function:nN { #1 } __lownds_makerow:n
}
\cs_new_protected:Nn __lownds_makerow:n {} % initialize
\ExplSyntaxOff
\begin{document}
\noindent
\begin{tabular}{|c|w{c}{10em}|w{c}{10em}|}
\hline
\textbf{#} & \textbf{First name} & \textbf{Last name} \
\hline
\makerows{10}{#1 & \vphantom{$\bigg|$} & \ \hline}
\end{tabular}
\end{document}

No need to complicate things with \multirow. You can decide for different widths of the cells and the number of rows. The idea is that \int_step_function:nN delivers its result in one swoop, so the & and \\ are processed after the whole table contents has been generated. The setting of the auxiliary function \__lownds_makerow:n has to be global so it survives the implicit grouping of table cells.
A variation for deciding the starting point.
\documentclass[letterpaper,oneside]{article}
\usepackage{array}
\ExplSyntaxOn
\NewDocumentCommand{\makerows}{O{1}mm}
{% #1 = optional starting point, #2 = last number, #3 = template
\cs_gset_protected:Nn __lownds_makerow:n { #3 }
\int_step_function:nnnN { #1 } { 1 } { #2 } __lownds_makerow:n
}
\cs_new_protected:Nn __lownds_makerow:n {} % initialize
\ExplSyntaxOff
\begin{document}
\noindent
\begin{tabular}{|c|w{c}{10em}|w{c}{10em}|}
\hline
\textbf{#} & \textbf{First name} & \textbf{Last name} \
\hline
\makerows{10}{#1 & \vphantom{$\bigg|$} & \ \hline }
\end{tabular}
\noindent
\begin{tabular}{|c|w{c}{10em}|w{c}{10em}|}
\hline
\textbf{#} & \textbf{First name} & \textbf{Last name} \
\hline
\makerows[11]{20}{#1 & \vphantom{$\bigg|$} & \ \hline }
\end{tabular}
\end{document}

You might also consider longtable.