I'm having trouble creating an IAM policy in the AWS PHP SDK and getting a MalformedPolicyDocument error.
The policy JSON seems good to me so I'm not sure why it's breaking. I'm sure it's something simple stupid I'm doing wrong but just not seeing it.
The use case here is we create a new IAM user, a new S3 bucket and a new policy that limits access only to the new bucket then attach that policy to the new user.
The IAM user and S3 bucket gets created but once it gets to creating the new policy it breaks with the MalformedPolicyDocument error.
Keep in mind this code is not for production and just intended to workout the flow and get the basic methods working which is why the keys are used directly in the code here. Figured I better throw that out there so the replies don't get hung up on that aspect.
Here's the code I'm using to test the workflow:
// VARIABLES
$key = 'SOMEKEY';
$secretKey = 'SOMESECRETKEY';
$domain = 'somedomain.com';
$stagingDomain = 'somestagingdomain.com';
$userName = 'somedomaincom';
$BUCKET_NAME = 'somedomaincom';
$s3Arn = 'arn:aws:s3:::' . $BUCKET_NAME;
$policyName = 'somedomaincomPolicy';
$policyArn = 'arn:aws:iam::aws:policy/' . $policyName;
require 'aws/aws-autoloader.php';
use Aws\S3\S3Client;
use Aws\Iam\IamClient;
use Aws\Exception\AwsException;
$iamClient = new IamClient([
'version' => 'latest',
'region' => 'us-west-2',
'credentials' => [
'key' => $key,
'secret' => $secretKey,
],
]);
try {
$result = $iamClient->createUser(array(
'UserName' => $userName,
));
var_dump($result);
} catch (AwsException $e) {
echo $e->getMessage();
error_log($e->getMessage());
}
//Create a S3Client
$s3Client = new S3Client([
'version' => 'latest',
'region' => 'us-west-2',
'credentials' => [ // CHANGE THIS TO A DIFFERENT METHOD BEFORE MOVING TO PRODUCTION
'key' => $key,
'secret' => $secretKey,
],
]);
//Creating S3 Bucket
try {
$result = $s3Client->createBucket([
'Bucket' => $BUCKET_NAME,
]);
} catch (AwsException $e) {
// output error message if fails
echo $e->getMessage();
echo "
";
}
// SET CORS RULES
$cors = array(array(
'AllowedOrigins' => array($domain, $stagingDomain),
'AllowedMethods' => array('POST', 'GET', 'PUT'),
'MaxAgeSeconds' => 3000,
'AllowedHeaders' => array('*')
));
// ADD CORS RULES
$result = $s3Client->putBucketCors(array(
'Bucket' => $BUCKET_NAME,
'CORSConfiguration' => array('CORSRules' => $cors)
));
// CREATE IAM POLICY - BREAKS ON THIS, MALFORMED POLICY???
$myManagedPolicy = '{
"Version": "latest",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListAllMyBuckets",
"Resource": "arn:aws:s3:::*"
},
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"' . $s3Arn . '",
"'. $s3Arn . '/*"
]
}
]
}';
try {
$result = $iamClient->createPolicy(array(
'PolicyName' => $policyName,
'PolicyDocument' => $myManagedPolicy
));
var_dump($result);
} catch (AwsException $e) {
// output error message if fails
error_log($e->getMessage());
echo $e->getMessage();
}
// ATTACH IAM POLICY TO USER
try {
$attachedUserPolicies = $iamClient->getIterator('ListAttachedUserPolicies', ([
'UserName' => $userName,
]));
if (count($attachedUserPolicies) > 0) {
foreach ($attachedUserPolicies as $attachedUserPolicy) {
if ($attachedUserPolicy['PolicyName'] == $policyName) {
echo $policyName . " is already attached to this role.
";
exit();
}
}
}
$result = $iamClient->attachUserPolicy(array(
'UserName' => $userName,
'PolicyArn' => $policyArn,
));
var_dump($result);
} catch (AwsException $e) {
// output error message if fails
error_log($e->getMessage());
echo $e->getMessage();
}
I've tried various ways of formatting the policy JSON such as adding [] to the actions and resources as well as hard-coding the values instead of using variables.
This seems like such a simple thing to do but I've hit a wall with it. Any idea where I'm going wrong here?
Of course as soon as I post this I figure it out.
I think my issue was with trying to use "latest" as the version so I changed it to 2012-10-17.
In case this helps someone else wanting to do something similar here's the complete working code to create a new IAM user, create access keys for the new user, create a new S3 bucket, set CORS on the S3 bucket to allow access from a domain and staging domain, create a new policy to limit access only to the new S3 bucket and then attach that new policy to the new IAM user:
// VARIABLES
$key = 'YOURKEY';
$secretKey = 'YOURSECRETKEY';
$iamUserKey = '';
$iamUserSecretKey = '';
$domain = 'somedomain.com';
$stagingDomain = 'somestagingdomain.com';
$userName = 'someusername';
$BUCKET_NAME = 'somebucketname';
$s3Arn = 'arn:aws:s3:::' . $BUCKET_NAME;
$policyName = 'somepolicynamePolicy';
$policyArn = '';
require 'aws/aws-autoloader.php';
use Aws\S3\S3Client;
use Aws\Iam\IamClient;
use Aws\Exception\AwsException;
// CREATE IAM CLIENT
$iamClient = new IamClient([
'version' => 'latest',
'region' => 'us-west-2',
'credentials' => [ // CHANGE THIS TO A DIFFERENT METHOD BEFORE MOVING TO PRODUCTION
'key' => $key,
'secret' => $secretKey,
],
]);
// CREATE IAM USER
try {
$result = $iamClient->createUser(array(
'UserName' => $userName,
));
//var_dump($result);
} catch (AwsException $e) {
echo $e->getMessage();
error_log($e->getMessage());
}
// CREATE IAM USER ACCESS KEYS
try {
$result = $iamClient->createAccessKey([
'UserName' => $userName,
]);
$iamUserKey = $result['AccessKey']['AccessKeyId'];
$iamUserSecretKey= $result['AccessKey']['SecretAccessKey'];
} catch (AwsException $e) {
// output error message if fails
error_log($e->getMessage());
}
// CREATE S3 CLIENT
$s3Client = new S3Client([
'version' => 'latest',
'region' => 'us-west-2',
'credentials' => [ // CHANGE THIS TO A DIFFERENT METHOD BEFORE MOVING TO PRODUCTION
'key' => $key,
'secret' => $secretKey,
],
]);
// CREATE S3 BUCKET
try {
$result = $s3Client->createBucket([
'Bucket' => $BUCKET_NAME,
]);
} catch (AwsException $e) {
echo $e->getMessage();
echo "
";
}
// SET CORS RULES
$cors = array(array(
'AllowedOrigins' => array($domain, $stagingDomain),
'AllowedMethods' => array('POST', 'GET', 'PUT'),
'MaxAgeSeconds' => 3000,
'AllowedHeaders' => array('*')
));
// ADD CORS RULES
$result = $s3Client->putBucketCors(array(
'Bucket' => $BUCKET_NAME,
'CORSConfiguration' => array('CORSRules' => $cors)
));
// CREATE IAM POLICY - BREAKS ON THIS, MALFORMED POLICY???
$myManagedPolicy = '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListAllMyBuckets",
"Resource": "' . $s3Arn . '"
},
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "'. $s3Arn . '/*"
}
]
}';
try {
$result = $iamClient->createPolicy(array(
// PolicyName is required
'PolicyName' => $policyName,
// PolicyDocument is required
'PolicyDocument' => $myManagedPolicy
));
//var_dump($result);
$policyArn = $result['Policy']['Arn'];
} catch (AwsException $e) {
// output error message if fails
error_log($e->getMessage());
echo $e->getMessage();
}
// ATTACH IAM POLICY TO USER
try {
$attachedUserPolicies = $iamClient->getIterator('ListAttachedUserPolicies', ([
'UserName' => $userName,
]));
if (count($attachedUserPolicies) > 0) {
foreach ($attachedUserPolicies as $attachedUserPolicy) {
if ($attachedUserPolicy['PolicyName'] == $policyName) {
echo $policyName . " is already attached to this role.
";
exit();
}
}
}
$result = $iamClient->attachUserPolicy(array(
// UserName is required
'UserName' => $userName,
// PolicyArn is required
'PolicyArn' => $policyArn,
));
//var_dump($result);
} catch (AwsException $e) {
// output error message if fails
error_log($e->getMessage());
echo $e->getMessage();
}
Do not use this code as-is, you should not add your access keys directly into the code as I did in this test example. You should look at the various ways to authenticate within the SDK and use the method that works best for your situation.