Why are the vertices of the faces not moved with transformation? Is there any way around this?

To use the Dave method, you would multiply all the x and y values by a constant, create your groups at the enlarged size, and apply a scaling transformation all of them to restore the correct size.

I’ve updated my code but now I am not really sure how to run it.

SKETCHUP_CONSOLE.clear
module Practice
  module Create_structure
    extend self
    # get model set up
    model = Sketchup.active_model # Open model
    manager = model.options 
    provider = manager["UnitsOptions"] 
    provider["LengthUnit"] = 2 # set default values to mm
    ents = model.active_entities
    layers_array = model.layers # create an array to store layer information
    
    @substrate = ents.add_group # add substrate group
    @via = ents.add_group # add group
    @insulator = ents.add_group # adds insulator to group
    @m1 = ents.add_group
    
    # this makes the chosen face orient up
    def face_up(face)
      face.reverse! unless face.normal.z == 1
      raise "face not on z plane" unless face.normal.z == 1
    end
    
    # this makes the chosen face orient down
    def face_down(face)
      face.reverse! unless face.normal.z == -1
      raise "face not on z plane" unless face.normal.z == -1
    end
    
    # this gets the highest point in a group. (not used yet)
    def get_highest_point_in_group(group)
      max_pt = entities.flat_map(&:vertices).map(&:position).map(&:z).max
      return max_pt
    end
    
    # set up initial variables
    @sub_t = 2.mm # thickness of substrate
    @via_t = 5.mm # thickness of via (i.e total height)
    @via_into = 1.mm # penetration of via into thing below it.
    @insulator_t = 2.mm # thickness of insulator layer
    @m1_t = 2.mm # thickness of m1 layer
    
    # This will be used to track the positions for adding edges off the previous layer
    # think of it like a hill and a valley that we are building on. 
    @highest_pt = 0.mm # current highest (like a hill)
    @highest_low_pt = 0.mm # this will be the lowest occupied point (like a valley)
    
    # add layer tags to layers_array
    substrate_tag = layers_array.add "substrate" 
    insulator_tag = layers_array.add "insulator" 
    via_tag = layers_array.add "via"
    m1_tag = layers_array.add "m1"
    
    # create substrate
    def create_substrate
      model = Sketchup.active_model # Open model
      model.start_operation('Create Substrate', true)
      @substrate = ents.add_group # add substrate group
      sub_face = @substrate.entities.add_face [0,0,0], [5.mm,0,0], [5.mm,8.mm,0], [0,8.mm,0] # add face
      face_down(subface) # make face down
      sub_face.pushpull @sub_t # extrude 10mm below (no need to update lowest point at this part)
      model.commit_operation
    end
    
    # now deposit the via layer
    def create_via
      model = Sketchup.active_model # Open model
      model.start_operation('Create Via', true)
      @via.name = "via" # name group (debugging)
      @via.entities.add_face([1.mm,1.mm,@highest_low_pt], [4.mm,1.mm,@highest_low_pt], [4.mm,3.mm,@highest_low_pt], [1.mm,3.mm,@highest_low_pt]) # adds face to via group
      @via.entities.add_face([1.mm,5.mm,@highest_low_pt], [4.mm,5.mm,@highest_low_pt], [4.mm,7.mm,@highest_low_pt], [1.mm,7.mm,@highest_low_pt]) # adds face to via group
      via_faces = @via.entities.grep(Sketchup::Face) # find the via faces 
      points = via_faces.flat_map(&:vertices).uniq.map(&:position) # get all the positions of the 
      # go up by 25 mm 
      vector = Geom::Vector3d.new(0, 0, @via_t) # the vector will be used to generate the thickness of this layer.
      top_face_pts=[] # empty array to push the via top face points into
      via_faces.each{|f| top_face_pts.push(f.vertices.map(&:position).map{|p| p.offset(vector)})} # creates points that will be used to make the top face.
      # There is probably a better way to do this with the push_pull function but I want to make a pyramidal shape later
      # adds the top face (later version this will possibly be an offset)
      top_face_pts.each{|pts| via.entities.add_face pts} # adds the top faces 
      # create a new edge for each point
      points.each{|pt| via.entities.add_edges(pt,pt.offset(vector))} # add egdges from the bottom face points to the top face points.
      @highest_pt += @via_t # update the highest point. (since we started at 0 we add the height of the extrusion
      via_edges = @via.entities.grep(Sketchup::Edge) # find all the via groups edges.
      via_edges.each(&:find_faces) # add faces using the edges found above.
      move_via = Geom::Transformation.new([0,0,-@via_into]) # transformation to move the via so that it penetrates the substrate.
      @via.transform! move_via # move the via into the substrate
      @highest_pt -= @via_into # update highest point
      @substrate=@via.trim(@substrate) # trim is non destructive method to remove the part of the substrate that the via penetrates into.
      model.commit_operation
    end
    
     # Add the insulator layer
    def create_insulator
      model = Sketchup.active_model # Open model
      model.start_operation('Create Insulator', true)
      ins_face = @insulator.entities.add_face [0,0,@highest_low_pt], [5.mm,0,@highest_low_pt], [5.mm,8.mm,@highest_low_pt], [0,8.mm,@highest_low_pt] # adds face to insulator
      face_up(ins_face) # makes the face upright
      ins_face.pushpull @insulator_t # pulls the face up to the correct thickness starting at the insulator layer.
      # update information
      calc_total_height = @insulator_t + @highest_low_pt
      if calc_total_height < @highest_pt # if this is the new highest low point
        @highest_low_pt = calc_total_height # update highest low point
      else # if this is now the highest point.
        @highest_low_pt = @highest_pt # update highest low point to old highest point
        @highest_pt = calc_total_height # overwrite highest point to be the new total height.
      end
      @insulator = @via.trim(@insulator) # trim the insulator using the via.
      model.commit_operation
    end
    
    # deposit m1 uniformly
    def Create_m1
      model.start_operation('Create Insulator', true)
      groups = ents.grep(Sketchup::Group)
      #faces = groups.flat_map{|g| g.entities.grep(Sketchup::Face)}
      positions = groups.flat_map{|g| g.entities.grep(Sketchup::Face)}.flat_map(&:vertices).map(&:position)
      top_points = positions.find_all{|p| p.z >= @highest_low_pt && p.z <= @highest_pt}
      top_points = top_points.map(&:to_a).uniq
      vector = Geom::Vector3d.new(0, 0, m1_t)
      top_points.each{|pt| @m1.entities.add_edges(pt,pt.offset(vector))}
      m1_edges = @m1.entities.grep(Sketchup::Edge)
      m1_edges.each(&:find_faces)
      model.commit_operation
    end
    
    #name groups
    @substrate.name = "substrate" # name substrate group
    @via.name = "via"
    @insulator.name = "insulator"
    @m1.name = "m1"
    
    # add layer tags
    @via.layer = via_tag
    @insulator.layer = insulator_tag
    @substrate.layer=substrate_tag 
    @m1.layer = m1_tag
    
    model.rendering_options['DisplayColorByLayer']= true # colors each layer
  end
