Merge pull request #1237 from dod-ccpo/management-group-sketches
Management group sketches
This commit is contained in:
commit
095120a806
1
Pipfile
1
Pipfile
@ -29,6 +29,7 @@ azure-mgmt-subscription = "*"
|
|||||||
azure-graphrbac = "*"
|
azure-graphrbac = "*"
|
||||||
msrestazure = "*"
|
msrestazure = "*"
|
||||||
azure-mgmt-authorization = "*"
|
azure-mgmt-authorization = "*"
|
||||||
|
azure-mgmt-managementgroups = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
bandit = "*"
|
bandit = "*"
|
||||||
|
177
Pipfile.lock
generated
177
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "6d2ab855267daac877ae7464de9dba5b62b7d89288992f87d8fc6ff0c0d2520f"
|
"sha256": "c2b19c436646705ea3bf4df8c35c2833083f048da37fc619e66f7236153607c5"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -39,11 +39,11 @@
|
|||||||
},
|
},
|
||||||
"apache-libcloud": {
|
"apache-libcloud": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:201751f738109f25d58dcdfb5804e17216e0dc8f68b522e9e26ac16e0b9ff2ea",
|
"sha256:9bc5cd5c32151bb7a04a7c7de0be9b4a4b8271e348ac91dd79eaaeeae627115f",
|
||||||
"sha256:40215db1bd489d17dc1abfdb289d7f035313c7297b6a7462c79d8287cbbeae91"
|
"sha256:fcc165f2cc2db9a379c6d3a17b3beb9081bb64ba5c0bf7bbb58da864810092f0"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.6.0"
|
"version": "==2.6.1"
|
||||||
},
|
},
|
||||||
"azure-common": {
|
"azure-common": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -68,6 +68,14 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.60.0"
|
"version": "==0.60.0"
|
||||||
},
|
},
|
||||||
|
"azure-mgmt-managementgroups": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3d5237947458dc94b4a392141174b1c1258d26611241ee104e9006d1d798f682",
|
||||||
|
"sha256:8194ee6274df865eccd1ed9d385ea625aeba9b8058b9e4fdf547f5207271a775"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.2.0"
|
||||||
|
},
|
||||||
"azure-mgmt-subscription": {
|
"azure-mgmt-subscription": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:504b4c42ba859070c3c50637ec07ca36aca600e613fcccaa398db22822fe21f1",
|
"sha256:504b4c42ba859070c3c50637ec07ca36aca600e613fcccaa398db22822fe21f1",
|
||||||
@ -117,10 +125,10 @@
|
|||||||
},
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50",
|
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
|
||||||
"sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"
|
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
|
||||||
],
|
],
|
||||||
"version": "==2019.9.11"
|
"version": "==2019.11.28"
|
||||||
},
|
},
|
||||||
"cffi": {
|
"cffi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -248,10 +256,10 @@
|
|||||||
},
|
},
|
||||||
"importlib-metadata": {
|
"importlib-metadata": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26",
|
"sha256:b044f07694ef14a6683b097ba56bd081dbc7cdc7c7fe46011e499dfecc082f21",
|
||||||
"sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"
|
"sha256:e6ac600a142cf2db707b1998382cc7fc3b02befb7273876e01b8ad10b9652742"
|
||||||
],
|
],
|
||||||
"version": "==0.23"
|
"version": "==1.1.0"
|
||||||
},
|
},
|
||||||
"isodate": {
|
"isodate": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -330,10 +338,10 @@
|
|||||||
},
|
},
|
||||||
"more-itertools": {
|
"more-itertools": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832",
|
"sha256:53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2",
|
||||||
"sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"
|
"sha256:a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45"
|
||||||
],
|
],
|
||||||
"version": "==7.2.0"
|
"version": "==8.0.0"
|
||||||
},
|
},
|
||||||
"msrest": {
|
"msrest": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -422,11 +430,11 @@
|
|||||||
},
|
},
|
||||||
"pyopenssl": {
|
"pyopenssl": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:aeca66338f6de19d1aa46ed634c3b9ae519a64b458f8468aec688e7e3c20f200",
|
"sha256:621880965a720b8ece2f1b2f54ea2071966ab00e2970ad2ce11d596102063504",
|
||||||
"sha256:c727930ad54b10fc157015014b666f2d8b41f70c0d03e83ab67624fd3dd5d1e6"
|
"sha256:9a24494b2602aaf402be5c9e30a0b82d4a5c67528fe8fb475e3f3bc00dd69507"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==19.0.0"
|
"version": "==19.1.0"
|
||||||
},
|
},
|
||||||
"python-dateutil": {
|
"python-dateutil": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -459,22 +467,20 @@
|
|||||||
},
|
},
|
||||||
"pyyaml": {
|
"pyyaml": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
|
"sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc",
|
||||||
"sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
|
"sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803",
|
||||||
"sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
|
"sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc",
|
||||||
"sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
|
"sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15",
|
||||||
"sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
|
"sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075",
|
||||||
"sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
|
"sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd",
|
||||||
"sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
|
"sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31",
|
||||||
"sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
|
"sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f",
|
||||||
"sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
|
"sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c",
|
||||||
"sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
|
"sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04",
|
||||||
"sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
|
"sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4"
|
||||||
"sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
|
|
||||||
"sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
|
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==5.1.2"
|
"version": "==5.2"
|
||||||
},
|
},
|
||||||
"redis": {
|
"redis": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -650,10 +656,10 @@
|
|||||||
},
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50",
|
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
|
||||||
"sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"
|
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
|
||||||
],
|
],
|
||||||
"version": "==2019.9.11"
|
"version": "==2019.11.28"
|
||||||
},
|
},
|
||||||
"chardet": {
|
"chardet": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -785,10 +791,10 @@
|
|||||||
},
|
},
|
||||||
"importlib-metadata": {
|
"importlib-metadata": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26",
|
"sha256:b044f07694ef14a6683b097ba56bd081dbc7cdc7c7fe46011e499dfecc082f21",
|
||||||
"sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"
|
"sha256:e6ac600a142cf2db707b1998382cc7fc3b02befb7273876e01b8ad10b9652742"
|
||||||
],
|
],
|
||||||
"version": "==0.23"
|
"version": "==1.1.0"
|
||||||
},
|
},
|
||||||
"ipdb": {
|
"ipdb": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -799,11 +805,11 @@
|
|||||||
},
|
},
|
||||||
"ipython": {
|
"ipython": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:dfd303b270b7b5232b3d08bd30ec6fd685d8a58cabd54055e3d69d8f029f7280",
|
"sha256:c66c7e27239855828a764b1e8fc72c24a6f4498a2637572094a78c5551fb9d51",
|
||||||
"sha256:ed7ebe1cba899c1c3ccad6f7f1c2d2369464cc77dba8eebc65e2043e19cda995"
|
"sha256:f186b01b36609e0c5d0de27c7ef8e80c990c70478f8c880863004b3489a9030e"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==7.9.0"
|
"version": "==7.10.1"
|
||||||
},
|
},
|
||||||
"ipython-genutils": {
|
"ipython-genutils": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -908,30 +914,30 @@
|
|||||||
},
|
},
|
||||||
"more-itertools": {
|
"more-itertools": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832",
|
"sha256:53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2",
|
||||||
"sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"
|
"sha256:a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45"
|
||||||
],
|
],
|
||||||
"version": "==7.2.0"
|
"version": "==8.0.0"
|
||||||
},
|
},
|
||||||
"mypy": {
|
"mypy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:1521c186a3d200c399bd5573c828ea2db1362af7209b2adb1bb8532cea2fb36f",
|
"sha256:02d9bdd3398b636723ecb6c5cfe9773025a9ab7f34612c1cde5c7f2292e2d768",
|
||||||
"sha256:31a046ab040a84a0fc38bc93694876398e62bc9f35eca8ccbf6418b7297f4c00",
|
"sha256:088f758a50af31cf8b42688118077292370c90c89232c783ba7979f39ea16646",
|
||||||
"sha256:3b1a411909c84b2ae9b8283b58b48541654b918e8513c20a400bb946aa9111ae",
|
"sha256:28e9fbc96d13397a7ddb7fad7b14f373f91b5cff538e0772e77c270468df083c",
|
||||||
"sha256:48c8bc99380575deb39f5d3400ebb6a8a1cb5cc669bbba4d3bb30f904e0a0e7d",
|
"sha256:30e123b24931f02c5d99307406658ac8f9cd6746f0d45a3dcac2fe5fbdd60939",
|
||||||
"sha256:540c9caa57a22d0d5d3c69047cc9dd0094d49782603eb03069821b41f9e970e9",
|
"sha256:3294821b5840d51a3cd7a2bb63b40fc3f901f6a3cfb3c6046570749c4c7ef279",
|
||||||
"sha256:672e418425d957e276c291930a3921b4a6413204f53fe7c37cad7bc57b9a3391",
|
"sha256:41696a7d912ce16fdc7c141d87e8db5144d4be664a0c699a2b417d393994b0c2",
|
||||||
"sha256:6ed3b9b3fdc7193ea7aca6f3c20549b377a56f28769783a8f27191903a54170f",
|
"sha256:4f42675fa278f3913340bb8c3371d191319704437758d7c4a8440346c293ecb2",
|
||||||
"sha256:9371290aa2cad5ad133e4cdc43892778efd13293406f7340b9ffe99d5ec7c1d9",
|
"sha256:54d205ccce6ed930a8a2ccf48404896d456e8b87812e491cb907a355b1a9c640",
|
||||||
"sha256:ace6ac1d0f87d4072f05b5468a084a45b4eda970e4d26704f201e06d47ab2990",
|
"sha256:6992133c95a2847d309b4b0c899d7054adc60481df6f6b52bb7dee3d5fd157f7",
|
||||||
"sha256:b428f883d2b3fe1d052c630642cc6afddd07d5cd7873da948644508be3b9d4a7",
|
"sha256:6ecbd0e8e371333027abca0922b0c2c632a5b4739a0c61ffbd0733391e39144c",
|
||||||
"sha256:d5bf0e6ec8ba346a2cf35cb55bf4adfddbc6b6576fcc9e10863daa523e418dbb",
|
"sha256:83fa87f556e60782c0fc3df1b37b7b4a840314ba1ac27f3e1a1e10cb37c89c17",
|
||||||
"sha256:d7574e283f83c08501607586b3167728c58e8442947e027d2d4c7dcd6d82f453",
|
"sha256:c87ac7233c629f305602f563db07f5221950fe34fe30af072ac838fa85395f78",
|
||||||
"sha256:dc889c84241a857c263a2b1cd1121507db7d5b5f5e87e77147097230f374d10b",
|
"sha256:de9ec8dba773b78c49e7bec9a35c9b6fc5235682ad1fc2105752ae7c22f4b931",
|
||||||
"sha256:f4748697b349f373002656bf32fede706a0e713d67bfdcf04edf39b1f61d46eb"
|
"sha256:f385a0accf353ca1bca4bbf473b9d83ed18d923fdb809d3a70a385da23e25b6a"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.740"
|
"version": "==0.750"
|
||||||
},
|
},
|
||||||
"mypy-extensions": {
|
"mypy-extensions": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -961,10 +967,10 @@
|
|||||||
},
|
},
|
||||||
"pbr": {
|
"pbr": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2c8e420cd4ed4cec4e7999ee47409e876af575d4c35a45840d59e8b5f3155ab8",
|
"sha256:139d2625547dbfa5fb0b81daebb39601c478c21956dc57e2e07b74450a8c506b",
|
||||||
"sha256:b32c8ccaac7b1a20c0ce00ce317642e6cf231cf038f9875e0280e28af5bf7ac9"
|
"sha256:61aa52a0f18b71c5cc58232d2cf8f8d09cd67fcad60b742a60124cb8d6951488"
|
||||||
],
|
],
|
||||||
"version": "==5.4.3"
|
"version": "==5.4.4"
|
||||||
},
|
},
|
||||||
"pexpect": {
|
"pexpect": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -983,18 +989,17 @@
|
|||||||
},
|
},
|
||||||
"pluggy": {
|
"pluggy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6",
|
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
|
||||||
"sha256:fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"
|
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
|
||||||
],
|
],
|
||||||
"version": "==0.13.0"
|
"version": "==0.13.1"
|
||||||
},
|
},
|
||||||
"prompt-toolkit": {
|
"prompt-toolkit": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:46642344ce457641f28fc9d1c9ca939b63dadf8df128b86f1b9860e59c73a5e4",
|
"sha256:0278d2f51b5ceba6ea8da39f76d15684e84c996b325475f6e5720edc584326a7",
|
||||||
"sha256:e7f8af9e3d70f514373bf41aa51bc33af12a6db3f71461ea47fea985defb2c31",
|
"sha256:63daee79aa8366c8f1c637f1a4876b890da5fc92a19ebd2f7080ebacb901e990"
|
||||||
"sha256:f15af68f66e664eaa559d4ac8a928111eebd5feda0c11738b5998045224829db"
|
|
||||||
],
|
],
|
||||||
"version": "==2.0.10"
|
"version": "==3.0.2"
|
||||||
},
|
},
|
||||||
"ptyprocess": {
|
"ptyprocess": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -1012,10 +1017,10 @@
|
|||||||
},
|
},
|
||||||
"pygments": {
|
"pygments": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127",
|
"sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b",
|
||||||
"sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"
|
"sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"
|
||||||
],
|
],
|
||||||
"version": "==2.4.2"
|
"version": "==2.5.2"
|
||||||
},
|
},
|
||||||
"pylint": {
|
"pylint": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -1058,11 +1063,11 @@
|
|||||||
},
|
},
|
||||||
"pytest-mock": {
|
"pytest-mock": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b3514caac35fe3f05555923eabd9546abce11571cc2ddf7d8615959d04f2c89e",
|
"sha256:96a0cebc66e09930be2a15b03333d90b59584d3fb011924f81c14b50ee0afbba",
|
||||||
"sha256:ea502c3891599c26243a3a847ccf0b1d20556678c528f86c98e3cd6d40c5cf11"
|
"sha256:e5381be2608e49547f5e47633c5f81241ebf6206d17ce516a7a18d5a917e3859"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.11.2"
|
"version": "==1.12.1"
|
||||||
},
|
},
|
||||||
"pytest-watch": {
|
"pytest-watch": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -1080,22 +1085,20 @@
|
|||||||
},
|
},
|
||||||
"pyyaml": {
|
"pyyaml": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
|
"sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc",
|
||||||
"sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
|
"sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803",
|
||||||
"sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
|
"sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc",
|
||||||
"sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
|
"sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15",
|
||||||
"sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
|
"sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075",
|
||||||
"sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
|
"sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd",
|
||||||
"sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
|
"sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31",
|
||||||
"sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
|
"sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f",
|
||||||
"sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
|
"sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c",
|
||||||
"sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
|
"sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04",
|
||||||
"sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
|
"sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4"
|
||||||
"sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
|
|
||||||
"sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
|
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==5.1.2"
|
"version": "==5.2"
|
||||||
},
|
},
|
||||||
"regex": {
|
"regex": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -3,6 +3,7 @@ import re
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from atst.models.user import User
|
from atst.models.user import User
|
||||||
|
from atst.models.application import Application
|
||||||
from atst.models.environment import Environment
|
from atst.models.environment import Environment
|
||||||
from atst.models.environment_role import EnvironmentRole
|
from atst.models.environment_role import EnvironmentRole
|
||||||
|
|
||||||
@ -399,13 +400,14 @@ REMOTE_ROOT_ROLE_DEF_ID = "/providers/Microsoft.Authorization/roleDefinitions/00
|
|||||||
|
|
||||||
class AzureSDKProvider(object):
|
class AzureSDKProvider(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
from azure.mgmt import subscription, authorization
|
from azure.mgmt import subscription, authorization, managementgroups
|
||||||
import azure.graphrbac as graphrbac
|
import azure.graphrbac as graphrbac
|
||||||
import azure.common.credentials as credentials
|
import azure.common.credentials as credentials
|
||||||
from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD
|
from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD
|
||||||
|
|
||||||
self.subscription = subscription
|
self.subscription = subscription
|
||||||
self.authorization = authorization
|
self.authorization = authorization
|
||||||
|
self.managementgroups = managementgroups
|
||||||
self.graphrbac = graphrbac
|
self.graphrbac = graphrbac
|
||||||
self.credentials = credentials
|
self.credentials = credentials
|
||||||
# may change to a JEDI cloud
|
# may change to a JEDI cloud
|
||||||
@ -428,42 +430,23 @@ class AzureCloudProvider(CloudProviderInterface):
|
|||||||
def create_environment(
|
def create_environment(
|
||||||
self, auth_credentials: Dict, user: User, environment: Environment
|
self, auth_credentials: Dict, user: User, environment: Environment
|
||||||
):
|
):
|
||||||
|
# since this operation would only occur within a tenant, should we source the tenant
|
||||||
|
# via lookup from environment once we've created the portfolio csp data schema
|
||||||
|
# something like this:
|
||||||
|
# environment_tenant = environment.application.portfolio.csp_data.get('tenant_id', None)
|
||||||
|
# though we'd probably source the whole credentials for these calls from the portfolio csp
|
||||||
|
# data, as it would have to be where we store the creds for the at-at user within the portfolio tenant
|
||||||
|
# credentials = self._get_credential_obj(environment.application.portfolio.csp_data.get_creds())
|
||||||
credentials = self._get_credential_obj(self._root_creds)
|
credentials = self._get_credential_obj(self._root_creds)
|
||||||
sub_client = self.sdk.subscription.SubscriptionClient(credentials)
|
|
||||||
|
|
||||||
display_name = f"{environment.application.name}_{environment.name}_{environment.id}" # proposed format
|
display_name = f"{environment.application.name}_{environment.name}_{environment.id}" # proposed format
|
||||||
|
management_group_id = "?" # management group id chained from environment
|
||||||
|
parent_id = "?" # from environment.application
|
||||||
|
|
||||||
billing_profile_id = "?" # something chained from environment?
|
management_group = self._create_management_group(
|
||||||
sku_id = AZURE_SKU_ID
|
credentials, management_group_id, display_name, parent_id,
|
||||||
# we want to set AT-AT as an owner here
|
|
||||||
# we could potentially associate subscriptions with "management groups" per DOD component
|
|
||||||
body = self.sdk.subscription.models.ModernSubscriptionCreationParameters(
|
|
||||||
display_name,
|
|
||||||
billing_profile_id,
|
|
||||||
sku_id,
|
|
||||||
# owner=<AdPrincipal: for AT-AT user>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# These 2 seem like something that might be worthwhile to allow tiebacks to
|
return management_group
|
||||||
# TOs filed for the environment
|
|
||||||
billing_account_name = "?"
|
|
||||||
invoice_section_name = "?"
|
|
||||||
# We may also want to create billing sections in the enrollment account
|
|
||||||
sub_creation_operation = sub_client.subscription_factory.create_subscription(
|
|
||||||
billing_account_name, invoice_section_name, body
|
|
||||||
)
|
|
||||||
|
|
||||||
# the resulting object from this process is a link to the new subscription
|
|
||||||
# not a subscription model, so we'll have to unpack the ID
|
|
||||||
new_sub = sub_creation_operation.result()
|
|
||||||
|
|
||||||
subscription_id = self._extract_subscription_id(new_sub.subscription_link)
|
|
||||||
if subscription_id:
|
|
||||||
return subscription_id
|
|
||||||
else:
|
|
||||||
# troublesome error, subscription should exist at this point
|
|
||||||
# but we just don't have a valid ID
|
|
||||||
pass
|
|
||||||
|
|
||||||
def create_atat_admin_user(
|
def create_atat_admin_user(
|
||||||
self, auth_credentials: Dict, csp_environment_id: str
|
self, auth_credentials: Dict, csp_environment_id: str
|
||||||
@ -502,6 +485,83 @@ class AzureCloudProvider(CloudProviderInterface):
|
|||||||
"role_name": role_assignment_id,
|
"role_name": role_assignment_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _create_application(self, auth_credentials: Dict, application: Application):
|
||||||
|
management_group_name = str(uuid4()) # can be anything, not just uuid
|
||||||
|
display_name = application.name # Does this need to be unique?
|
||||||
|
credentials = self._get_credential_obj(auth_credentials)
|
||||||
|
parent_id = "?" # application.portfolio.csp_details.management_group_id
|
||||||
|
|
||||||
|
return self._create_management_group(
|
||||||
|
credentials, management_group_name, display_name, parent_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _create_management_group(
|
||||||
|
self, credentials, management_group_id, display_name, parent_id=None,
|
||||||
|
):
|
||||||
|
mgmgt_group_client = self.sdk.managementgroups.ManagementGroupsAPI(credentials)
|
||||||
|
create_parent_grp_info = self.sdk.managementgroups.models.CreateParentGroupInfo(
|
||||||
|
id=parent_id
|
||||||
|
)
|
||||||
|
create_mgmt_grp_details = self.sdk.managementgroups.models.CreateManagementGroupDetails(
|
||||||
|
parent=create_parent_grp_info
|
||||||
|
)
|
||||||
|
mgmt_grp_create = self.sdk.managementgroups.models.CreateManagementGroupRequest(
|
||||||
|
name=management_group_id,
|
||||||
|
display_name=display_name,
|
||||||
|
details=create_mgmt_grp_details,
|
||||||
|
)
|
||||||
|
create_request = mgmgt_group_client.management_groups.create_or_update(
|
||||||
|
management_group_id, mgmt_grp_create
|
||||||
|
)
|
||||||
|
|
||||||
|
# result is a synchronous wait, might need to do a poll instead to handle first mgmt group create
|
||||||
|
# since we were told it could take 10+ minutes to complete, unless this handles that polling internally
|
||||||
|
return create_request.result()
|
||||||
|
|
||||||
|
def _create_subscription(
|
||||||
|
self,
|
||||||
|
credentials,
|
||||||
|
display_name,
|
||||||
|
billing_profile_id,
|
||||||
|
sku_id,
|
||||||
|
management_group_id,
|
||||||
|
billing_account_name,
|
||||||
|
invoice_section_name,
|
||||||
|
):
|
||||||
|
sub_client = self.sdk.subscription.SubscriptionClient(credentials)
|
||||||
|
|
||||||
|
display_name = f"{environment.application.name}_{environment.name}_{environment.id}" # proposed format
|
||||||
|
billing_profile_id = "?" # where do we source this?
|
||||||
|
sku_id = AZURE_SKU_ID
|
||||||
|
# These 2 seem like something that might be worthwhile to allow tiebacks to
|
||||||
|
# TOs filed for the environment
|
||||||
|
billing_account_name = "?" # from TO?
|
||||||
|
invoice_section_name = "?" # from TO?
|
||||||
|
|
||||||
|
body = self.sdk.subscription.models.ModernSubscriptionCreationParameters(
|
||||||
|
display_name=display_name,
|
||||||
|
billing_profile_id=billing_profile_id,
|
||||||
|
sku_id=sku_id,
|
||||||
|
management_group_id=management_group_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# We may also want to create billing sections in the enrollment account
|
||||||
|
sub_creation_operation = sub_client.subscription_factory.create_subscription(
|
||||||
|
billing_account_name, invoice_section_name, body
|
||||||
|
)
|
||||||
|
|
||||||
|
# the resulting object from this process is a link to the new subscription
|
||||||
|
# not a subscription model, so we'll have to unpack the ID
|
||||||
|
new_sub = sub_creation_operation.result()
|
||||||
|
|
||||||
|
subscription_id = self._extract_subscription_id(new_sub.subscription_link)
|
||||||
|
if subscription_id:
|
||||||
|
return subscription_id
|
||||||
|
else:
|
||||||
|
# troublesome error, subscription should exist at this point
|
||||||
|
# but we just don't have a valid ID
|
||||||
|
pass
|
||||||
|
|
||||||
def _get_management_service_principal(self):
|
def _get_management_service_principal(self):
|
||||||
# we really should be using graph.microsoft.com, but i'm getting
|
# we really should be using graph.microsoft.com, but i'm getting
|
||||||
# "expired token" errors for that
|
# "expired token" errors for that
|
||||||
|
@ -1,27 +1,81 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
from unittest.mock import Mock
|
||||||
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from atst.domain.csp.cloud import AzureCloudProvider
|
from atst.domain.csp.cloud import AzureCloudProvider
|
||||||
|
|
||||||
from tests.mock_azure import mock_azure, AUTH_CREDENTIALS
|
from tests.mock_azure import mock_azure, AUTH_CREDENTIALS
|
||||||
from tests.factories import EnvironmentFactory
|
from tests.factories import EnvironmentFactory, ApplicationFactory
|
||||||
|
|
||||||
|
|
||||||
def test_create_environment_succeeds(mock_azure: AzureCloudProvider):
|
# TODO: Directly test create subscription, provide all args √
|
||||||
|
# TODO: Test create environment (create management group with parent)
|
||||||
|
# TODO: Test create application (create manageemnt group with parent)
|
||||||
|
# Create reusable mock for mocking the management group calls for multiple services
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_subscription_succeeds(mock_azure: AzureCloudProvider):
|
||||||
environment = EnvironmentFactory.create()
|
environment = EnvironmentFactory.create()
|
||||||
|
|
||||||
subscription_id = str(uuid4())
|
subscription_id = str(uuid4())
|
||||||
|
|
||||||
|
credentials = mock_azure._get_credential_obj(AUTH_CREDENTIALS)
|
||||||
|
display_name = "Test Subscription"
|
||||||
|
billing_profile_id = str(uuid4())
|
||||||
|
sku_id = str(uuid4())
|
||||||
|
management_group_id = (
|
||||||
|
environment.cloud_id # environment.csp_details.management_group_id?
|
||||||
|
)
|
||||||
|
billing_account_name = (
|
||||||
|
"?" # environment.application.portfilio.csp_details.billing_account.name?
|
||||||
|
)
|
||||||
|
invoice_section_name = "?" # environment.name? or something specific to billing?
|
||||||
|
|
||||||
mock_azure.sdk.subscription.SubscriptionClient.return_value.subscription_factory.create_subscription.return_value.result.return_value.subscription_link = (
|
mock_azure.sdk.subscription.SubscriptionClient.return_value.subscription_factory.create_subscription.return_value.result.return_value.subscription_link = (
|
||||||
f"subscriptions/{subscription_id}"
|
f"subscriptions/{subscription_id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
result = mock_azure._create_subscription(
|
||||||
|
credentials,
|
||||||
|
display_name,
|
||||||
|
billing_profile_id,
|
||||||
|
sku_id,
|
||||||
|
management_group_id,
|
||||||
|
billing_account_name,
|
||||||
|
invoice_section_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result == subscription_id
|
||||||
|
|
||||||
|
|
||||||
|
def mock_management_group_create(mock_azure, spec_dict):
|
||||||
|
mock_azure.sdk.managementgroups.ManagementGroupsAPI.return_value.management_groups.create_or_update.return_value.result.return_value = Mock(
|
||||||
|
**spec_dict
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_environment_succeeds(mock_azure: AzureCloudProvider):
|
||||||
|
environment = EnvironmentFactory.create()
|
||||||
|
|
||||||
|
mock_management_group_create(mock_azure, {"id": "Test Id"})
|
||||||
|
|
||||||
result = mock_azure.create_environment(
|
result = mock_azure.create_environment(
|
||||||
AUTH_CREDENTIALS, environment.creator, environment
|
AUTH_CREDENTIALS, environment.creator, environment
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result == subscription_id
|
assert result.id == "Test Id"
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_application_succeeds(mock_azure: AzureCloudProvider):
|
||||||
|
application = ApplicationFactory.create()
|
||||||
|
|
||||||
|
mock_management_group_create(mock_azure, {"id": "Test Id"})
|
||||||
|
|
||||||
|
result = mock_azure._create_application(AUTH_CREDENTIALS, application)
|
||||||
|
|
||||||
|
assert result.id == "Test Id"
|
||||||
|
|
||||||
|
|
||||||
def test_create_atat_admin_user_succeeds(mock_azure: AzureCloudProvider):
|
def test_create_atat_admin_user_succeeds(mock_azure: AzureCloudProvider):
|
||||||
|
@ -10,9 +10,9 @@ AZURE_CONFIG = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AUTH_CREDENTIALS = {
|
AUTH_CREDENTIALS = {
|
||||||
"CLIENT_ID": AZURE_CONFIG["AZURE_CLIENT_ID"],
|
"client_id": AZURE_CONFIG["AZURE_CLIENT_ID"],
|
||||||
"SECRET_KEY": AZURE_CONFIG["AZURE_SECRET_KEY"],
|
"secret_key": AZURE_CONFIG["AZURE_SECRET_KEY"],
|
||||||
"TENANT_ID": AZURE_CONFIG["AZURE_TENANT_ID"],
|
"tenant_id": AZURE_CONFIG["AZURE_TENANT_ID"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -28,6 +28,12 @@ def mock_authorization():
|
|||||||
return Mock(spec=authorization)
|
return Mock(spec=authorization)
|
||||||
|
|
||||||
|
|
||||||
|
def mock_managementgroups():
|
||||||
|
from azure.mgmt import managementgroups
|
||||||
|
|
||||||
|
return Mock(spec=managementgroups)
|
||||||
|
|
||||||
|
|
||||||
def mock_graphrbac():
|
def mock_graphrbac():
|
||||||
import azure.graphrbac as graphrbac
|
import azure.graphrbac as graphrbac
|
||||||
|
|
||||||
@ -46,6 +52,7 @@ class MockAzureSDK(object):
|
|||||||
|
|
||||||
self.subscription = mock_subscription()
|
self.subscription = mock_subscription()
|
||||||
self.authorization = mock_authorization()
|
self.authorization = mock_authorization()
|
||||||
|
self.managementgroups = mock_managementgroups()
|
||||||
self.graphrbac = mock_graphrbac()
|
self.graphrbac = mock_graphrbac()
|
||||||
self.credentials = mock_credentials()
|
self.credentials = mock_credentials()
|
||||||
# may change to a JEDI cloud
|
# may change to a JEDI cloud
|
||||||
|
Loading…
x
Reference in New Issue
Block a user