diff --git a/api/canvas_classes.py b/api/canvas_classes.py
index 69b0de55f588b86dbb68e506bb666d4d99da2b31..2ad19a6f495397e82c46629cc2941ea228d08e02 100644
--- a/api/canvas_classes.py
+++ b/api/canvas_classes.py
@@ -3,8 +3,10 @@ from typing import ClassVar, Dict, Iterable, List, Optional, Union
 from canvasapi import Canvas
 from canvasapi.assignment import Assignment, AssignmentGroup
 from canvasapi.course import Course
+from canvasapi.exceptions import ResourceDoesNotExist
 from canvasapi.group import Group, GroupCategory
 from canvasapi.quiz import QuizSubmission, QuizSubmissionQuestion
+from canvasapi.section import Section
 from canvasapi.submission import Submission
 from canvasapi.user import User
 
@@ -631,6 +633,90 @@ class CanvasAssignmentGroup:
 """
 
 
+class CanvasCourseSection:
+    canvas_section: Section
+
+    def __init__(self, section: Section, course: "CanvasCourse"):
+        super().__init__()
+        self.canvas_section = section
+        self.course = course
+
+    def get_students(self) -> List[CanvasUser]:
+        # course_students: List[CanvasUser] = None
+        course_students: List[CanvasUser] = self.course.get_students()
+        enrollments = self.canvas_section.get_enrollments()
+        students: List[CanvasUser] = []
+        for enrollment in enrollments:
+            if enrollment.role == 'StudentEnrollment':
+                user_json = enrollment.user
+                """
+                try:
+                    user = self.course.canvas_course.get_user(int(user_json['sis_user_id']),
+                                                              'sis_user_id')  # TODO re-encapsulate
+                    students.append(CanvasUser(user))
+                except ResourceDoesNotExist:
+                    if course_students is None:
+                        course_students = self.course.get_students()
+                    candidates = list(filter(lambda c: c.get_username() == user_json['login_id'], course_students))
+                    if len(candidates) == 1:
+                        students.append(candidates[0])
+                        print(f'WARNING: Canvas user {user_json["name"]} located indirectly '
+                              f'(could not directly locate Canvas REST resource). Adding to list.')
+                    elif len(candidates) > 1:
+                        print(f'ERROR: Indirectly found {len(candidates)} canvas users for {user_json["name"]} '
+                              f'using the "unique" login {user_json["login_id"]}. Adding none of them. '
+                              f'(Could not directly locate Canvas REST resource)')
+                    else:
+                        print(f'ERROR: Could not locate canvas user: {user_json["name"]}')
+                    """
+                candidates = list(filter(lambda c: c.get_username() == user_json['login_id'], course_students))
+                if len(candidates) == 1:
+                    students.append(candidates[0])
+                elif len(candidates) > 1:
+                    print(f'ERROR: Indirectly found {len(candidates)} canvas users for {user_json["name"]} '
+                          f'using the "unique" login {user_json["login_id"]}. Adding none of them.')
+                else:
+                    print(f'ERROR: Could not locate canvas user: {user_json["name"]}')
+        return students
+
+    def __repr__(self) -> str:
+        return self.canvas_section.name
+
+
+"""
+{
+  // The unique identifier for the section.
+  "id": 1,
+  // The name of the section.
+  "name": "Section A",
+  // The sis id of the section. This field is only included if the user has
+  // permission to view SIS information.
+  "sis_section_id": "s34643",
+  // Optional: The integration ID of the section. This field is only included if
+  // the user has permission to view SIS information.
+  "integration_id": "3452342345",
+  // The unique identifier for the SIS import if created through SIS. This field
+  // is only included if the user has permission to manage SIS information.
+  "sis_import_id": 47,
+  // The unique Canvas identifier for the course in which the section belongs
+  "course_id": 7,
+  // The unique SIS identifier for the course in which the section belongs. This
+  // field is only included if the user has permission to view SIS information.
+  "sis_course_id": "7",
+  // the start date for the section, if applicable
+  "start_at": "2012-06-01T00:00:00-06:00",
+  // the end date for the section, if applicable
+  "end_at": null,
+  // Restrict user enrollments to the start and end dates of the section
+  "restrict_enrollments_to_section_dates": null,
+  // The unique identifier of the original course of a cross-listed section
+  "nonxlist_course_id": null,
+  // optional: the total number of active and invited students in the section
+  "total_students": 13
+}
+"""
+
+
 #   THE COURSE ITSELF
 
 
@@ -694,6 +780,13 @@ class CanvasCourse:
             assignments.append(CanvasAssignment(assignment))
         return assignments
 
+    def get_sections(self):
+        canvas_sections: Iterable[Section] = self.canvas_course.get_sections()
+        sections: List[CanvasCourseSection] = []
+        for section in canvas_sections:
+            sections.append(CanvasCourseSection(section, self))
+        return sections
+
     def __repr__(self) -> str:
         return f'{self.canvas_course.course_code}: {self.canvas_course.name}'
 
diff --git a/create_canvas_roster.py b/create_canvas_roster.py
new file mode 100644
index 0000000000000000000000000000000000000000..54928b0b74fa9c75a8d60da756f6dbc43630e918
--- /dev/null
+++ b/create_canvas_roster.py
@@ -0,0 +1,19 @@
+import csv
+
+from api.canvas_classes import *
+
+if __name__ == '__main__':
+    canvas_course_id: int = int(input("What is the Canvas course ID? "))
+    filename: str = input("What file do you want to save the roster to? ")
+    course: CanvasCourse = CanvasCourse(canvas_course_id)
+    # students: List[CanvasUser] = course.get_students()
+    fields = ['SortableName', 'ReadableName', 'Section', 'NUID', 'Username', 'Email']
+    with open(filename, mode='w') as csv_file:
+        # writer = csv.DictWriter(csv_file, fieldnames=fields)
+        # writer.writeheader()
+        writer = csv.writer(csv_file)
+        writer.writerow(fields)
+        for section in list(sorted(course.get_sections(), key=lambda s: s.__repr__())):
+            for student in list(sorted(section.get_students(), key=lambda s: s.get_sortable_name())):
+                writer.writerow([student.get_sortable_name(), student.get_name(), section,
+                                 student.get_nuid(), student.get_username(), student.get_email()])