end

Again … all executing code needs to be in methods.

Only constants local to your extension submodule should be outside of the methods.
They’ll get defined when the module loads, which is when SketchUp first starts.

All of the instance references (ie @vars) are things that can change each time commands are run, and therefore could be on different models, or different components, etc.
In many cases it is better programming to pass data into methods rather then have a bunch of @vars.
You can also either use an OpenStruct or a custom class, that holds this state data and promotes better reading code. Then you need only pass the reference to these data objects around.

SketchUp code is normally event driven by events caused by the user.
Common events would be clicking a menu item, clicking a toolbar button, activating a certain tool, etc.
For now (and testing) just create a menu items that fires off the creation of your model objects.
(Ie, it can serve temporarily as a placeholder for a GDS file import which is fired thru the menu and the import dialog.)

See: UI.menu module method and Sketchup::Menu class.

Just be sure to put the menu item creation with a block …

    if !@loaded
      @loaded = true
      UI.menu("Extensions").add_item("My Thing") { do_my_thing() }
    end

Lastly, do yourself and us a favor and write code more readable.

Try (in most cases) to put the explanation comment before the code that does the task.
(In code editors the comment color serves to help space the code into more readable chunks.)

Do not omit spaces, especially around operators.
Use the same spacing around {| as you would if using do |

Try and keep lines to 80 characters or less. ie, learn to indent long blocks and long methods calls.
(It is no fun to be forced to do horizontal scrolling in the forums.)

A long block:

# Create points that will be used to make the top face.
via_faces.each { |f|
  top_face_pts.push( f.vertices.map(&:position).map { |p| p.offset(vector) } )
}

A long method call:

# Add face to via group
@via.entities.add_face(
  [1.mm, 5.mm, @highest_low_pt], [4.mm, 5.mm, @highest_low_pt],
  [4.mm, 7.mm, @highest_low_pt], [1.mm, 7.mm, @highest_low_pt]
)

A long array definition …

# Points for via face
points = [
  [1.mm, 5.mm, @highest_low_pt], [4.mm, 5.mm, @highest_low_pt],
  [4.mm, 7.mm, @highest_low_pt], [1.mm, 7.mm, @highest_low_pt]
]

See this style guide: https://rubystyle.guide/
It is used by Rubocop and by the SketchUp extension GitHub - SketchUp/rubocop-sketchup: Rubocop cops for SketchUp - test against our Extension Warehouse technical requirements and other pitfalls
which the Trimble Extensibility team runs on all submissions to the Extension Warehouse.

Pay first attention to the format and spacing rules. (Not so much the use this method over that method stuff which is enforcing silly subjective preference.)

But the format rules really serve to reduce fatigue after a full day of staring at code.