diff --git a/awx/ui/src/screens/Instances/InstanceAdd/InstanceAdd.js b/awx/ui/src/screens/Instances/InstanceAdd/InstanceAdd.js
new file mode 100644
index 0000000000..0fa6f1c630
--- /dev/null
+++ b/awx/ui/src/screens/Instances/InstanceAdd/InstanceAdd.js
@@ -0,0 +1,45 @@
+import React, { useState } from 'react';
+import { useHistory } from 'react-router-dom';
+import { Card, PageSection } from '@patternfly/react-core';
+import { InstancesAPI } from 'api';
+import InstanceForm from '../Shared/InstanceForm';
+
+function InstanceAdd() {
+ const history = useHistory();
+ const [formError, setFormError] = useState();
+ const handleSubmit = async (values) => {
+ const { instanceGroups, executionEnvironment } = values;
+ values.execution_environment = executionEnvironment?.id;
+
+ try {
+ const {
+ data: { id },
+ } = await InstancesAPI.create();
+
+ for (const group of instanceGroups) {
+ await InstancesAPI.associateInstanceGroup(id, group.id);
+ }
+ history.push(`/instances/${id}/details`);
+ } catch (err) {
+ setFormError(err);
+ }
+ };
+
+ const handleCancel = () => {
+ history.push('/instances');
+ };
+
+ return (
+
+
+
+
+
+ );
+}
+
+export default InstanceAdd;
diff --git a/awx/ui/src/screens/Instances/InstanceAdd/index.js b/awx/ui/src/screens/Instances/InstanceAdd/index.js
new file mode 100644
index 0000000000..c6ddcff5bc
--- /dev/null
+++ b/awx/ui/src/screens/Instances/InstanceAdd/index.js
@@ -0,0 +1 @@
+export { default } from './InstanceAdd';
diff --git a/awx/ui/src/screens/Instances/Shared/InstanceForm.js b/awx/ui/src/screens/Instances/Shared/InstanceForm.js
new file mode 100644
index 0000000000..6d706b39cf
--- /dev/null
+++ b/awx/ui/src/screens/Instances/Shared/InstanceForm.js
@@ -0,0 +1,130 @@
+import React from 'react';
+import { t } from '@lingui/macro';
+import { Formik, useField } from 'formik';
+import { Form, FormGroup, CardBody } from '@patternfly/react-core';
+import { FormColumnLayout } from 'components/FormLayout';
+import FormField, { FormSubmitError } from 'components/FormField';
+import FormActionGroup from 'components/FormActionGroup';
+import { required } from 'util/validators';
+import AnsibleSelect from 'components/AnsibleSelect';
+import {
+ ExecutionEnvironmentLookup,
+ InstanceGroupsLookup,
+} from 'components/Lookup';
+
+// This is hard coded because the API does not have the ability to send us a list that contains
+// only the types of instances that can be added. Control and Hybrid instances cannot be added.
+
+const INSTANCE_TYPES = [
+ { id: 2, name: t`Execution`, value: 'execution' },
+ { id: 3, name: t`Hop`, value: 'hop' },
+];
+
+function InstanceFormFields() {
+ const [instanceType, , instanceTypeHelpers] = useField('type');
+ const [instanceGroupsField, , instanceGroupsHelpers] =
+ useField('instanceGroups');
+ const [
+ executionEnvironmentField,
+ executionEnvironmentMeta,
+ executionEnvironmentHelpers,
+ ] = useField('executionEnvironment');
+ return (
+ <>
+
+
+
+ ({
+ key: type.id,
+ value: type.value,
+ label: type.name,
+ isDisabled: false,
+ }))}
+ value={instanceType.value}
+ onChange={(e, opt) => {
+ instanceTypeHelpers.setValue(opt);
+ }}
+ />
+
+ {
+ instanceGroupsHelpers.setValue(value);
+ }}
+ fieldName="instanceGroups"
+ />
+ executionEnvironmentHelpers.setTouched()}
+ value={executionEnvironmentField.value}
+ onChange={(value) => {
+ executionEnvironmentHelpers.setValue(value);
+ }}
+ />
+ >
+ );
+}
+
+function InstanceForm({
+ instance = {},
+ submitError,
+ handleCancel,
+ handleSubmit,
+}) {
+ return (
+
+ {
+ handleSubmit(values);
+ }}
+ >
+ {(formik) => (
+
+ )}
+
+
+ );
+}
+
+export default InstanceForm;