Robert Lewandowski put Bayern in front and they appeared to be cruising until Pierre-Emerick Aubameyang levelled in the 75th minute.
Arjen Robben came and went with an injury ending his brief involvement in the game, while Dortmund ended the game with 10 men after Kevin Kampl was sent off in extra-time.
The real drama was to unfold in the penalty shoot-out, though, with Philipp Lahm and Xabi Alonso both slipping and Mario Gotze’s penalty saved before Manuel Neuer sent his spot-kick against the crossbar with Dortmund netting only two of their three penalties to go through.
Dortmund wasted the first big chance of the game when Shinji Kagawa’s attempted pass to put Marco Reus through on goal was intercepted, and within seconds the ball was being played through to Lewandowski.
His first shot came back off the post, but the danger did not end there as the Polish forward stroked the ball into an unguarded goal from a tight angle to put Bayern in front.
The hosts were in full control of the game, holding possession and teasing Dortmund at times.
Lewandowski nearly had a second early in the second half when he was put through by Juan Bernat’s neat reverse pass, but the Pole struck the crossbar.
The ball this time fell back out for Thomas Muller, who tried to nudge it past Marcel Schmelzer, but the Dortmund defender made a block with his arm and was lucky not to see a penalty awarded.
Mitchell Langerak then made an excellent stop from Thiago Alcantara before the second big cheer of the night welcomed Robben back into action for the first time since early March.
Bayern were still in complete control, until Dortmund levelled out of nowhere.
In truth, they had thrown their tactics book out of the window from the moment Henrikh Mkhitaryan took the place of Kagawa, and it was from his cross that Aubameyang turned the ball in with Neuer’s clearance coming after the ball had already crossed his line.
Dortmund were suddenly not only back in the game, but on top of it too with Neuer forced into making fine saves, first from Mkhitaryan and then from Reus.
A worrying sight was to follow for Bayern when Robben limped off the field again, just 15 minutes after coming on, and in the end it was they more than Dortmund who were relieved to hear the full-time whistle.
Tiredness was evident on both sides in the extra 30 minutes and, apart from two Bastian Schweinsteiger headers, neither came close to deciding matters within 120 minutes, while Dortmund lost Kampl early in the second period to a second yellow card.
Lahm and Alonso both slipped as they sent their penalties off target and Mario Gotze saw his penalty saved by Langerak before Neuer sent Bayern’s fourth penalty against the crossbar, and Dortmund went through with Ilkay Gundogan and Sebastian Kehl on target.